"""Zhang's admissible tuple: the first k primes greater than k. Admissible because no element is divisible by a prime q <= k (all elements are primes > k), and k elements cannot cover q > k residue classes. Computes pi(k), p_{pi(k)+1}, p_{pi(k)+k} and the diameter with an odd-only segmented sieve (numpy).""" import sys, math, time, json import numpy as np def small_primes(n): s = np.ones(n + 1, dtype=bool); s[:2] = False for p in range(2, int(n ** 0.5) + 1): if s[p]: s[p * p::p] = False return np.nonzero(s)[0] def odd_segment(lo, hi, primes): """Boolean array over odd n in [lo, hi) (lo odd): True iff n is prime (n >= 3).""" assert lo % 2 == 1 n = (hi - lo + 1) // 2 seg = np.ones(n, dtype=bool) for p in primes: if p == 2: continue if p * p >= hi: break start = max(p * p, ((lo + p - 1) // p) * p) if start % 2 == 0: start += p if start >= hi: continue seg[(start - lo) // 2::p] = False if lo <= 1 < hi: seg[(1 - lo) // 2] = False return seg def zhang(k, seg_len=2 ** 28): t0 = time.time() # pi(k) X_est = int(k * (math.log(k) + math.log(math.log(k)) + 2)) + 10 ** 6 primes = small_primes(int(math.isqrt(X_est)) + 10) cnt = 1 if k >= 2 else 0 # the prime 2 lo = 1 first = None; need = k; last = None count_after = 0 while True: hi = lo + seg_len seg = odd_segment(lo, hi, primes) vals_idx = np.nonzero(seg)[0] vals = lo + 2 * vals_idx if first is None: le = vals[vals <= k] cnt += len(le) gt = vals[vals > k] if len(gt): first = int(gt[0]) take = gt else: take = gt else: take = vals if first is not None: if count_after + len(take) >= need: last = int(take[need - count_after - 1]) count_after = need break count_after += len(take) lo = hi return dict(k=k, pi_k=cnt, first=first, last=last, diameter=last - first, secs=round(time.time() - t0, 1)) if __name__ == "__main__": for k in [int(float(x)) for x in sys.argv[1].split(",")]: print(json.dumps(zhang(k)), flush=True)