Basic sieve
with numpy
is amazing fast. May be the fastest implementation
# record: sieve 1_000_000_000 in 6.9s (core i7 - 2.6Ghz)def sieve_22max_naive(bound): sieve = np.ones(bound, dtype=bool) # default all prime sieve[:2] = False # 0, 1 is not prime sqrt_bound = math.ceil(math.sqrt(bound)) for i in range(2, sqrt_bound): if sieve[i]: inc = i if i == 2 else 2 * i sieve[i * i:bound:inc] = False return np.arange(bound)[sieve]if __name__ == '__main__': start = time.time() prime_list = sieve_22max_naive(1_000_000_000) print(f'Count: {len(prime_list):,}\n' f'Greatest: {prime_list[-1]:,}\n' f'Elapsed: %.3f' % (time.time() - start))
Segment sieve (use less memory)
# find prime in range [from..N), base on primes in range [2..from)def sieve_era_part(primes, nfrom, n): sieve_part = np.ones(n - nfrom, dtype=bool) # default all prime limit = math.ceil(math.sqrt(n)) # [2,3,5,7,11...p] can find primes < (p+2)^2 if primes[-1] < limit - 2: print(f'Not enough base primes to find up to {n:,}') return for p in primes: if p >= limit: break mul = p * p inc = p * (2 if p > 2 else 1) if mul < nfrom: mul = math.ceil(nfrom / p) * p (mul := mul + p) if p > 2 and (mul & 1) == 0 else ... # odd, not even sieve_part[mul - nfrom::inc] = False return np.arange(nfrom, n)[sieve_part] # return np.where(sieve_part)[0] + nfrom # return [i + nfrom for i, is_p in enumerate(sieve_part) if is_p] # return [i for i in range(max(nfrom, 2), n) if sieve_part[i - nfrom]]# find nth prime number, use less memory,# extend bound to SEG_SIZE each loop# record: 50_847_534 nth prime in 6.78s, core i7 - 9850H 2.6GHhzdef nth_prime(n): # find prime up to bound bound = 500_000 primes = sieve_22max_naive(bound) SEG_SIZE = int(50e6) while len(primes) < n: # sieve for next segment new_primes = sieve_era_part(primes, bound, bound + SEG_SIZE) # extend primes bound += SEG_SIZE primes = np.append(primes, new_primes) return primes[n - 1]if __name__ == '__main__': start = time.time() prime = nth_prime(50_847_534) print(f'{prime:,} Time %.6f' % (time.time() - start))