2012-02-15 4 views
5

Является ли следующий код для генерации простых чисел pythonic?- это генератор простых чисел pythonic

def get_primes(n): 
    primes=[False,False]+[True]*(n-1) 
    next_p=(i for i,j in enumerate(primes) if j) 
    while True: 
     p=next(next_p) 
     yield p 
     primes[p*p::p]=[False]*((n-p*p)//p+1) 

Обратите внимание, что следующий (next_p), в конечном счете бросить ошибку StopIteration, которая каким-то образом заканчивает функции get_primes. Это плохо?

Также обратите внимание, что next_p - это генератор, который выполняет итерацию по простым числам, однако переменные меняются во время итерации. Это плохой стиль?

добавив следующее, если оператор получает под 0,25 секунды для первого миллиона простых чисел:

if p*p<=n: 
    primes[p*p::p]=[False]*((n-p*p)//p+1) 
+1

вы можете сохранить одну строку, если вы хотите с помощью 'простых чисел = [Ложные, Ложные] + [Правда] * (п-1)', а также усложняя можно оптимизировать, чтобы использовать половину сито, пропустить даже номера , см. http://stackoverflow.com/a/3035188/464543 – ChessMaster

+0

спасибо @ChessMaster –

+1

проверьте свой код на 0,1,2,3 без строки 'if p * p <= n:' ... на моей машине, что строка не нужна – ChessMaster

ответ

3

Это не плохо, что next(next_p) бросает ошибку StopIteration - это то, что генератор всегда делает, когда он выбегает из пунктов !

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

Одно небольшое наблюдение: когда вы «вычеркиваете» кратные простые числа, вы найдете, если подумаете об этом немного, что вам не обязательно начинать с p * 2. Вы можете перейти к p ** 2.

+0

Ах, конечно, перейдите к p ** 2, так как все меньшие простые числа уже найдены. Я изменю его спасибо –

2

Нет ничего плохого в StopIteration, действительно, это ожидаемое поведение генераторов.

Я бы сказал, что эта реализация более вещий (не обязательно более эффективным):

def get_primes(n): 
    """Generates prime numbers < n""" 
    return (x for x in xrange(2,n) if all(x % i for i in xrange(2,x))) 

Pythonic мне означает ясным, кратким и содержательным, используя сильные стороны языка. Хотя я вижу, что ваша реализация - это какое-то сито, я знаю только это из предыдущего опыта с такими алгоритмами. Выполнение выше, я могу читать непосредственно как прямое испытание делимости.


Примечание: есть небольшая разница в интерфейсе, ваша реализация даст простые числа < = п, тогда как моя реализация даст простые числа < п. Очевидно, что это можно легко и тривиально изменить (просто измените n на n + 1 в теле функции), но я чувствую, что более pythonic более эффективно генерировать простые числа, но не включать n, чтобы быть более согласованным с тем, скажем , range() сборник работ.


EDIT: JUST FOR FUN

Вот вещий реализация минимум, и, вероятно, довольно неэффективный тоже :)

def get_primes(n): 
    import re 
    return (x for x in xrange(2,n) if re.match(r'^1?$|^(11+?)\1+$', '1' * x) is None) 

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

+1

Спасибо, помощник. Да, это будет очень медленным, если n будет намного больше 1000, но его легко прочитать. –

+1

Примечание. Я просто пытаюсь ответить на _pythonic_, если вас интересует способ _fastest_ в python, этот вопрос был хорошо освещен [здесь] (http://stackoverflow.com/questions/2068372/fastest-way-to -list-все-простые числа-ниже-п-в-питон). – wim

+0

yup, я бы написал что-то вроде этого, если бы мне быстро понадобились небольшие простые числа, или если у меня была 0 памяти. Я использовал вашу идею, чтобы представить еще один ответ, который не совсем как пифонический, но немного более эффективный. –

0

Вот еще несколько несколько pythonic решений, мотивированных @wim, однако вы можете видеть, что он немного медленнее первого метода.

def get_primes2(n): 
    primes=[] 
    for i in range(2,n+1): 
     small_primes=(p for p in primes if p*p<=i) 
     if all(i%p for p in small_primes): 
      yield i 
      primes.append(i) 

import timeit 
print(timeit.timeit("list(get_primes(10**5))",number=5,setup="from __main__ import get_primes")/5.0) 
"0.0350940692182945" 
print(timeit.timeit("list(get_primes2(10**5))",number=5,setup="from __main__ import get_primes2")/5.0) 
"8.226938898658908" 
+0

, на который ссылается здесь 'get_primes'? – wim

+0

один в моем вопросе. Если вы добавите, если p * p <= n, вы получите 0.025. wim, я не пробовал ваш, я предположил, что это будет очень медленно :) –

+0

да, я думаю, что это будет примерно в 500 раз медленнее, lol – wim

Смежные вопросы