Сито Эратосфена - Поиск простых чисел Python

Чтобы уточнить, это не домашнее задание :)

Я хотел найти простые числа для математического приложения, которое создаю, и наткнулся на Сито Эратосфена подход.

Я написал его реализацию на Python. Но это ужасно медленно. Например, если я хочу найти все простые числа меньше 2 миллионов. Это займет> 20 минут. (Я остановил это на этом этапе). Как я могу это ускорить?

def primes_sieve(limit):
    limitn = limit+1
    primes = range(2, limitn)

    for i in primes:
        factors = range(i, limitn, i)
        for f in factors[1:]:
            if f in primes:
                primes.remove(f)
    return primes

print primes_sieve(2000)

ОБНОВЛЕНИЕ: я закончил профилирование этого кода и обнаружил, что довольно много времени было потрачено на удаление элемента из списка. Вполне понятно, учитывая, что он должен пройти весь список (в худшем случае), чтобы найти элемент, а затем удалить его, а затем перенастроить список (может быть, некоторая копия продолжается?). Во всяком случае, я выбросил список для словаря. Моя новая реализация -

def primes_sieve1(limit):
    limitn = limit+1
    primes = dict()
    for i in range(2, limitn): primes[i] = True

    for i in primes:
        factors = range(i,limitn, i)
        for f in factors[1:]:
            primes[f] = False
    return [i for i in primes if primes[i]==True]

print primes_sieve1(2000000)

person Srikar Appalaraju    schedule 15.10.2010    source источник
comment
Здесь есть аналогичный вопрос, stackoverflow.com/questions/2897297, который может оказаться полезным.   -  person Scott Griffiths    schedule 15.10.2010
comment
Проверьте что ответ.   -  person tzot    schedule 15.10.2010
comment
stackoverflow.com/questions/2068372/   -  person Robert William Hanks    schedule 19.10.2010
comment
@Srikar: вместо того, чтобы повторять до предела, вы можете просто перебирать до квадратного корня из предела, поскольку любое составное число в вашем словаре будет иметь на один множитель меньше квадратного корня из предела.   -  person sayantankhan    schedule 17.08.2013
comment
Прекрасно использовать параметр step в range. factors неправильно и должно быть multiples.   -  person Tom Russell    schedule 01.03.2018


Ответы (19)


Вы не совсем реализуете правильный алгоритм:

В вашем первом примере primes_sieve не поддерживает список флагов примитивности для удаления / снятия (как в алгоритме), а вместо этого непрерывно изменяет размер списка целых чисел, что очень дорого: удаление элемента из списка требует сдвига всех последующих элементов вниз на один.

Во втором примере primes_sieve1 поддерживает словарь флагов примитивности, что является шагом в правильном направлении, но он выполняет итерацию по словарю в неопределенном порядке и избыточно вычеркивает факторы факторов (вместо только множители простых чисел, как в алгоритме). Вы можете исправить это, отсортировав ключи и пропустив непростые числа (что уже делает его на порядок быстрее), но все же гораздо эффективнее просто использовать список напрямую.

Правильный алгоритм (со списком вместо словаря) выглядит примерно так:

def primes_sieve2(limit):
    a = [True] * limit                          # Initialize the primality list
    a[0] = a[1] = False

    for (i, isprime) in enumerate(a):
        if isprime:
            yield i
            for n in range(i*i, limit, i):     # Mark factors non-prime
                a[n] = False

(Обратите внимание, что это также включает алгоритмическую оптимизацию начала маркировки непростых чисел с квадрата простого числа (i*i) вместо его двойного.)

