Код простого числа Python работает медленно

Я пытаюсь решить проблему, упомянутую здесь: https://www.spoj.pl/problems/PRIME1/

Я также даю описание ниже.

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

Вход

Ввод начинается с количества t тестов в одной строке (t‹=10). В каждой из следующих t строк через пробел записаны два числа m и n (1 ‹= m ‹= n ‹= 1000000000, n-m‹=100000).

Выход

Для каждого теста выведите все простые числа p такие, что m ‹= p ‹= n, по одному числу в строке, тесты разделены пустой строкой.`

Мой код, как показано ниже. Я думаю, что метод удаления из списка очень медленный.

import sys
import math

num = int(sys.stdin.readline());
indices = []
maxrange = 2
while(num > 0):
    a,b = sys.stdin.readline().split(" ");
    a = int(a)
    b = int(b)
    if(a < 2):
        a = 2
    indices.append((a,b))
    if(b > maxrange):
        maxrange= b          
    num = num - 1

val = int(math.sqrt(maxrange)+1)
val2 = int(math.sqrt(val)+1)
checks = range(2,val2)

for i in range(2,val2):
    for j in checks:
        if(i!= j and j%i == 0):
            checks.remove(j)

primes = range(2,val)
for i in checks:
    for j in primes:
        if(i != j and j%i == 0):
            primes.remove(j)

primes2 = range(2,maxrange)
for i in primes:
    for j in primes2:
        if(j != i and j%i == 0):
            primes2.remove(j)

for (a,b) in indices:
    for p in primes2:
        if(a<= p and b >= p):
            print p
        if(p > b):
            break
    print

Я думаю, что удаление списка python происходит очень медленно. Мой код правильный, но у меня превышен лимит времени. может кто-нибудь помочь мне улучшить этот код.


person Boolean    schedule 06.03.2011    source источник
comment
Вы его профилировали?   -  person nmichaels    schedule 06.03.2011
comment
Я этого не делал.. позволь мне сделать это.   -  person Boolean    schedule 06.03.2011


Ответы (4)


Вместо того, чтобы удалять элемент, который не является простым числом, почему бы не заменить его каким-нибудь контрольным значением, например -1 или None? Затем при печати просто печатайте значения, которые не являются часовыми.

Используйте список длиной (n-m), а затем индекс для номера i равен x[m+i].

person dappawit    schedule 06.03.2011
comment
как его заменить..у меня нет индекса в списке - person Boolean; 06.03.2011
comment
Моя ошибка, я слишком быстро посмотрел на ваш код. Я считаю, что это метод индекса в списках. Однако это было бы линейно по длине списка для каждого поиска, поэтому само по себе это может быть довольно дорого... Но, зная нижнюю границу, вы могли бы иметь список, в котором I находится в lower_bound + I, и просто использовать поиск по индексу - person dappawit; 06.03.2011
comment
это действительно правильный путь, даже если вам придется изменить алгоритм, чтобы у вас был индекс. - person ; 06.03.2011

Функция проверки простоты будет работать лучше всего. Псевдокод есть на страница википедии Миллера-Рабина

person Community    schedule 06.03.2011
comment
Разве это не вероятностный ответ? На сто тысяч, думаю, потребуется значительное время. - person Boolean; 07.03.2011
comment
Если вы перейдете по ссылке на страницу википедии, вы увидите, что существует детерминированный, эффективный вариант, который работает для интересующего вас диапазона чисел. Вам не нужно проверять все 100 тысяч чисел, например, вам нужно только рассмотрим те, которые равны 1 или 5 по модулю 6. - person ; 07.03.2011
comment
Таким образом, ваш детерминистский вариант должен проверять 1/3 числа от того числа, что и наивная проверка 100 000. Таким образом, вариант должен быть не более чем в 3 раза быстрее простой проверки 100K, чтобы быть быстрее. Что ж, вы можете оптимизировать наивную проверку 100 000 и делать нечетные числа только после 2. Таким образом, отношение будет 1/3N к 1/2N (и если вы сбрасываете со счетов что-либо, оканчивающееся на 5, когда › 5, вы получите 1/ 3N на 2/5N — так что ваше преимущество в скорости сократится до 20% или около того). - person typo.pl; 07.03.2011
comment
@typo Вы уверены, что прочитали вопрос? Это для генерации простых чисел между заданным m и n, а не для генерации всех простых чисел ниже 100K. - person ; 07.03.2011
comment
Да, я прочитал вопрос. Вам даны два числа ( m ‹= n ‹= 1000000000), разница которых может быть не более 100 000. Итак, в худшем случае нужно проверить 100 000 чисел, используя наивный метод. - person typo.pl; 08.03.2011

remove() не является медленным по большому счету, просто код называет его МНОГО.

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

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

Ошибка с вашим исходным кодом заключается в том, что вы используете range(2,maxrange). Это означает, что maxrange нет в списке рассматриваемых номеров. Попробуйте 3 5 в качестве входных данных для a и b, чтобы увидеть ошибку.

range(2,maxrange+1) устраняет проблему.

Еще одна ошибка в коде заключается в том, что вы изменяете исходную последовательность:

Из документов по Python — for-statement

Небезопасно изменять повторяющуюся в цикле последовательность (это может произойти только для изменяемых типов последовательностей, таких как списки). Если вам нужно изменить список, по которому вы выполняете итерацию (например, чтобы дублировать выбранные элементы), вы должны выполнить итерацию по копии. Обозначение среза делает это особенно удобным:

Мои навыки работы с Python рудиментарны, но, похоже, это работает:

Старый:

primes2 = range(2,maxrange)
for i in primes:
     for j in primes2:
         if(j != i and j%i == 0):
             primes2.remove(j)

for (a,b) in indices:
    for p in primes2:
        if(a<= p and b >= p):

Новый:

primes2 = array.array('L', range(minrange,maxrange+1))
for i in primes:
    for j in primes2:
        if(j != i and j%i == 0):
            primes2[j-minrange] = 0

for (a,b) in indices:
    for p in primes2:
        if (p != 0):
            if(a<= p and b >= p):

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

person typo.pl    schedule 06.03.2011

Вот детерминированный вариант теста Миллера-Рабина на простоту для небольших нечетные целые числа в Python:

from math import log

def isprime(n):
    assert 1 < n < 4759123141 and n % 2 != 0, n

    # (n-1) == 2**s * d
    s = 0
    d = n-1
    while d & 1 == 0:
        s += 1
        d >>= 1
    assert d % 2 != 0 and (n-1) == d*2**s

    for a in [2, 7, 61]:
        if not 2 <= a <= min(n-1, int(2*log(n)**2)):
            break
        if (pow(a, d, n) != 1 and
            all(pow(a, d*2**r, n) != (n-1) for r in xrange(s))):
            return False
    return True

Намерение кода состоит в том, чтобы быть исполняемым псевдокодом.

person jfs    schedule 09.03.2011