Przyspieszyć bitstring / operacje bitowe w Pythonie?

Napisałem generator liczb pierwszych używając Sita Eratostenesa i Pythona 3.1. Kod działa poprawnie i z wdziękiem W 0.32 sekundy na ideone.com Aby wygenerować liczby pierwsze do 1,000,000.

# from bitstring import BitString

def prime_numbers(limit=1000000):
    '''Prime number generator. Yields the series
    2, 3, 5, 7, 11, 13, 17, 19, 23, 29 ...    
    using Sieve of Eratosthenes.
    '''
    yield 2
    sub_limit = int(limit**0.5) 
    flags = [False, False] + [True] * (limit - 2)   
#     flags = BitString(limit)
    # Step through all the odd numbers
    for i in range(3, limit, 2):       
        if flags[i] is False:
#        if flags[i] is True:
            continue
        yield i
        # Exclude further multiples of the current prime number
        if i <= sub_limit:
            for j in range(i*3, limit, i<<1):
                flags[j] = False
#                flags[j] = True
Problem w tym, że kończy mi się pamięć, gdy próbuję wygenerować liczby do 1,000,000,000.
    flags = [False, False] + [True] * (limit - 2)   
MemoryError

Jak można sobie wyobrazić, przydzielanie 1 miliarda wartości logicznych ( 1 bajt 4 lub 8 bajtów (patrz komentarz) każdy w Pythonie) jest naprawdę niewykonalne, więc spojrzałem do bitstring . Pomyślałem, że użycie 1 bitu dla każdej flagi będzie znacznie bardziej wydajne w pamięci. Jednak wydajność programu spadła drastycznie - czas trwania 24 sekund, dla liczby pierwszej do 1 000 000. Jest to prawdopodobnie spowodowane wewnętrzną implementacją bitstring.

Możesz skomentować / odkomentować trzy linie, aby zobaczyć, co zmieniłem, aby użyć BitString, jak urywek kodu powyżej.

Moje pytanie brzmi: czy istnieje sposób na przyspieszenie mojego programu, Z czy bez bitstring?

Edit: proszę przetestować kod samodzielnie przed wysłaniem. Nie mogę zaakceptować odpowiedzi, które działają wolniej niż mój istniejący kod, oczywiście.

Edit again:

Sporządziłem listę benchmarków na mojej maszynie.

Author: Community, 2010-05-24

11 answers

Istnieje kilka małych optymalizacji dla twojej wersji. Odwracając role prawdy i fałszu, możesz zmienić "if flags[i] is False: " na " if flags[i]:". A wartością początkową dla drugiej instrukcji range może być i*i zamiast i*3. Twoja Oryginalna wersja trwa 0.166 sekund na moim systemie. Z tymi zmianami, Wersja poniżej trwa 0.156 sekund na moim systemie.

def prime_numbers(limit=1000000):
    '''Prime number generator. Yields the series
    2, 3, 5, 7, 11, 13, 17, 19, 23, 29 ...
    using Sieve of Eratosthenes.
    '''
    yield 2
    sub_limit = int(limit**0.5)
    flags = [True, True] + [False] * (limit - 2)
    # Step through all the odd numbers
    for i in range(3, limit, 2):
        if flags[i]:
            continue
        yield i
        # Exclude further multiples of the current prime number
        if i <= sub_limit:
            for j in range(i*i, limit, i<<1):
                flags[j] = True
To nie pomaga w problemach z pamięcią.

Przechodząc do świata rozszerzeń C, użyłem wersja rozwojowa gmpy . (Zastrzeżenie: jestem jednym z opiekunów.) Wersja rozwojowa nosi nazwę gmpy2 i obsługuje zmienną liczbę całkowitą o nazwie xmpz. Używając gmpy2 i poniższego kodu, mam czas działania 0.140 sekund. Czas trwania limitu 1,000,000,000 wynosi 158 sekund.

import gmpy2

def prime_numbers(limit=1000000):
    '''Prime number generator. Yields the series
    2, 3, 5, 7, 11, 13, 17, 19, 23, 29 ...
    using Sieve of Eratosthenes.
    '''
    yield 2
    sub_limit = int(limit**0.5)
    # Actual number is 2*bit_position + 1.
    oddnums = gmpy2.xmpz(1)
    current = 0
    while True:
        current += 1
        current = oddnums.bit_scan0(current)
        prime = 2 * current + 1
        if prime > limit:
            break
        yield prime
        # Exclude further multiples of the current prime number
        if prime <= sub_limit:
            for j in range(2*current*(current+1), limit>>1, prime):
                oddnums.bit_set(j)

Naciskając optymalizacje i poświęcając jasność, uzyskuję czasy działania 0.107 i 123 sekund z następującym kodem:

import gmpy2