person Pi Delport    schedule 15.10.2010
comment
еще одна оптимизация, размер шага вашего xrange(i*i,limit,i) можно сделать 2*i - person st0le; 21.10.2010
comment
Хорошо подмечено! Если я не ошибаюсь, это шаг в сторону эйлеровой версии сита, верно? - person Pi Delport; 21.10.2010
comment
Мне нравится ваша лаконичная реализация Сита Эратосфена. :) Однако у меня ошибка OverflowError: Python int слишком большой для преобразования в C long. Я изменил xrange (i * i, limit, i) на xrange (i, limit, i). Спасибо, что поделились этим фрагментом кода! - person Annie Lagang; 02.04.2012
comment
@ st0le: Нет, размер шага нельзя сделать 2*i. Просто попробовал. Это дает 14 как простое число. - person mpen; 13.07.2012
comment
@Mark, мне жаль, что я не объяснил это полностью. Удалите все четные числа, выполнив итерацию с i=2 с шагом i, но для остальных вы можете использовать 2*i. Фактически, в своей реализации я использую половину логических значений, поскольку я не храню четные числа, а вместо этого использую простой mod 2. Здесь вы можете найти мою реализацию Java, которая использует еще меньше (1/8) памяти. ЗДЕСЬ - person st0le; 13.07.2012
comment
@AnneLagang Если вы сделаете это, вы потеряете большую часть оптимизации, поскольку вы избыточно проверяете множество чисел. Для альтернативы xrange см. Мой ответ. - person Asad Saeeduddin; 04.04.2013
comment
+1, небольшая деталь, если вы используете [False] * 2 + [True] * (limit-2) при инициализации, вы можете избежать IndexError при передаче числа ‹2 в качестве аргумента - person Jan Vorcak; 10.11.2013
comment
@ st0le Можно ли расширить вашу оптимизацию до любого предыдущего простого числа? Т.е. сохранить значение x, которое является ранее обнаруженным простым числом, а затем сразу после выхода определить приращение как i * x и приращение на это вместо i? похоже, что это сработает, поскольку все числа, кратные ранее встречавшимся числам, уже проверены - person Vyas; 28.11.2013
comment
@ Vyas, кажется правдоподобным, но мне нужно это проверить. - person st0le; 28.11.2013
comment
@ st0le Похоже, он работает до 10 миллионов, я не могу тестировать дальше этого, если прогон существенно не повлияет на производительность моего компа. Я также провел быстрый временной тест, и результаты были интересными. Похоже, что просто создание генератора путем присвоения x = prime_sieve (n) выполняется быстрее, но если я сделаю x = list (prime_sieve (n)), чтобы фактически пройти через все поколение, то исходная версия будет быстрее. Есть идеи, почему? - person Vyas; 28.11.2013
comment
@Vyas, это потому, что когда вы делаете list (), он выделяет память в зависимости от запуска генератора. Список () похож на вектор в C ++, его емкость постепенно увеличивается, следовательно, он требует постоянного перераспределения. Это определенно повлияет на время. Поэтому ванильный запуск генератора будет быстрее. - person st0le; 28.11.2013
comment
@ st0le Я понимаю, почему преобразование в список происходит медленно, но поскольку два генератора создают списки одинакового размера, я не понимаю, почему один будет значительно медленнее другого. Пытается ли list () угадать размер на основе содержимого функции генератора? - person Vyas; 28.11.2013
comment
@Vyas, нет (и не может). Он начинается с емкости по умолчанию. Вы создаете список при запуске генератора? Теоретически это должно занять то же время, а на самом деле больше. - person st0le; 28.11.2013
comment
@ st0le Где я могу разместить свой код, чтобы показать вам? - person Vyas; 28.11.2013
comment
@ st0le Извините за задержку, мне пришлось успеть на рейс, и я понял, что не так с моей идеей, но не смог опубликовать. Этот метод не может пометить произведения простых чисел как простые; он работает только для кратных i до i, которые уже исключены запуском проверки с i * i. В моем коде отступ для увеличения x был отключен, поэтому на самом деле он просто выполнял i * 1. Для создания генератора он должен был быть быстрее для одной итерации, потому что в нем отсутствует предложение if, но после определенного момента оптимизация i * 2 сделала другую версию быстрее, я предполагаю - person Vyas; 28.11.2013
comment
К вашему сведению, гораздо более эффективный подход к использованию памяти заключался бы в замене list из bool на bytearray (снижение стоимости одной записи сита с 4-8 байтов до 1 байта; что-то лучшее потребует манипуляции с битами, что снизит производительность или потребуется третье party C, чтобы избежать попадания). Код будет идентичным, за исключением того, что вы измените a = [True] * limit на a = bytearray(b'\x01') * limit. - person ShadowRanger; 22.01.2016
comment
Кроме того, использование bytearray может значительно снизить затраты на рассев; вместо установки цикла False вы можете назначить срез с соответствующим умноженным b'\0'; цикл все еще существует под капотом, но это цикл уровня C, который выполняется на несколько порядков быстрее. - person ShadowRanger; 22.01.2016

Удаление с начала массива (списка) требует перемещения всех элементов после него вниз. Это означает, что удаление каждого элемента из списка таким образом, начиная с фронта, является операцией O (n ^ 2).

