Оптимизация алгоритма решета Эратосфена Python

Я пытаюсь реализовать Решето Эратосфена. Вывод кажется правильным (минус «2», которое необходимо добавить), но если входные данные функции больше 100 КБ или около того, это, по-видимому, займет слишком много времени. Как я могу оптимизировать эту функцию?

def sieveErato(n):
     numberList = range(3,n,2)

     for item in range(int(math.sqrt(len(numberList)))):
            divisor = numberList[item]
            for thing in numberList:
                    if(thing % divisor == 0) and thing != divisor:
                            numberList.remove(thing)
    return numberList

person pri0ritize    schedule 28.01.2012    source источник
comment
возможный дубликат Сито Эратосфена - Поиск простых чисел Python   -  person MAK    schedule 28.01.2012
comment
мы могли бы начать с построения графика зависимости времени разрешения от n, это может дать нам некоторые идеи ...   -  person jimifiki    schedule 28.01.2012
comment
Ознакомьтесь с stackoverflow.com/questions/2068372/   -  person Jason Sundram    schedule 28.01.2012
comment
Спасибо, Джейсон, я прочитал этот пост пару дней назад и с трудом работал с данной функцией Эратосфена. Думаю, теперь я понимаю, чего мне не хватало.   -  person pri0ritize    schedule 28.01.2012


Ответы (6)


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

def sieve(n):
    m = (n-1) // 2
    b = [True]*m
    i,p,ps = 0,3,[2]
    while p*p < n:
        if b[i]:
            ps.append(p)
            j = 2*i*i + 6*i + 3
            while j < m:
                b[j] = False
                j = j + 2*i + 3
        i+=1; p+=2
    while i < m:
        if b[i]:
            ps.append(p)
        i+=1; p+=2
    return ps

Мы просеиваем только нечетные числа, останавливаясь на квадратном корне из n. Странные вычисления на j отображают между просеиваемыми целыми числами 3, 5, 7, 9, ... и индексами 0, 1, 2, 3, ... в b массив бит.

Вы можете увидеть эту функцию в действии на странице http://ideone.com/YTaMB, где она вычисляет простые числа до миллион менее чем за секунду.

person user448810    schedule 28.01.2012
comment
простая реализация должна выглядеть этой. - person jfs; 29.01.2012

Вы можете попробовать то же самое, что и Эратосфен. Возьмите массив со всеми числами, которые нужно проверять по возрастанию, перейдите к номеру 2 и отметьте его. Теперь очищайте каждое второе число до конца массива. Затем перейдите к пункту 3 и отметьте его. После этого сотрите каждое третье число. Затем переходите к 4 - он уже поцарапан, так что пропустите. Повторите это для каждого n + 1, который еще не поцарапан.

В конце концов, отмеченные числа являются простыми. Этот алгоритм работает быстрее, но иногда требуется много памяти. Вы можете немного оптимизировать его, отбросив все четные числа (потому что они не простые) и вручную добавить 2 в список. Это немного исказит логику, но займет половину памяти.

Вот иллюстрация того, о чем я говорю: http://en.wikipedia.org/wiki/Sieve_of_Eratosthenes

person anthares    schedule 28.01.2012
comment
если вы последуете этому процессу, вы отметите каждое число более одного раза. их можно проверить только один раз. смотри мой ответ. - person Dan D.; 28.01.2012

Предупреждение: удаление элементов из итератора во время итерации может быть опасным ...

Вы могли бы сделать

    if(thing % divisor == 0) and thing != divisor:

проверьте зажигалку, разделив ее на цикл, который прерывается, когда вы дойдете до индекса 'divisor', а затем тест:

for thing in numberList_fromDivisorOn:
    if(thing % divisor == 0):
        numberList.remove(thing)
person jimifiki    schedule 28.01.2012

Этот код занимает 2 секунды для генерации простых чисел менее 10M (это не мой, я нашел его где-то в Google)