def prime_numbers(limit=1000000):
    '''Prime number generator. Yields the series
    2, 3, 5, 7, 11, 13, 17, 19, 23, 29 ...
    using Sieve of Eratosthenes.
    '''
    yield 2
    sub_limit = int(limit**0.5)
    # Actual number is 2*bit_position + 1.
    oddnums = gmpy2.xmpz(1)
    f_set = oddnums.bit_set
    f_scan0 = oddnums.bit_scan0
    current = 0
    while True:
        current += 1
        current = f_scan0(current)
        prime = 2 * current + 1
        if prime > limit:
            break
        yield prime
        # Exclude further multiples of the current prime number
        if prime <= sub_limit:
            list(map(f_set,range(2*current*(current+1), limit>>1, prime)))

Edit: na podstawie tego ćwiczenia zmodyfikowałem gmpy2 do przyjęcia xmpz.bit_set(iterator). Używając poniższego kodu, czas uruchomienia wszystkich liczb pierwszych mniejszy niż 1,000,000,000 wynosi 56 sekund dla Pythona 2.7 i 74 sekundy dla Pythona 3.2. (Jak zaznaczono w komentarzach, xrange jest szybszy niż range.)

import gmpy2

try:
    range = xrange
except NameError:
    pass

def prime_numbers(limit=1000000):
    '''Prime number generator. Yields the series
    2, 3, 5, 7, 11, 13, 17, 19, 23, 29 ...
    using Sieve of Eratosthenes.
    '''
    yield 2
    sub_limit = int(limit**0.5)
    oddnums = gmpy2.xmpz(1)
    f_scan0 = oddnums.bit_scan0
    current = 0
    while True:
        current += 1
        current = f_scan0(current)
        prime = 2 * current + 1
        if prime > limit:
            break
        yield prime
        if prime <= sub_limit:
            oddnums.bit_set(iter(range(2*current*(current+1), limit>>1, prime)))

Edit # 2: Jeszcze jedna próba! Zmodyfikowałem gmpy2, aby zaakceptować xmpz.bit_set(slice). Używając poniższego kodu, czas uruchomienia wszystkich liczb pierwszych mniejszy niż 1,000,000,000 wynosi około 40 sekund zarówno dla Pythona 2.7, jak i Pythona 3.2.

from __future__ import print_function
import time
import gmpy2

def prime_numbers(limit=1000000):
    '''Prime number generator. Yields the series
    2, 3, 5, 7, 11, 13, 17, 19, 23, 29 ...
    using Sieve of Eratosthenes.
    '''
    yield 2
    sub_limit = int(limit**0.5)
    flags = gmpy2.xmpz(1)
    # pre-allocate the total length
    flags.bit_set((limit>>1)+1)
    f_scan0 = flags.bit_scan0
    current = 0
    while True:
        current += 1
        current = f_scan0(current)
        prime = 2 * current + 1
        if prime > limit:
            break
        yield prime
        if prime <= sub_limit:
            flags.bit_set(slice(2*current*(current+1), limit>>1, prime))

start = time.time()
result = list(prime_numbers(1000000000))
print(time.time() - start)

Edit # 3: zaktualizowałem gmpy2, aby poprawnie obsługiwać krojenie na poziomie bitowym xmpz. Bez zmian w wydajności, ale dużo ładny API. Zrobiłem małe poprawki i mam czas do około 37 sekund. (Patrz Edycja # 4 do zmian w gmpy2 2.0. 0B1.)

from __future__ import print_function
import time
import gmpy2

def prime_numbers(limit=1000000):
    '''Prime number generator. Yields the series
    2, 3, 5, 7, 11, 13, 17, 19, 23, 29 ...
    using Sieve of Eratosthenes.
    '''
    sub_limit = int(limit**0.5)
    flags = gmpy2.xmpz(1)
    flags[(limit>>1)+1] = True
    f_scan0 = flags.bit_scan0
    current = 0
    prime = 2
    while prime <= sub_limit:
        yield prime
        current += 1
        current = f_scan0(current)
        prime = 2 * current + 1
        flags[2*current*(current+1):limit>>1:prime] = True
    while prime <= limit:
        yield prime
        current += 1
        current = f_scan0(current)
        prime = 2 * current + 1

start = time.time()
result = list(prime_numbers(1000000000))
print(time.time() - start)

Edit # 4: wprowadziłem kilka zmian w gmpy2 2.0. 0B1, które łamią poprzedni przykład. gmpy2 nie traktuje już True jako specjalnej wartości, która zapewnia nieskończone źródło 1-bitów. -1 należy użyć zamiast tego.

from __future__ import print_function
import time
import gmpy2