Вы можете сделать это намного эффективнее с помощью наборов:

def primes_sieve(limit):
    limitn = limit+1
    not_prime = set()
    primes = []

    for i in range(2, limitn):
        if i in not_prime:
            continue

        for f in range(i*2, limitn, i):
            not_prime.add(f)

        primes.append(i)

    return primes

print primes_sieve(1000000)

... или, в качестве альтернативы, избегайте переупорядочивания списка:

def primes_sieve(limit):
    limitn = limit+1
    not_prime = [False] * limitn
    primes = []

    for i in range(2, limitn):
        if not_prime[i]:
            continue
        for f in xrange(i*2, limitn, i):
            not_prime[f] = True

        primes.append(i)

    return primes
person Glenn Maynard    schedule 15.10.2010
comment
См. Ответ @Piet Delport ниже для оптимизации: замените i*2 выше на i*i. - person President James K. Polk; 17.10.2010

Намного быстрее:

import time
def get_primes(n):
  m = n+1
  #numbers = [True for i in range(m)]
  numbers = [True] * m #EDIT: faster
  for i in range(2, int(n**0.5 + 1)):
    if numbers[i]:
      for j in range(i*i, m, i):
        numbers[j] = False
  primes = []
  for i in range(2, m):
    if numbers[i]:
      primes.append(i)
  return primes

start = time.time()
primes = get_primes(10000)
print(time.time() - start)
print(get_primes(100))
person MrHIDEn    schedule 14.08.2015

Я понимаю, что на самом деле это не отвечает на вопрос о том, как быстро генерировать простые числа, но, возможно, некоторые найдут эту альтернативу интересной: поскольку python обеспечивает ленивую оценку с помощью генераторов, решето эратосфена может быть реализовано точно так, как указано:

def intsfrom(n):
    while True:
        yield n
        n += 1

def sieve(ilist):
    p = next(ilist)
    yield p
    for q in sieve(n for n in ilist if n%p != 0):
        yield q


try:
    for p in sieve(intsfrom(2)):
        print p,

    print ''
except RuntimeError as e:
    print e

Блок try существует, потому что алгоритм работает до тех пор, пока он не взорвет стек, а без блока try отображается обратная трассировка, подталкивая фактический результат, который вы хотите увидеть, за пределами экрана.

person Paul Gardiner    schedule 20.07.2014
comment
нет, это не сито Эратосфена, а скорее сито пробного деления. Даже это очень неоптимально, потому что это не отложено: любой номер кандидата должен быть проверен только простые числа не выше квадратного корня. Реализация этого по строкам псевдокода в нижней части связанного выше ответа (последний) даст вашему коду огромное ускорение (даже до того, как вы переключитесь на правильное сито) и / потому что это значительно минимизирует использование стека - так что в конце концов, вам может не понадобиться ваш try блок. - person Will Ness; 21.07.2014
comment
Интересно, хотя я еще не понимаю, почему то, что я поставил, отличается от сита Эратосфена. Я думал, что это было описано как размещение всех промежуточных чисел из 2 в строке, затем многократно брать первое в строке как простое и вычеркивать все кратные. n для n в ilist, если n% p! = 0 бит должен был представлять вычеркивание кратных. По общему признанию, очень неоптимально, но определенно - person Paul Gardiner; 18.02.2015
comment
n for n in ilist if n%p != 0 проверяет каждое число n в диапазоне на делимость на p; но range(p*p, N, p) генерирует мультипликаторы напрямую, самостоятельно, без проверки всех этих чисел. - person Will Ness; 25.02.2015

Объединив вклад многих энтузиастов (включая Гленна Мейнарда и MrHIDEn из приведенных выше комментариев), я придумал следующий фрагмент кода на Python 2:

def simpleSieve(sieveSize):
    #creating Sieve.
    sieve = [True] * (sieveSize+1)
    # 0 and 1 are not considered prime.
    sieve[0] = False
    sieve[1] = False
    for i in xrange(2,int(math.sqrt(sieveSize))+1):
        if sieve[i] == False:
            continue
        for pointer in xrange(i**2, sieveSize+1, i):
            sieve[pointer] = False
    # Sieve is left with prime numbers == True
    primes = []
    for i in xrange(sieveSize+1):
        if sieve[i] == True:
            primes.append(i)
    return primes

sieveSize = input()
primes = simpleSieve(sieveSize)

