To najlepszy algorytm, jaki mogłem wymyślić.
def get_primes(n):
numbers = set(range(n, 1, -1))
primes = []
while numbers:
p = numbers.pop()
primes.append(p)
numbers.difference_update(set(range(p*2, n+1, p)))
return primes
>>> timeit.Timer(stmt='get_primes.get_primes(1000000)', setup='import get_primes').timeit(1)
1.1499958793645562
Czy można to zrobić jeszcze szybciej?
Ten kod ma wadę: ponieważ numbers
jest to zestaw nieuporządkowany, nie ma gwarancji, że numbers.pop()
usunie z niego najniższą liczbę. Niemniej jednak działa (przynajmniej dla mnie) dla niektórych liczb wejściowych:
>>> sum(get_primes(2000000))
142913828922L
#That's the correct sum of all numbers below 2 million
>>> 529 in get_primes(1000)
False
>>> 529 in get_primes(530)
True
python
math
optimization
primes
jbochi
źródło
źródło
import antigravity
. Czy nie ma czegoś takiego jakrequire 'prime'; Prime.take(10)
(Ruby)?Odpowiedzi:
Ostrzeżenie:
timeit
wyniki mogą się różnić ze względu na różnice w sprzęcie lub wersji Pythona.Poniżej znajduje się skrypt, który porównuje wiele implementacji:
Wiele dzięki Stephan do wniesienia sieve_wheel_30 moją uwagę. Uznanie dla Roberta Williama Hanksa za primesfrom2to, primesfrom3to, rwh_primes, rwh_primes1 i rwh_primes2.
Spośród testowanych prostych metod Pythona, psyco , dla n = 1000000, rwh_primes1 był najszybciej testowany.
Spośród przetestowanych prostych metod Pythona, bez psyco , dla n = 1000000, rwh_primes2 był najszybszy.
Ze wszystkich testowanych metod, pozwalających na numpy , dla n = 1000000, primesfrom2to był najszybszym testowanym.
Czasy zmierzono za pomocą polecenia:
z
{method}
zastąpionymi przez każdą z nazw metod.primes.py:
Uruchomienie testów skryptu, aby wszystkie implementacje dały ten sam wynik.
źródło
gmpy
- ma całkiem dobrą obsługę liczb pierwszych, za pomocąnext_prime
metody tegompz
typu.Szybszy i bardziej czysty pod względem pamięci czysty kod Python:
lub zaczynając od półsita
Szybszy i bardziej logiczny kod numpy:
szybsza odmiana zaczynająca się od jednej trzeciej sita:
Wersja (trudna do kodowania) czysto pythonowego powyższego kodu to:
Niestety, czysty python nie przyjmuje prostszego i szybszego numerycznego sposobu wykonywania przypisania, a wywoływanie
len()
wewnątrz pętli, jak w,[False]*len(sieve[((k*k)//3)::2*k])
jest zbyt wolne. Musiałem więc improwizować, aby poprawić dane wejściowe (i uniknąć więcej matematyki) i wykonać ekstremalną (i bolesną) matematykę.Osobiście uważam, że szkoda, że numpy (która jest tak szeroko stosowana) nie jest częścią standardowej biblioteki Pythona, a ulepszenia składni i szybkości wydają się być całkowicie pomijane przez programistów Pythona.
źródło
bitarray
- jak tutaj użyto (dla najprostszego sita głównego; nie ma tu miejsca w wyścigu!) stackoverflow.com/questions/31120986/…primesfrom2to()
metody podział powinien znajdować się w nawiasach?Jest to całkiem zgrabny próbka z Python Cookbook tutaj - najszybsza wersja proponowana na tym adresem jest:
to by dało
Pomiary w wierszu powłoki (jak wolę to zrobić) za pomocą tego kodu w pri.py, obserwuję:
więc wygląda na to, że rozwiązanie Cookbook jest ponad dwukrotnie szybsze.
źródło
Używając sita Sundaram , myślę, że pobiłem rekord czysto Pythona:
Porównanie:
źródło
None
zamiast oryginalnej funkcji działa i jest nawet szybsze niżzero.__sub__
sundaram3(9)
, wróci[2, 3, 5, 7, 9]
? Wydaje się, że robi to z licznymi - być może wszystkimi - liczbami nieparzystymi (nawet jeśli nie są liczbami pierwszymi)Algorytm jest szybki, ale ma poważną wadę:
Zakładasz,
numbers.pop()
że zwróci najmniejszą liczbę w zestawie, ale nie jest to wcale gwarantowane. Zestawy są nieuporządkowane ipop()
usuwają i zwracają dowolny element, więc nie można go użyć do wybrania kolejnej liczby pierwszej z pozostałych liczb.źródło
Dla naprawdę najszybszego rozwiązania z wystarczająco dużą wartością N byłoby pobranie wstępnie obliczonej listy liczb pierwszych , przechowanie jej jako krotki i zrobienie czegoś takiego:
Jeśli
N > primes[-1]
tylko, to oblicz więcej liczb pierwszych i zapisz nową listę w kodzie, więc następnym razem będzie równie szybka.Zawsze myśl nieszablonowo.
źródło
Jeśli nie chcesz wymyślać koła od nowa , możesz zainstalować symboliczną sympię biblioteki matematycznej (tak, jest kompatybilna z Python 3)
I użyj funkcji primerange
źródło
Jeśli akceptujesz narzędzia itertools, ale nie numpy, oto adaptacja rwh_primes2 dla Python 3, która działa około dwa razy szybciej na moim komputerze. Jedyną istotną zmianą jest użycie bytearray zamiast listy dla wartości logicznej i użycie kompresji zamiast interpretacji listy do zbudowania ostatecznej listy. (Dodałbym to jako komentarz jak moarningsun, gdybym mógł.)
Porównania:
i
źródło
Pouczające jest pisanie własnego kodu znalezienia głównej, ale przydatne jest również posiadanie szybkiej niezawodnej biblioteki. Napisałem wrapper wokół biblioteki C ++ primesieve , o nazwie primesieve-python
Spróbuj
pip install primesieve
Byłbym ciekawy, czy prędkość jest porównywana.
źródło
count_primes
funkcja jest znacznie szybsza niżgenerate_primes
Oto dwie zaktualizowane (czysto Python 3.6) wersje jednej z najszybszych funkcji,
źródło
Deterministyczna implementacja testu pierwszeństwa Millera-Rabina przy założeniu, że N <9,080,191
Zgodnie z artykułem na Wikipedii ( http://en.wikipedia.org/wiki/Miller–Rabin_primality_test ) testowanie N <9,080,191 dla a = 2,3,37, a 73 wystarczy, aby zdecydować, czy N jest złożony, czy nie.
I dostosowałem kod źródłowy z probabilistycznej implementacji oryginalnego testu Millera-Rabina znalezionego tutaj: http://en.literateprograms.org/Miller-Rabin_primality_test_(Python)
źródło
Jeśli masz kontrolę nad N, najszybszym sposobem wyliczenia wszystkich liczb pierwszych jest ich wstępne obliczenie. Poważnie. Wstępne obliczenia to sposób na przeoczenie optymalizacji.
źródło
Oto kod, którego zwykle używam do generowania liczb pierwszych w Pythonie:
Nie może konkurować z szybszymi rozwiązaniami zamieszczonymi tutaj, ale przynajmniej jest to czysty python.
Dziękujemy za opublikowanie tego pytania. Naprawdę dużo się dzisiaj nauczyłem.
źródło
W przypadku najszybszego kodu najlepsze jest rozwiązanie numpy. Jednak ze względów czysto akademickich publikuję moją czystą wersję Pythona, która jest nieco mniej niż 50% szybsza niż wersja książki kucharskiej opublikowana powyżej. Ponieważ tworzę całą listę w pamięci, potrzebujesz wystarczająco dużo miejsca, aby pomieścić wszystko, ale wydaje się, że skaluje się dość dobrze.
A wyniki:
źródło
Nieco inna implementacja półsita z użyciem Numpy:
http://rebrained.com/?p=458
Czy ktoś może to porównać z innymi czasami? Na mojej maszynie wydaje się całkiem porównywalna z innym pół-sitem Numpy.
źródło
upto=10**6
:primesfrom2to()
- 7 ms;prime6()
- 12 ms ideone.com/oDg2YWszystko jest napisane i przetestowane. Nie ma więc potrzeby wymyślania nowego koła.
daje nam rekord 12,2 milisekund !
Jeśli to nie jest wystarczająco szybkie, możesz wypróbować PyPy:
Co skutkuje w:
Odpowiedź z 247 głosami up-up zawiera 15,9 ms dla najlepszego rozwiązania. Porównaj to !!!
źródło
Przetestowałem niektóre funkcje unutbu , obliczyłem je z liczbą głodnych milionów
Zwycięzcami są funkcje korzystające z biblioteki numpy,
Uwaga : Ciekawe byłoby również wykonanie testu wykorzystania pamięci :)
Przykładowy kod
Kompletny kod w moim repozytorium github
źródło
Dla Python 3
źródło
Najszybsze sito główne w Pure Python :
Zoptymalizowałem Sito Eratostenesa pod kątem szybkości i pamięci.
Reper
Wynik
źródło
Pierwszy raz korzystam z Pythona, więc niektóre metody, które stosuję w tym przypadku, mogą wydawać się nieco kłopotliwe. Właśnie przekonwertowałem mój kod c ++ do Pythona i to właśnie mam (choć odrobinę wolniej w Pythonie)
źródło
Wiem, że konkurs jest zamknięty na kilka lat. …
Niemniej jednak jest to moja sugestia dotycząca czystego głównego sita pythonowego, opartego na pomijaniu wielokrotności 2, 3 i 5 poprzez zastosowanie odpowiednich kroków podczas przetwarzania sita do przodu. Niemniej jednak jest on wolniejszy dla N <10 ^ 9 niż lepsze rozwiązania Roberta Williama Hanksa rwh_primes2 i rwh_primes1. Dzięki zastosowaniu sita ctypes.c_ushort powyżej 1,5 * 10 ^ 8 w jakiś sposób dostosowuje się do limitów pamięci.
10 ^ 6
$ python -mtimeit -s "import primeSieveSpeedComp" "primeSieveSpeedComp.primeSieveSeq (1000000)" 10 pętli, najlepiej 3: 46,7 ms na pętlę
10 ^ 7
$ python -mtimeit -s "import primeSieveSpeedComp" "primeSieveSpeedComp.primeSieveSeq (10000000)" 10 pętli, najlepiej 3: 530 ms na pętlę
10 ^ 8
$ python -mtimeit -s "import primeSieveSpeedComp" "primeSieveSpeedComp.primeSieveSeq (100000000)" 10 pętli, najlepiej 3: 5,55 s na pętlę
10 ^ 9
$ python -mtimeit -s "import primeSieveSpeedComp" "primeSieveSpeedComp.primeSieveSeq (1000000000)" 10 pętli, najlepiej 3: 61,2 s na pętlę
Możesz skopiować poniższy kod do ubuntus primeSieveSpeedComp, aby przejrzeć te testy.
źródło
Oto numpy wersja Sita Eratostenesa, mająca zarówno dobrą złożoność (mniejszą niż sortowanie tablicy długości n), jak i wektoryzację. W porównaniu do czasów @unutbu jest to tak szybkie, jak pakiety z 46 mikrosekundami, aby znaleźć wszystkie liczby pierwsze poniżej miliona.
Czasy:
źródło
Zaktualizowałem wiele kodu Python 3 i rzuciłem go na perfplot (mój projekt), aby zobaczyć, który z nich jest najszybszy. Okazuje się, że za duża
n
,primesfrom{2,3}to
wziąć ciasto:Kod do odtworzenia fabuły:
źródło
Domyślam się, że najszybszym ze wszystkich sposobów jest kodowanie liczb pierwszych w twoim kodzie.
Dlaczego więc nie napisać powolnego skryptu, który generuje inny plik źródłowy, w którym zapisane są wszystkie liczby, a następnie zaimportować ten plik źródłowy po uruchomieniu rzeczywistego programu.
Oczywiście działa to tylko wtedy, gdy znasz górną granicę N w czasie kompilacji, ale tak jest w przypadku (prawie) wszystkich problemów Eulera projektu.
PS: Mogę się mylić, choć iff parsowanie źródła za pomocą stałych liczb stałych jest wolniejsze niż obliczanie ich w pierwszej kolejności, ale o ile wiem, Python działa ze skompilowanych
.pyc
plików, więc czytanie tablicy binarnej ze wszystkimi liczbami pierwszymi do N powinno być krwawe w takim razie szybko.źródło
Przepraszam, że przeszkadzam, ale erat2 () ma poważną wadę w algorytmie.
Szukając następnego kompozytu, musimy przetestować tylko liczby nieparzyste. q, p oba są nieparzyste; wtedy q + p jest parzyste i nie musi być testowane, ale q + 2 * p jest zawsze nieparzyste. Eliminuje to test „jeśli nawet” w pętli while i pozwala zaoszczędzić około 30% czasu wykonywania.
Skoro już jesteśmy przy tym: zamiast eleganckiej metody „D.pop (q, None)” pobierz i usuń użyj „jeśli q w D: p = D [q], del D [q]”, która jest dwa razy szybsza ! Przynajmniej na mojej maszynie (P3-1 Ghz). Sugeruję więc implementację tego sprytnego algorytmu:
źródło
Najszybsza metoda, jaką do tej pory wypróbowałem, opiera się na funkcji książki kucharskiej Python
erat2
:Zobacz tę odpowiedź, aby uzyskać wyjaśnienie przyspieszenia.
źródło
Mogę się spóźnić na imprezę, ale będę musiał do tego dodać własny kod. Wykorzystuje w przybliżeniu n / 2 w przestrzeni, ponieważ nie musimy przechowywać liczb parzystych, a także korzystam z modułu python bitarray, dodatkowo drastycznie zmniejszając zużycie pamięci i umożliwiając obliczenie wszystkich liczb pierwszych do 1 000 000 000
Zostało to uruchomione na 64-bitowym 2.4GHZ MAC OSX 10.8.3
źródło
Z czasem zebrałem kilka sit liczb pierwszych. Najszybszy na moim komputerze jest to:
źródło
Powoli odpowiadam na to pytanie, ale wydawało mi się, że to zabawne ćwiczenie. Używam numpy, który może oszukiwać i wątpię, aby ta metoda była najszybsza, ale powinna być jasna. Przesiewa tablicę boolowską odnoszącą się tylko do jej wskaźników i wywołuje liczby pierwsze ze wskaźników wszystkich wartości True. Nie wymaga modulo.
źródło
ajs_primes3a(10)
->array([2, 3, 5, 7, 9])
.9
nie jest liczbą pierwsząnumpy
rozwiązań bazujących na zwracaniu tablicy. Uwaga: żadna prawdziwa implementacja Sieve of Eratosthenes nie używa modulo - nie trzeba o tym wspominać. Możesz użyćmat[idx*idx::idx]
zamiastmat[idx*2::idx]
. Inp.nonzero(mat)[0]
zamiastnp.where(mat == True)[0]
.Oto interesująca technika generowania liczb pierwszych (jeszcze nie najskuteczniejszych) przy użyciu listowej interpretacji Pythona:
Przykład i kilka wyjaśnień znajdziesz tutaj
źródło