def prime_numbers(limit=1000000):
    '''Prime number generator. Yields the series
    2, 3, 5, 7, 11, 13, 17, 19, 23, 29 ...
    using Sieve of Eratosthenes.
    '''
    sub_limit = int(limit**0.5)
    flags = gmpy2.xmpz(1)
    flags[(limit>>1)+1] = 1
    f_scan0 = flags.bit_scan0
    current = 0
    prime = 2
    while prime <= sub_limit:
        yield prime
        current += 1
        current = f_scan0(current)
        prime = 2 * current + 1
        flags[2*current*(current+1):limit>>1:prime] = -1
    while prime <= limit:
        yield prime
        current += 1
        current = f_scan0(current)
        prime = 2 * current + 1

start = time.time()
result = list(prime_numbers(1000000000))
print(time.time() - start)

Edit # 5: wprowadziłem kilka ulepszeń do gmpy2 2.0. 0B2. Możesz teraz powtarzaj wszystkie bity, które są albo ustawione, albo wyczyszczone. Czas pracy poprawił się o ~30%.

from __future__ import print_function
import time
import gmpy2

def sieve(limit=1000000):
    '''Returns a generator that yields the prime numbers up to limit.'''

    # Increment by 1 to account for the fact that slices do not include
    # the last index value but we do want to include the last value for
    # calculating a list of primes.
    sieve_limit = gmpy2.isqrt(limit) + 1
    limit += 1

    # Mark bit positions 0 and 1 as not prime.
    bitmap = gmpy2.xmpz(3)

    # Process 2 separately. This allows us to use p+p for the step size
    # when sieving the remaining primes.
    bitmap[4 : limit : 2] = -1

    # Sieve the remaining primes.
    for p in bitmap.iter_clear(3, sieve_limit):
        bitmap[p*p : limit : p+p] = -1

    return bitmap.iter_clear(2, limit)

if __name__ == "__main__":
    start = time.time()
    result = list(sieve(1000000000))
    print(time.time() - start)
    print(len(result))
 34
Author: casevh,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-10-12 06:05:07

Okay, oto (prawie kompletny) kompleksowy benchmarking zrobiłem dziś wieczorem, aby zobaczyć, który kod działa najszybciej. Mam nadzieję, że ktoś uzna tę listę za przydatną. Pominąłem wszystko, co zajmuje więcej niż 30 sekund, aby zakończyć na mojej maszynie.

Chciałbym podziękować wszystkim, którzy wnieśli wkład. Dzięki twoim wysiłkom zdobyłem wiele wglądu i mam nadzieję, że ty też.

Moja maszyna: AMD zm-86, Dwurdzeniowy 2,40 Ghz, z 4GB PAMIĘCI RAM. To jest laptop HP TouchSmart Tx2. Zauważ, że chociaż mogłem połączyć się z pastebinem, porównałem wszystkie poniższe na mojej własnej maszynie.

Dodam benchmark gmpy2, gdy tylko będę w stanie go zbudować.

Wszystkie benchmarki są testowane w Pythonie 2.6 x86

Zwracanie listy liczb pierwszych n do 1 000 000: (Using Python Generatory)

Sebastian ' s numpy generator version (updated) - 121 ms @

Sito Marka + koło - 154 ms

Wersja Roberta z krojeniem - 159 ms

Moja ulepszona wersja z krojeniem]} - 205 ms

Generator Numpy z wyliczaniem - 249 ms @

Sito podstawowe Marka - 317 ms

Poprawa Casevh na moim oryginale rozwiązanie - 343 ms

Moje zmodyfikowane rozwiązanie generatora numpy - 407 ms

Moja oryginalna metoda w pytanie - 409 ms

Rozwiązanie Bitarray - 414 ms @

Czysty Python z bytearray - 1394 ms @

Scott ' s BitString solution - 6659 ms @

'@' oznacza, że metoda ta jest zdolna do generowania do n

Ponadto, jeśli nie potrzebujesz generator i po prostu chcę całą listę od razu:

Rozwiązanie Numpy z RosettaCode - 32 ms @

(The rozwiązanie numpy jest również w stanie wygenerować do 1 miliarda, co zajęło 61,6259 sekund. Podejrzewam, że pamięć została zamieniona raz, stąd ten podwójny czas.)

 8
Author: Xavier Ho,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 12:02:48

OK, to jest moja druga odpowiedź, ale ponieważ szybkość jest najważniejsza, pomyślałem, że muszę wspomnieć o module bitarray - mimo że jest to nemezis bitstring:). Idealnie nadaje się do tego przypadku, ponieważ nie tylko jest to rozszerzenie C (a więc szybsze niż czysty Python ma nadzieję być), ale także obsługuje przypisania slice. Jednak nie jest jeszcze Dostępny dla Pythona 3.

Nawet nie próbowałem tego zoptymalizować, po prostu przepisałem wersję bitstring. Na mojej maszynie dostaję 0.16 sekundy dla liczb pierwszych poniżej miliona.

Za miliard, działa idealnie i kończy się w 2 minuty i 31 sekund.
import bitarray