Время, затраченное на вычисление на моей машине для различных входов в степени 10, составляет:

  • 3 : 0.3 ms
  • 4 : 2.4 ms
  • 5 : 23 ms
  • 6 : 0.26 s
  • 7 : 3.1 s
  • 8 : 33 s
person Ajay    schedule 04.09.2016
comment
сравнение с True или False больше не нужно, так как они уже являются логическими, - person Copperfield; 04.09.2016
comment
@Copperfield Спасибо! Помогло увеличить скорость на 10-20%. - person Ajay; 04.09.2016
comment
Это sieve = [True] * (sieveSize+1) быстрее, чем мое решение, но sieve[0]/[1] и xrange(sieveSize+1) при простых числах [] ничего не улучшают. xrange(2, sieveSize+1) хорош во всем. :). Также вместо for i in xrange(2,int(math.sqrt(sieveSize))+1): мы можем просто использовать for i in xrange(2, int((sieveSize+1)**0.5): Хороший код. :) - person MrHIDEn; 28.11.2016

Простой способ уловить скорость: когда вы определяете переменную "простые числа", установите шаг на 2, чтобы автоматически пропускать все четные числа, и установите начальную точку на 1.

Затем вы можете дополнительно оптимизировать вместо for i в простых числах, используйте for i в простых числах [: round (len (primes) ** 0,5)]. Это резко повысит производительность. Кроме того, вы можете удалить числа, заканчивающиеся на 5, чтобы еще больше увеличить скорость.

person Community    schedule 01.02.2015

Моя реализация:

import math
n = 100
marked = {}
for i in range(2, int(math.sqrt(n))):
    if not marked.get(i):
        for x in range(i * i, n, i):
            marked[x] = True

for i in range(2, n):
    if not marked.get(i):
        print i
person SilentDirge    schedule 04.05.2016
comment
Я только что протестировал ваш код и увидел, что решение dict в 2 раза медленнее, чем решение list. - person MrHIDEn; 28.11.2016

Вот версия, которая немного более эффективна с точки зрения памяти (и: правильное сито, а не пробные деления). По сути, вместо того, чтобы хранить массив всех чисел и вычеркивать те, которые не являются простыми, это сохраняет массив счетчиков - по одному для каждого обнаруженного простого числа - и опережает их по сравнению с предполагаемым простым числом. Таким образом, он использует память, пропорциональную количеству простых чисел, а не до самого высокого простого числа.

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__), и он работал почти с той же скоростью. Меня тоже удивило!

person Jules May    schedule 05.01.2017
comment
интересная идея - это улучшит производительность, если вы сохраните основные счетчики в минимальной куче (внутренний цикл будет O (log num_primes) вместо O (num_primes)) - person anthonybell; 25.07.2017
comment
Почему? Даже если бы они были в куче, мы все равно должны отчитаться за каждого. - person Jules May; 26.07.2017
comment
Если вы храните каждое простое число в куче с ключом к его следующему значению, вам нужно будет смотреть только на простые числа, следующее значение которых является текущим значением n. самые большие простые числа будут опускаться в самый низ кучи, и их нужно будет оценивать гораздо реже, чем меньшие простые числа. - person anthonybell; 26.07.2017

Я предпочитаю NumPy из-за скорости.

import numpy as np

# Find all prime numbers using Sieve of Eratosthenes
def get_primes1(n):
    m = int(np.sqrt(n))
    is_prime = np.ones(n, dtype=bool)
    is_prime[:2] = False  # 0 and 1 are not primes

    for i in range(2, m):
        if is_prime[i] == False:
            continue
        is_prime[i*i::i] = False

    return np.nonzero(is_prime)[0]

# Find all prime numbers using brute-force.
def isprime(n):
    ''' Check if integer n is a prime '''
    n = abs(int(n))  # n is a positive integer
    if n < 2:  # 0 and 1 are not primes
        return False
    if n == 2:  # 2 is the only even prime number
        return True
    if not n & 1:  # all other even numbers are not primes
        return False
    # Range starts with 3 and only needs to go up the square root
    # of n for all odd numbers
    for x in range(3, int(n**0.5)+1, 2):
        if n % x == 0:
            return False
    return True

# To apply a function to a numpy array, one have to vectorize the function
def get_primes2(n):
    vectorized_isprime = np.vectorize(isprime)
    a = np.arange(n)
    return a[vectorized_isprime(a)]

Проверьте вывод:

