Первое, на что нужно обратить внимание, это то, что вы написали не сито эратосфена. Посмотрите, сколько петель выполняет абсолютно наивное сито эратосфена:
def sieve1(n):
loops = 0
numbers = set(range(2, n))
for i in range(2, int(n ** 0.5) + 1):
for j in range(i * 2, n, i):
numbers.discard(j)
loops += 1
return sorted(numbers), loops
Протестировано:
>>> sieve1(100)
([2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41,
43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97],
178)
178 циклов на 100 номеров (не включая сортировку). Это может быть улучшено с небольшими изменениями:
def sieve2(n):
loops = 0
numbers = range(0, n)
for prime in numbers:
if prime < 2:
continue
elif prime > n ** 0.5:
break
for i in range(prime ** 2, n, prime):
numbers[i] = 0
loops += 1
return [x for x in numbers if x > 1], loops
Протестировано:
>>> sieve2(100)
([2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41,
43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97],
102)
102 петли для 100 номеров (не включая фильтр в конце). Теперь посмотри на свои:
def sieve3(n):
loops = 0
numbers = range(2, n)
for i in numbers:
for j in numbers:
if j != i and j % i == 0:
numbers.remove(j)
loops += 1
return numbers, loops
Протестировано:
>>> sieve3(100)
([2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41,
43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97],
663)
Становится хуже:
>>> [sieve1(x)[1] for x in [100, 1000, 10000]]
[178, 2978, 41723]
>>> [sieve2(x)[1] for x in [100, 1000, 10000]]
[102, 1409, 16979]
>>> [sieve3(x)[1] for x in [100, 1000, 10000]]
[663, 28986, 1523699]
На n = 10000
ваша реализация выполняет почти в 100 раз больше!
Мое предложение состояло бы в том, чтобы создать разумную реализацию, прежде чем сделать ее "компактной". Кодовый гольф может быть увлекательным, но он далеко не так сложен или поучителен, как написание эффективного кода любой длины.
Тем не менее, рассмотрите эту однострочную строку (если не считать импорт, от которого можно избавиться, используя lambda x, y: x - y
вместо operator.sub
). Это реализует первый алгоритм с небольшим улучшением:
>>> from operator import sub
>>> reduce(sub, (set(range(x ** 2, 100, x)) for x in range(2, int(100 ** 0.5) + 1)), set(range(2, 100)))
set([2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97])