Подтвердить что ты не робот

Python-сито из Eratosthenes-Compact Python

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

list = [i for i in range(2, int(raw_input("Compute primes up to what number? "))+1)]  

for i in list:
    for a in list:
            if a!=i and a%i == 0:
                list.remove(a)

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

Думая о чем-то подобном:

print map(list.remove(a), filter(lambda a, i: (a%i ==0 and a!=i), [(a, i) for i in list for a in list])

Очевидно, что это не работает по десяткам причин. Если я просто использовал часть фильтра этого кода:

filter(lambda a, i: (a%i ==0 and a!=i), **[(a, i) for i in list for a in list]**

Каков правильный способ размещения двух переменных в лямбда? (a, i) делает его кортежем, но я хочу представить 'a' и 'i' в качестве независимых переменных для ввода в лямбда.

В результате я решил решить эту проблему одним слоем:

print sorted(set([i for i in range(2, int(raw_input("Compute primes up to what number? "))+1)]).difference(a for i in l for a in l if a!=i and a%i == 0))
4b9b3361

Ответ 1

Это не совсем прямой перевод ваших циклов, но он довольно близкий и компактный:

>>> l = range(2, 101)
>>> sorted(set(l).difference(a for i in l for a in l if a!=i and a%i == 0))
[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]

Хотя я бы предложил a > i вместо a != 0 быть короче и быстрее;)

Ответ 2

Первое, что нужно отметить, это то, что вы написали, это не сито эратосфенов. Посмотрите, сколько циклов исполняет абсолютно наивное сито эратосфенов:

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])

Ответ 3

Ты не делаешь сито Эратосфена; опасность неправильного внедрения алгоритма заключается в том, что он будет очень медленным. Попробуйте свой алгоритм на 10**6, например.

Кратчайшая реализация ограниченного сита эратосфенов. Я могу придумать:

def primes(upTo):
    isPrime = list(range(upTo))
    for p in range(2,int(upTo**0.5)+1): #p: 2,3,4,...,sqrt(N)
        print(p, isPrime[p])
        if isPrime[p]:
            for multiple in range(p**2,upTo,p): #mult: p^2, p^2+p, p^2+2p, ..., N
                isPrime[multiple] = False
    return [x for x in isPrime[2:] if x]

Демо:

>>> list(primes(29))
[2, 3, 5, 7, 11, 13, 17, 19, 23]

Это на самом деле довольно лаконично, если вы проигнорируете линейные разрывы и массивные оптимизации числа пропущенных чисел:

isPrime=[True]*upTo for p in range(2,upTo): if isPrime[p]: yield p for m in range(p,upTo,p): isPrime[m]=False

Ответ 4

Следующий однострочный шрифт никак не связан с вашим кодом:

def primes(n):
    return set(range(2,n))-{c for i in range(2,n) for c in range(2*i,n,i)}

Как и ваш код, это по-прежнему не реально Сито Эратосфена, потому что, например, он будет бесполезно сбрасывать кратные 6 и 9 и т.д. Тем не менее он все еще работает значительно быстрее, чем у большинства других ситовых искажений при значениях менее миллиона или более, так как при малом N число "примерно столько же", что и в простых числах (доля чисел < N, которые являются первичными, равна 1/log(N)),.

Сильно измененный из источника, возможно, менее эффективный, чем оригинал: http://codeblog.dhananjaynene.com/2011/06/10-python-one-liners-to-impress-your-friends/

Ответ 5

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

import itertools

def primes():
    ints = itertools.count(2)
    while True:
        p = next(ints)
        yield p
        ints = itertools.ifilter(p.__rmod__, ints)

print list(itertools.islice(primes(), 10))
# [2, 3, 5, 7, 11, 13, 17, 19, 23, 29]

Ответ 6

def sieve(n):
    sieve_list = range(n)
    zero_list = [0] * n
    for i in range(2, int(n**.5) + 1):
        if sieve_list[i]:
            sieve_list[2*i:n:i] = zero_list[2*i:n:i]
    return filter(None, sieve_list)[1:]

Ответ 7

Вот самое компактное истинное сито, которое я придумал до сих пор. Это работает на удивление хорошо.

def pgen(n): # Sieve of Eratosthenes generator
    np = set() # keeps track of composite (not prime) numbers
    for q in xrange(2, n+1):
        if q not in np:
            yield q
            np.update(range(q*q, n+1, q))

>>> list(pgen(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]            

Эта немного более сложная версия является самой быстрой, что я видел:

def pgen(n): # Sieve of Eratosthenes generator by Dan Salmonsen
    yield 2
    np = set()
    for q in xrange(3, n+1, 2):
        if q not in np:
            yield q
            np.update(range(q*q, n+1, q+q))            

Вот истинное сито как понимание списка:

def primes(n):
    sieve = set(sum([range(q*q, n+1, q+q) for q in odds], []))
    return [2] + [p for p in odds if p not in sieve]

>>> primes(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]