n = 100
print(get_primes1(n))
print(get_primes2(n))    
    [ 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]
    [ 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]

Сравните скорость Сита Эратосфена и перебора на Jupyter Notebook. Сито Эратосфена в 539 раз быстрее перебора миллиона элементов.

%timeit get_primes1(1000000)
%timeit get_primes2(1000000)
4.79 ms ± 90.3 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
2.58 s ± 31.2 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
person FooBar167    schedule 14.09.2017
comment
Должно ли содержимое вашего внутреннего цикла быть (с учетом предыдущих ответов и комментариев) одной строкой if is_prime[i]: is_prime[i*i::2*i]=False? - person Lutz Lehmann; 06.05.2019

Я решил, что можно просто использовать пустой список в качестве условия завершения цикла, и пришел к следующему:

limit = 100
ints = list(range(2, limit))   # Will end up empty

while len(ints) > 0:
    prime = ints[0]
    print prime
    ints.remove(prime)
    i = 2
    multiple = prime * i
    while multiple <= limit:
        if multiple in ints:
            ints.remove(multiple)
        i += 1
        multiple = prime * i
person Tom Russell    schedule 02.03.2018

Используя немного numpy, я мог найти все простые числа меньше 100 миллионов чуть более чем за 2 секунды.

Следует отметить две ключевые особенности.

  • Вырезать кратные i только для i до корня n
  • Установка кратных i на False с помощью x[2*i::i] = False намного быстрее, чем явный цикл for в python.

Эти два значительно ускоряют ваш код. Для пределов менее одного миллиона нет заметного времени работы.

import numpy as np

def primes(n):
    x = np.ones((n+1,), dtype=np.bool)
    x[0] = False
    x[1] = False
    for i in range(2, int(n**0.5)+1):
        if x[i]:
            x[2*i::i] = False

    primes = np.where(x == True)[0]
    return primes

print(len(primes(100_000_000)))
person storm125    schedule 10.10.2019

Самая быстрая реализация, которую я мог придумать:

isprime = [True]*N
isprime[0] = isprime[1] = False
for i in range(4, N, 2):
    isprime[i] = False
for i in range(3, N, 2):
    if isprime[i]:
        for j in range(i*i, N, 2*i):
            isprime[j] = False
person Madiyar    schedule 04.05.2019

Я только что придумал это. Возможно, он не самый быстрый, но я не использую ничего, кроме прямых сложений и сравнений. Конечно, здесь вас останавливает ограничение на рекурсию.

def nondivsby2():
    j = 1
    while True:
        j += 2
        yield j

def nondivsbyk(k, nondivs):
    j = 0
    for i in nondivs:
        while j < i:
            j += k
        if j > i:
            yield i

def primes():
    nd = nondivsby2()
    while True:
        p = next(nd)
        nd = nondivsbyk(p, nd)
        yield p

def main():
    for p in primes():
        print(p)
person tamir    schedule 03.10.2020
comment
очень красивая формулировка, чистая, ясная и лаконичная! Я сделаю закладку. конечно, чтобы получить 100-е простое число, цепочка nd будет иметь глубину 99 уровней. но действительно необходимо только 10. и становится все хуже и хуже, чем дальше мы продвигаемся по списку простых чисел. вы можете найти способ справиться с этой проблемой? :) - person Will Ness; 06.10.2020
comment
Кроме того, я действительно не вижу здесь рекурсии, так что здесь не должно быть никаких ограничений. (конечно, я почти не знаю Python) - person Will Ness; 06.10.2020
comment
Сначала я был удивлен, когда получил RecursionError: maximum recursion depth exceeded исключение. Но потом я подумал, что в этом есть смысл. Потому что мы можем думать о генераторах как об объекте с функцией __next__. Таким образом, каждый генератор nondivsbyk является объектом одного и того же класса (только с другой инициализацией). Предположим, мы называем это class_nondivsbyk, поэтому, когда один вызывает другого, он фактически class_nondivsbyk.__next__ вызывает другого class_nondivsbyk.__next__ для другого объекта. - person tamir; 08.10.2020
comment
О 100-м простом числе, требующем только первых 10 простых чисел, поэтому сначала я могу сказать, что (пока я не хочу указывать верхний предел) нам нужно «собирать» простые числа на пути, поэтому создание этих генераторов кажется необходимым. . На данный момент я действительно не знаю, могу ли я «пропустить» те, которые не имеют отношения к вычислению. Потому что теперь я позволяю каждому проверять, является ли он разделителем, но если я отложу их в сторону, мне понадобится что-то еще, чтобы «запускать» их, когда числа увеличиваются, и я не знаю, как интегрировать это в рекурсию. Я тоже сделал плоский вариант, могу посмотреть там. Спасибо @WillNess - person tamir; 08.10.2020
comment
два nd после присвоения nd = nondivsbyk(p, nd) должны быть двумя разными объектами. т.е. nd - это сначала переменная, относящаяся к объекту; затем новый объект-генератор создается вызовом функции и назначается той же переменной (которая забывает свое старое значение). но внутри новый объект-генератор относится к более старому - другому - объекту. но, как я уже сказал, я не знаю Python. насчет 10 простых чисел против 100 - вот подсказка: надеюсь, каждый вызов primes() создает отдельный новый объект-генератор. (или какова правильная терминология?) - person Will Ness; 08.10.2020
comment
вот еще несколько советов: :) в python, haskell, go. последний прямо соответствует тому, что вы здесь написали. (!) следующая запись - это то, что я имел в виду. :) ваше здоровье. - person Will Ness; 11.02.2021

