Вот версия, которая немного более эффективна для памяти (и: правильное сито, а не пробные деления). По сути, вместо того, чтобы хранить массив всех чисел и вычеркивать те, которые не являются простыми, это сохраняет массив счетчиков - по одному на каждое обнаруженное простое число - и перепрыгивает их впереди предполагаемого простого числа. Таким образом, он использует память, пропорциональную количеству простых чисел, а не вплоть до самого высокого простого числа.
import itertools
def primes():
class counter:
def __init__ (this, n): this.n, this.current, this.isVirgin = n, n*n, True
# isVirgin means it's never been incremented
def advancePast (this, n): # return true if the counter advanced
if this.current > n:
if this.isVirgin: raise StopIteration # if this is virgin, then so will be all the subsequent counters. Don't need to iterate further.
return False
this.current += this.n # pre: this.current == n; post: this.current > n.
this.isVirgin = False # when it's gone, it's gone
return True
yield 1
multiples = []
for n in itertools.count(2):
isPrime = True
for p in (m.advancePast(n) for m in multiples):
if p: isPrime = False
if isPrime:
yield n
multiples.append (counter (n))
Вы заметите, что primes()
является генератором, поэтому вы можете сохранить результаты в списке или использовать их напрямую. Вот первые n
простых чисел:
import itertools
for k in itertools.islice (primes(), n):
print (k)
И, для полноты, вот таймер для измерения производительности:
import time
def timer ():
t, k = time.process_time(), 10
for p in primes():
if p>k:
print (time.process_time()-t, " to ", p, "\n")
k *= 10
if k>100000: return
На всякий случай, если вам интересно, я также написал primes()
как простой итератор (с использованием __iter__
и __next__
), и он работал почти с той же скоростью. Удивил меня тоже!