def prime_bitarray(limit=1000000):
    yield 2
    flags = bitarray.bitarray(limit)
    flags.setall(False)
    sub_limit = int(limit**0.5)
    for i in xrange(3, limit, 2):
        if not flags[i]:
            yield i
            if i <= sub_limit:
                flags[3*i:limit:i*2] = True
 8
Author: Scott Griffiths,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-02-07 15:12:11

Powiązane pytanie.
najszybszy sposób na wyświetlenie wszystkich liczb pierwszych poniżej N w Pythonie

Cześć, ja też szukam kodu w Pythonie, aby wygenerować liczby pierwsze do 10 9 tak szybko, jak Mogę, co jest trudne ze względu na problem z pamięcią. do tej pory wymyśliłem to, aby wygenerować primes do 106 & 10*7 (0,171 s & 1764 s W moim starym komputerze), który wydaje się być nieco szybszy(przynajmniej w moim komputerze) niż "moja ulepszona wersja z krojeniem" z poprzedniego posta, prawdopodobnie ponieważ używam n / / i-i +1 (patrz niżej)zamiast analogicznego len(flags[I2:: i

def primes(n):
    """ Returns  a list of primes < n """
    sieve = [True] * n
    for i in xrange(3,int(n**0.5)+1,2):
        if sieve[i]:
            sieve[i*i::2*i]=[False]*((n-i*i-1)/(2*i)+1)
    return [2] + [i for i in xrange(3,n,2) if sieve[i]]

W Jednym ze swoich kodów Xavier używa FLAG[i2:: ii..n mamy(n-ii)//2*i wielokrotności 2*i ponieważ chcemy policzyć ii również sumujemy 1 tak len (sito[ii::2*i]) equal (n-I * i) / / (2*i) + 1 to make kod szybciej. Podstawowym generatorem opartym na powyższym kodzie byłoby:

def primesgen(n):
    """ Generates all primes <= n """
    sieve = [True] * n
    yield 2
    for i in xrange(3,int(n**0.5)+1,2):
        if sieve[i]:
            yield i
            sieve[i*i::2*i] = [False]*((n-i*i-1)/(2*i)+1)
    for i in xrange(i+2,n,2):
        if sieve[i]: yield i

Z odrobiną modyfikacji można napisać nieco wolniejszą wersję powyższego kodu, która zaczyna się od sieve o połowę wielkości sieve = [True] * (n / / 2) i działa dla tej samej N. Nie wiem, jak to odzwierciedlają w problemie z pamięcią. Jako przykład realizacji tutaj jest zmodyfikowana wersja kodu numpy rosetta (może szybsza) zaczynająca się od Sita o połowę większego.

import numpy
def primesfrom3to(n):
    """ Returns a array of primes, 3 <= p < n """
    sieve = numpy.ones(n/2, dtype=numpy.bool)
    for i in xrange(3,int(n**0.5)+1,2):
        if sieve[i/2]: sieve[i*i/2::i] = False
    return 2*numpy.nonzero(sieve)[0][1::]+1

A Faster & more generator pamięci byłby:

import numpy
def primesgen1(n):
""" Input n>=6, Generates all primes < n """
sieve1 = numpy.ones(n/6+1, dtype=numpy.bool)
sieve5 = numpy.ones(n/6  , dtype=numpy.bool)
sieve1[0] = False
yield 2; yield 3;
for i in xrange(int(n**0.5)/6+1):
    if sieve1[i]:
        k=6*i+1; yield k;
        sieve1[ ((k*k)/6) ::k] = False
        sieve5[(k*k+4*k)/6::k] = False
    if sieve5[i]:
        k=6*i+5; yield k;
        sieve1[ ((k*k)/6) ::k] = False
        sieve5[(k*k+2*k)/6::k] = False
for i in xrange(i+1,n/6):
        if sieve1[i]: yield 6*i+1
        if sieve5[i]: yield 6*i+5
if n%6 > 1:
    if sieve1[i+1]: yield 6*(i+1)+1

Lub z nieco większym kodem:

import numpy
def primesgen(n):
    """ Input n>=30, Generates all primes < n """
    size = n/30 + 1
    sieve01 = numpy.ones(size, dtype=numpy.bool)
    sieve07 = numpy.ones(size, dtype=numpy.bool)
    sieve11 = numpy.ones(size, dtype=numpy.bool)
    sieve13 = numpy.ones(size, dtype=numpy.bool)
    sieve17 = numpy.ones(size, dtype=numpy.bool)
    sieve19 = numpy.ones(size, dtype=numpy.bool)
    sieve23 = numpy.ones(size, dtype=numpy.bool)
    sieve29 = numpy.ones(size, dtype=numpy.bool)
    sieve01[0] = False
    yield 2; yield 3; yield 5;
    for i in xrange(int(n**0.5)/30+1):
        if sieve01[i]:
            k=30*i+1; yield k;
            sieve01[     (k*k)/30::k] = False
            sieve07[(k*k+ 6*k)/30::k] = False
            sieve11[(k*k+10*k)/30::k] = False
            sieve13[(k*k+12*k)/30::k] = False
            sieve17[(k*k+16*k)/30::k] = False
            sieve19[(k*k+18*k)/30::k] = False
            sieve23[(k*k+22*k)/30::k] = False
            sieve29[(k*k+28*k)/30::k] = False
        if sieve07[i]:
            k=30*i+7; yield k;
            sieve01[(k*k+ 6*k)/30::k] = False
            sieve07[(k*k+24*k)/30::k] = False
            sieve11[(k*k+16*k)/30::k] = False
            sieve13[(k*k+12*k)/30::k] = False
            sieve17[(k*k+ 4*k)/30::k] = False
            sieve19[     (k*k)/30::k] = False
            sieve23[(k*k+22*k)/30::k] = False
            sieve29[(k*k+10*k)/30::k] = False
        if sieve11[i]:
            k=30*i+11; yield k;
            sieve01[     (k*k)/30::k] = False
            sieve07[(k*k+ 6*k)/30::k] = False
            sieve11[(k*k+20*k)/30::k] = False
            sieve13[(k*k+12*k)/30::k] = False
            sieve17[(k*k+26*k)/30::k] = False
            sieve19[(k*k+18*k)/30::k] = False
            sieve23[(k*k+ 2*k)/30::k] = False
            sieve29[(k*k+ 8*k)/30::k] = False
        if sieve13[i]:
            k=30*i+13; yield k;
            sieve01[(k*k+24*k)/30::k] = False
            sieve07[(k*k+ 6*k)/30::k] = False
            sieve11[(k*k+ 4*k)/30::k] = False
            sieve13[(k*k+18*k)/30::k] = False
            sieve17[(k*k+16*k)/30::k] = False
            sieve19[     (k*k)/30::k] = False
            sieve23[(k*k+28*k)/30::k] = False
            sieve29[(k*k+10*k)/30::k] = False
        if sieve17[i]:
            k=30*i+17; yield k;
            sieve01[(k*k+ 6*k)/30::k] = False
            sieve07[(k*k+24*k)/30::k] = False
            sieve11[(k*k+26*k)/30::k] = False
            sieve13[(k*k+12*k)/30::k] = False
            sieve17[(k*k+14*k)/30::k] = False
            sieve19[     (k*k)/30::k] = False
            sieve23[(k*k+ 2*k)/30::k] = False
            sieve29[(k*k+20*k)/30::k] = False
        if sieve19[i]:
            k=30*i+19; yield k;
            sieve01[     (k*k)/30::k] = False
            sieve07[(k*k+24*k)/30::k] = False
            sieve11[(k*k+10*k)/30::k] = False
            sieve13[(k*k+18*k)/30::k] = False
            sieve17[(k*k+ 4*k)/30::k] = False
            sieve19[(k*k+12*k)/30::k] = False
            sieve23[(k*k+28*k)/30::k] = False
            sieve29[(k*k+22*k)/30::k] = False
        if sieve23[i]:
            k=30*i+23; yield k;
            sieve01[(k*k+24*k)/30::k] = False
            sieve07[(k*k+ 6*k)/30::k] = False
            sieve11[(k*k+14*k)/30::k] = False
            sieve13[(k*k+18*k)/30::k] = False
            sieve17[(k*k+26*k)/30::k] = False
            sieve19[     (k*k)/30::k] = False
            sieve23[(k*k+ 8*k)/30::k] = False
            sieve29[(k*k+20*k)/30::k] = False
        if sieve29[i]:
            k=30*i+29; yield k;
            sieve01[     (k*k)/30::k] = False
            sieve07[(k*k+24*k)/30::k] = False
            sieve11[(k*k+20*k)/30::k] = False
            sieve13[(k*k+18*k)/30::k] = False
            sieve17[(k*k+14*k)/30::k] = False
            sieve19[(k*k+12*k)/30::k] = False
            sieve23[(k*k+ 8*k)/30::k] = False
            sieve29[(k*k+ 2*k)/30::k] = False
    for i in xrange(i+1,n/30):
            if sieve01[i]: yield 30*i+1
            if sieve07[i]: yield 30*i+7
            if sieve11[i]: yield 30*i+11
            if sieve13[i]: yield 30*i+13
            if sieve17[i]: yield 30*i+17
            if sieve19[i]: yield 30*i+19
            if sieve23[i]: yield 30*i+23
            if sieve29[i]: yield 30*i+29
    i += 1
    mod30 = n%30
    if mod30 > 1:
        if sieve01[i]: yield 30*i+1
    if mod30 > 7:
        if sieve07[i]: yield 30*i+7
    if mod30 > 11:
        if sieve11[i]: yield 30*i+11
    if mod30 > 13:
        if sieve13[i]: yield 30*i+13
    if mod30 > 17:
        if sieve17[i]: yield 30*i+17
    if mod30 > 19:
        if sieve19[i]: yield 30*i+19
    if mod30 > 23:
        if sieve23[i]: yield 30*i+23

Ps: Jeśli masz jakieś sugestie, jak przyspieszyć ten kod, cokolwiek od zmiany kolejności operacji do wstępnego przetwarzania czegokolwiek, proszę o komentarz.

 5
Author: Robert William Hanks,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 10:30:56

Oto wersja, którą napisałem jakiś czas temu; może być interesujące porównanie z Twoją dla szybkości. Nie ma to jednak nic wspólnego z problemami z przestrzenią (w rzeczywistości są one prawdopodobnie gorsze niż w Twojej wersji).

from math import sqrt

def basicSieve(n):
    """Given a positive integer n, generate the primes < n."""
    s = [1]*n
    for p in xrange(2, 1+int(sqrt(n-1))):
        if s[p]:
            a = p*p
            s[a::p] = [0] * -((a-n)//p)
    for p in xrange(2, n):
        if s[p]:
            yield p 
Mam szybsze wersje, używające koła, ale są znacznie bardziej skomplikowane. Oryginalne źródło to tutaj . / Align = "center" bgcolor = "# e0ffe0 " / cesarz chin / / align = center / wheelSieve jest głównym punktem wejścia.
from math import sqrt
from bisect import bisect_left

def basicSieve(n):
    """Given a positive integer n, generate the primes < n."""
    s = [1]*n
    for p in xrange(2, 1+int(sqrt(n-1))):
        if s[p]:
            a = p*p
            s[a::p] = [0] * -((a-n)//p)
    for p in xrange(2, n):
        if s[p]:
            yield p

class Wheel(object):
    """Class representing a wheel.

    Attributes:
       primelimit -> wheel covers primes < primelimit.
       For example, given a primelimit of 6
       the wheel primes are 2, 3, and 5.
       primes -> list of primes less than primelimit
       size -> product of the primes in primes;  the modulus of the wheel
       units -> list of units modulo size
       phi -> number of units

    """
    def __init__(self, primelimit):
        self.primelimit = primelimit
        self.primes = list(basicSieve(primelimit))

        # compute the size of the wheel
        size = 1
        for p in self.primes:
            size *= p
        self.size = size

        # find the units by sieving
        units = [1] * self.size
        for p in self.primes:
            units[::p] = [0]*(self.size//p)
        self.units = [i for i in xrange(self.size) if units[i]]

        # number of units
        self.phi = len(self.units)

    def to_index(self, n):
        """Compute alpha(n), where alpha is an order preserving map
        from the set of units modulo self.size to the nonnegative integers.

        If n is not a unit, the index of the first unit greater than n
        is given."""
        return bisect_left(self.units, n % self.size) + n // self.size * self.phi

    def from_index(self, i):
        """Inverse of to_index."""

        return self.units[i % self.phi] + i // self.phi * self.size

def wheelSieveInner(n, wheel):
    """Given a positive integer n and a wheel, find the wheel indices of
    all primes that are less than n, and that are units modulo the
    wheel modulus.
    """

    # renaming to avoid the overhead of attribute lookups
    U = wheel.units
    wS = wheel.size
    # inverse of unit map
    UI = dict((u, i) for i, u in enumerate(U))
    nU = len(wheel.units)

    sqroot = 1+int(sqrt(n-1)) # ceiling of square root of n

    # corresponding index (index of next unit up)
    sqrti = bisect_left(U, sqroot % wS) + sqroot//wS*nU
    upper = bisect_left(U, n % wS) + n//wS*nU
    ind2 = bisect_left(U, 2 % wS) + 2//wS*nU

    s = [1]*upper
    for i in xrange(ind2, sqrti):
        if s[i]:
            q = i//nU
            u = U[i%nU]
            p = q*wS+u
            u2 = u*u
            aq, au = (p+u)*q+u2//wS, u2%wS
            wp = p * nU
            for v in U:
                # eliminate entries corresponding to integers
                # congruent to p*v modulo p*wS
                uvr = u*v%wS
                m = aq + (au > uvr)
                bot = (m + (q*v + u*v//wS - m) % p) * nU + UI[uvr]
                s[bot::wp] = [0]*-((bot-upper)//wp)
    return s

def wheelSieve(n, wheel=Wheel(10)):
    """Given a positive integer n, generate the list of primes <= n."""
    n += 1
    wS = wheel.size
    U = wheel.units
    nU = len(wheel.units)
    s = wheelSieveInner(n, wheel)
    return (wheel.primes[:bisect_left(wheel.primes, n)] +
            [p//nU*wS + U[p%nU] for p in xrange(bisect_left(U, 2 % wS)
             + 2//wS*nU, len(s)) if s[p]])
Co do tego, czym jest koło: cóż, wiesz, że (oprócz 2), wszystkie liczby pierwsze są nieparzyste, więc większość sita pomija wszystkie liczby parzyste. Podobnie, można pójść nieco dalej i zauważyć, że wszystkie liczby pierwsze (z wyjątkiem 2 i 3) są przystające do 1 lub 5 modulo 6 (==2 * 3), więc można uciec tylko z przechowywania wpisów dla tych liczb w situ. Następnym krokiem jest zwrócenie uwagi, że wszystkie liczby pierwsze (z wyjątkiem 2, 3 i 5) są zgodne z jednym z 1, 7, 11, 13, 17, 19, 23, 29 (modulo 30) (tutaj 30 == 2*3*5), i tak dalej.
 4
Author: Mark Dickinson,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-05-24 15:25:18

Jednym z ulepszeń prędkości, które możesz wprowadzić za pomocą bitstring, jest użycie metody 'set', gdy wykluczysz wielokrotności bieżącej liczby.

Więc sekcja vital staje się

for i in range(3, limit, 2):
    if flags[i]:
        yield i
        if i <= sub_limit:
            flags.set(1, range(i*3, limit, i*2))    
Na mojej maszynie działa to około 3 razy szybciej niż oryginał. Inną moją myślą było użycie bitstringu do reprezentowania tylko nieparzystych liczb. Następnie można znaleźć unset bitów za pomocą flags.findall([0]), który zwraca generator. Nie jestem pewien, czy byłoby to znacznie szybsze (znalezienie wzorców bitowych nie jest strasznie łatwe zrobić sprawnie).

[pełne ujawnienie: napisałem moduł bitstring, więc stawką jest trochę dumy!]


Jako porównanie wyjąłem też flaki z metody bitstring tak, że robi to w ten sam sposób, ale bez sprawdzania zakresu itp. Myślę, że daje to rozsądny niższy limit dla czystego Pythona, który działa na miliard elementów (bez zmiany algorytmu, co moim zdaniem jest oszustwem!)

def prime_pure(limit=1000000):
    yield 2
    flags = bytearray((limit + 7) // 8)
    sub_limit = int(limit**0.5)
    for i in xrange(3, limit, 2):
        byte, bit = divmod(i, 8)
        if not flags[byte] & (128 >> bit):
            yield i
            if i <= sub_limit:
                for j in xrange(i*3, limit, i*2):
                    byte, bit = divmod(j, 8)
                    flags[byte] |= (128 >> bit)

Na mojej maszynie to działa w Około 0.62 sekundy dla milion elementów, co oznacza, że to około jedna czwarta prędkości odpowiedzi bitarray. Myślę, że to całkiem rozsądne dla czystego Pythona.

 3
Author: Scott Griffiths,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-07-25 21:56:29

Typy liczb całkowitych Pythona mogą mieć dowolny rozmiar, więc nie powinieneś potrzebować sprytnej biblioteki bitstring do reprezentowania zestawu bitów, tylko pojedynczej liczby.

Oto kod. Z łatwością obsługuje limit 1,000,000, a nawet może obsłużyć 10,000,000 bez narzekania zbytnio: {]}
def multiples_of(n, step, limit):
    bits = 1 << n
    old_bits = bits
    max = 1 << limit
    while old_bits < max:
        old_bits = bits
        bits += bits << step
        step *= 2
    return old_bits

def prime_numbers(limit=10000000):
    '''Prime number generator. Yields the series                                                                        
    2, 3, 5, 7, 11, 13, 17, 19, 23, 29 ...                                                                              
    using Sieve of Eratosthenes.                                                                                        
    '''
    yield 2
    sub_limit = int(limit**0.5)
    flags = ((1 << (limit - 2)) - 1) << 2
    # Step through all the odd numbers                                                                                  
    for i in xrange(3, limit, 2):
        if not (flags & (1 << i)):
            continue
        yield i
        # Exclude further multiples of the current prime number                                                         
        if i <= sub_limit:
            flags &= ~multiples_of(i * 3, i << 1, limit)

Funkcja multiples_of pozwala uniknąć kosztów ustawienia każdego pojedynczego wielokrotności z osobna. Ustawia początkowy bit, przesuwa go o początkowy Krok (i << 1) i dodaje wynik do siebie. Następnie podwaja krok, tak, że następny shift kopiuje oba bity, i tak dalej, aż osiągnie limit. Ustawia wszystkie wielokrotności liczby do limitu w czasie o (log (limit)).

 2
Author: Marcelo Cantos,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-05-24 14:21:56

Jedną z opcji, na którą warto spojrzeć, jest właśnie kompilacja modułu C / C++, dzięki czemu masz bezpośredni dostęp do funkcji bit-twidling. AFAIK mógłbyś napisać coś takiego i zwrócić wyniki dopiero po zakończeniu obliczeń wykonanych w C / C++. Teraz, gdy to piszę, możesz również spojrzeć na numpy, która przechowuje tablice jako natywne C dla szybkości. Nie wiem, czy będzie to szybsze niż Moduł bitstring!

 2
Author: Wayne Werner,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-05-24 14:29:45

Kolejna naprawdę głupia opcja, ale to może być pomocne, jeśli naprawdę potrzebujesz dużej listy liczb pierwszych dostępnych bardzo szybko. Powiedzmy, że jeśli potrzebujesz ich jako narzędzia do odpowiedzi na problemy projektu Eulera(jeśli problem polega tylko na optymalizacji kodu jako gry umysłowej, to nie ma znaczenia).

Użyj dowolnego wolnego rozwiązania do generowania listy i zapisz ją do pliku źródłowego Pythona, mówi primes.py, który zawiera tylko:

primes = [ a list of a million primes numbers here ]

Teraz, aby ich użyć, musisz zrobić import primes i uzyskać je z minimalną pamięcią footprint z prędkością dysku IO. Złożoność to liczba liczb pierwszych: -)

Nawet jeśli użyłeś źle zoptymalizowanego rozwiązania do wygenerowania tej listy, zostanie to wykonane tylko raz i nie ma to większego znaczenia.

Prawdopodobnie możesz zrobić to jeszcze szybciej używając ogórków / rozporek, ale masz pomysł...

 1
Author: kriss,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-05-27 00:36:23

Przydałoby się segmentowe Sito Eratostenesa. Pamięć używana dla każdego segmentu jest ponownie używana dla następnego segmentu.

 0
Author: user448810,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-02-02 13:58:07

Oto kod Python3, który zużywa mniej pamięci niż opublikowane do tej pory rozwiązania bitarray/bitstring i około 1/8 pamięci Primesgen () Roberta Williama Hanksa, podczas gdy działa szybciej niż primesgen () (nieznacznie szybciej przy 1 000 000, używając 37kb pamięci, 3x szybciej niż primesgen () przy 1 000 000 000 używając poniżej 34MB). Zwiększenie rozmiaru fragmentu (zmienny fragment w kodzie) zużywa więcej pamięci, ale przyspiesza program, do limitu -- wybrałem wartość tak, aby jego wkład do pamięci jest poniżej około 10% N//30 bajtów sita. Nie jest tak wydajny jak nieskończony generator Willa Nessa (https://stackoverflow.com/a/19391111/5439078 ), ponieważ zapisuje (i zwraca na końcu, w postaci skompresowanej) wszystkie obliczone liczby pierwsze.

Powinno to działać poprawnie, o ile obliczenia pierwiastka kwadratowego są dokładne (około 2 * * 51, jeśli Python używa 64-bitowych podwojeń). Jednak nie należy używać tego programu, aby znaleźć primes tak duże!

(nie przeliczyłem / align = "center" bgcolor = "# e0ffe0 " / król Anglii / / align = center / Dzięki Robert!)

import numpy as np
def prime_30_gen(n):
    """ Input n, int-like.  Yields all primes < n as Python ints"""
    wheel = np.array([2,3,5])
    yield from wheel[wheel < n].tolist()
    powers = 1 << np.arange(8, dtype='u1')
    odds = np.array([1, 7, 11, 13, 17, 19, 23, 29], dtype='i8')
    offsets=np.array([[0,6,10,12,16,18,22,28],[6,24,16,12,4,0,22,10],
                      [0,6,20,12,26,18,2,8],  [24,6,4,18,16,0,28,10],
                      [6,24,26,12,14,0,2,20], [0,24,10,18,4,12,28,22],
                      [24,6,14,18,26,0,8,20], [0,24,20,18,14,12,8,2]],
                     dtype='i8')
    offsets = {d:f for d,f in zip(odds, offsets)}
    sieve = 255 * np.ones((n + 28) // 30, dtype='u1')
    if n % 30 > 1: sieve[-1] >>= 8 - odds.searchsorted(n % 30)
    sieve[0] &= ~1 
    for i in range((int((n - 1)**0.5) + 29) // 30):
        for odd in odds[(sieve[i] & powers).nonzero()[0]]:
            k = i * 30 + odd
            yield int(k)
            for clear_bit, off in zip(~powers, offsets[odd]): 
                sieve[(k * (k + off)) // 30 :: k] &= clear_bit
    chunk = min(1 + (n >> 13), 1<<15)
    for i in range(i + 1, len(sieve), chunk):
        a = (sieve[i : i + chunk, np.newaxis] & powers)
        a = np.flatnonzero(a.astype('bool')) + i * 8
        a = (a // 8 * 30 + odds[a & 7]).tolist()
        yield from a
    return sieve

Uwaga na marginesie: jeśli chcesz mieć rzeczywistą prędkość i mieć 2GB PAMIĘCI RAM wymagane dla listy primes do 10 * * 9, powinieneś użyć pyprimesieve (on https://pypi.python.org / , używając primesieve http://primesieve.org/).

 0
Author: Jason,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 12:26:38