Я сделал однострочную версию Сита Эратосфена

sieve = lambda j: [print(x) for x in filter(lambda n: 0 not in map(lambda i: n % i, range(2, n)) and (n!=1)&(n!=0), range(j + 1))]

Что касается производительности, я почти уверен, что это не самая быстрая вещь в любом случае, а с точки зрения читаемости / следования PEP8 это довольно ужасно, но это скорее новизна длины, чем что-либо еще.

РЕДАКТИРОВАТЬ: Обратите внимание, что это просто печатает сито и не возвращает (если вы попытаетесь распечатать его, вы получите список Nones, если вы хотите вернуться, измените print (x) в понимании списка только на x.

person pythoteric    schedule 06.06.2021

я думаю, что это самый короткий код для поиска простых чисел методом эратосфена

def prime(r):
    n = range(2,r)
    while len(n)>0:
        yield n[0]
        n = [x for x in n if x not in range(n[0],r,n[0])]


print(list(prime(r)))
person Pythoscorpion    schedule 06.05.2019
comment
Однако производительность просто ужасная. На каждой итерации создается новый список. - person Eric Duminil; 28.10.2020

не уверен, что мой код эффективен, кто-нибудь хочет прокомментировать?

from math import isqrt

def isPrime(n):
    if n >= 2: # cheating the 2, is 2 even prime?
        for i in range(3, int(n / 2 + 1),2): # dont waste time with even numbers
            if n % i == 0:
                return False
    return True

def primesTo(n): 
    x = [2] if n >= 2 else [] # cheat the only even prime
    if n >= 2:
        for i in range(3, n + 1,2): # dont waste time with even numbers
            if isPrime(i):
                x.append(i)  
    return x

def primes2(n): # trying to do this using set methods and the "Sieve of Eratosthenes"
    base = {2} # again cheating the 2
    base.update(set(range(3, n + 1, 2))) # build the base of odd numbers
    for i in range(3, isqrt(n) + 1, 2): # apply the sieve
        base.difference_update(set(range(2 * i, n + 1 , i)))
    return list(base)

print(primesTo(10000)) # 2 different methods for comparison
print(primes2(10000))
person lonny    schedule 04.07.2020

Вероятно, самый быстрый способ получить первичные числа - это следующий:

import sympy
list(sympy.primerange(lower, upper+1))

Если вам не нужно их хранить, просто используйте приведенный выше код без преобразования в list. sympy.primerange - это генератор, поэтому он не потребляет память.

person Prokhozhii    schedule 28.11.2020
comment
Пожалуйста, объясните в тексте своего ответа, почему это необходимо и какие улучшения это принесет, чтобы он выглядел значимым ответом. - person dmitryro; 28.11.2020

Использование рекурсии и оператора моржа:

def prime_factors(n):
    for i in range(2, int(n ** 0.5) + 1):
        if (q_r := divmod(n, i))[1] == 0:
            return [i] + factor_list(q_r[0])
    return [n]
person Vlad Bezden    schedule 07.02.2021

person    schedule
comment
вместо списка я бы использовал набор, чтобы ускорить тест на членство - person Copperfield; 04.09.2016
comment
Последний вывод показывает «Нет», как я могу его удалить? - person LordOfThunder; 17.09.2018