def erat_sieve(bound):
    if bound < 2:
        return []
    max_ndx = (bound - 1) // 2
    sieve = [True] * (max_ndx + 1)
    #loop up to square root
    for ndx in range(int(bound ** 0.5) // 2):
        # check for prime
        if sieve[ndx]:
            # unmark all odd multiples of the prime
            num = ndx * 2 + 3
            sieve[ndx+num:max_ndx:num] = [False] * ((max_ndx-ndx-num-1)//num + 1)
    # translate into numbers
    return [2] + [ndx * 2 + 3 for ndx in range(max_ndx) if sieve[ndx]]
person Luka Rahne    schedule 28.01.2012

Я перешел по этой ссылке: Сито Эратосфена - поиск простых чисел Python, как было предложено @ MAK, и я обнаружил, что принятый ответ можно улучшить с помощью идеи, которую я нашел в вашем коде:

def primes_sieve2(limit):
    a = [True] * limit               # Initialize the primality list
    a[0] = a[1] = False
    sqrt = int(math.sqrt(limit))+1
    for i in xrange(sqrt):
        isprime = a[i]
        if isprime:
            yield i
            for n in xrange(i*i, limit, i):     # Mark factors non-prime
                a[n] = False
    for (i, isprime) in enumerate(a[sqrt:]):
        if isprime:
            yield i+sqrt
person jimifiki    schedule 28.01.2012
comment
Это возвращает [2, 3, 5, 7, 1, 3, 7, 9, 13, 19, 21, 27, 31, 33, 37, 43, 49, 51, 57, 61, 63, 69, 73, 79 , 87] для limit = 100. Мало того, что есть странное дублирование, но многие из этих терминов не являются простыми. - person DSM; 28.01.2012

если даны неограниченные память и время, следующий код напечатает все простые числа. И сделает это без пробного деления. он основан на коде haskell в статье: Подлинное сито Эратосфена Мелисса Э. О'Нил

from heapq import heappush, heappop, heapreplace
def sieve():
    w = [2,4,2,4,6,2,6,4,2,4,6,6,2,6,4,2,6,4,6,8,4,2,4,2,4,8,6,4,6,2,4,6,2,6,6,4,2,4,6,2,6,4,2,4,2,10,2,10]
    for p in [2,3,5,7]: print p
    n,o = 11,0
    t = []
    l = len(w)
    p = n
    heappush(t, (p*p, n,o,p))
    print p
    while True:
        n,o = n+w[o],(o+1)%l
        p = n
        if not t[0][0] <= p:
            heappush(t, (p*p, n,o,p))
            print p
            continue
        while t[0][0] <= p:
            _, b,c,d = t[0]
            b,c = b+w[c],(c+1)%l
            heapreplace(t, (b*d, b,c,d))
sieve()
person Dan D.    schedule 28.01.2012
comment
Мало того, что код не печатает 11, он, кажется, печатает много простых чисел, кратных 11 [121, 143, 187 и т. Д.] - person DSM; 28.01.2012
comment
Ой, я этого не видел, но исправить это просто. - person Dan D.; 29.01.2012
comment
Что, если вы распечатаете все простые числа до 100? Ты вообще своей кучей пользовался? Его начальная запись - 121. Это не нужно, пока вы не проверите 121, а также следующие записи всех простых чисел ниже 100, которые заполняют вашу кучу при достижении 100 - но ни одна из них еще не нужна. Это серьезный недостаток кода из статьи, который вы здесь точно воссоздали. Запись для простого числа следует добавлять в кучу только тогда, когда виден кандидат таким образом, что он равен квадрату этого простого числа. Таким образом, для вывода n простых чисел требуется O (n) вместо O (sqrt (n / log (n))). - person Will Ness; 07.05.2012
comment
(продолжение), и поскольку ему приходится манипулировать гораздо большей кучей, чем это действительно необходимо, он также будет работать медленнее. Чтобы произвести 1000000 простых чисел, вам нужно проверить не более 550 простых чисел; но ваш код будет хранить в куче 999 999 записей. Мелисса О'Нил исправила эту проблему в своем распространении кода ZIP-файл. На странице простых чисел Haskellwiki тоже есть свое мнение. - person Will Ness; 07.05.2012