Zgodnie z piękną tradycją pytań, takich jak Znajdź największą liczbę pierwszą, której długość, suma i iloczyn jest liczbą pierwszą , jest to wariant największego największego wyzwania.
Wkład
Twój kod nie powinien przyjmować żadnych danych wejściowych.
Definicja
Mówimy głównym p
jest good
jeśli p-1
ma dokładnie 2
odmienne czynniki pierwszych.
Wydajność
Kod powinien wyjściowa bezwzględna różnica między kolejnymi liczbami pierwszymi dobrymi q
i p
tak, że |q-p|
jest tak duży, jak to możliwe i q
jest najmniejszym dobry prime większa niż p
. Możesz wydać dowolną liczbę dobrych par, a twój ostatni wynik zostanie wykorzystany jako wynik.
Przykład
Sekwencja pierwszych 55 dobrych liczb pierwszych to https://oeis.org/A067466 .
Wynik
Twój wynik jest po prostu |q-p|
za parę dobrych liczb pierwszych, które wyprowadzasz.
Języki i biblioteki
Możesz użyć dowolnego języka lub biblioteki (która nie została zaprojektowana do tego wyzwania), z wyjątkiem funkcji bibliotecznych do testowania pierwotności lub faktorowania liczb całkowitych. Jednak do celów punktacji uruchomię Twój kod na moim komputerze, więc podaj jasne instrukcje, jak uruchomić go na Ubuntu.
Moja maszyna Czasy zostaną uruchomione na moim komputerze. Jest to standardowa instalacja Ubuntu na 8-rdzeniowym procesorze AMD FX-8350 8 GB. Oznacza to również, że muszę być w stanie uruchomić Twój kod.
Detale
- Zabiję twój kod po 2 minutach, chyba że wcześniej zabraknie mu pamięci. Dlatego powinien upewnić się, że coś wyprowadził przed odcięciem.
- Nie możesz używać żadnego zewnętrznego źródła liczb pierwszych.
- Możesz użyć probabilistycznych metod testowania liczb pierwszych, chociaż Mego powiedział mi, że przy dobrych tabelach Miller-Rabin może testować do 341,550,071,728,321 (lub nawet wyżej) deterministycznie. Zobacz także http://miller-rabin.appspot.com/ .
Najlepsze wpisy, które sprawdzają wszystkie liczby całkowite od 1
- 756 przez cat in Go
- 756 autorstwa El'endii Starman w Pythonie
- 1932 Adnan w C # (używając mono 3.2.8)
- 2640 przez yeti w Pythonie (przy użyciu pypy 4.01)
- 2754 autorstwa Reto Koradi w C ++
- 3486 autorstwa Petera Taylora w Javie
- 3900 przez primo w RPython (przy użyciu pypy 4.01)
- 4176 autor: The Coder in Java
Najlepsze wpisy, które mogą pominąć dużą liczbę liczb całkowitych, aby znaleźć dużą lukę
- 14226 autorstwa Reto Koradi w C ++
- 22596 przez primo w RPython (używając pypy 4.01). Rekord osiągnięty po 5 sekundach!
źródło
Odpowiedzi:
RPython (PyPy 4.0.1), 4032
RPython jest ograniczonym podzbiorem Pythona, który można przetłumaczyć na C, a następnie skompilować za pomocą RPython Toolchain. Jego wyraźnym celem jest pomoc w tworzeniu tłumaczy językowych, ale można go również używać do kompilacji prostych programów.
Aby skompilować, pobierz bieżące źródło PyPy (PyPy 4.0.1) i uruchom następujące czynności:
Wynikowy plik wykonywalny zostanie nazwany
good-primes-c
lub podobny w bieżącym katalogu roboczym.Uwagi dotyczące wdrażania
Generator liczb pierwszych
primes
jest nieograniczonym sito Eratostenesa, które używa koła, aby uniknąć wielokrotności 2 , 3 , 5 lub 7 . Wywołuje się także rekurencyjnie, aby wygenerować następną wartość do użycia przy oznaczaniu. Jestem bardzo zadowolony z tego generatora. Profilowanie linii ujawnia, że dwie najwolniejsze linie to:więc nie sądzę, że jest wiele miejsca na ulepszenia, inne niż być może przy użyciu większego koła.
W celu sprawdzenia „dobroci”, najpierw wszystkie czynniki dwóch są usuwane z n-1 , używając nieco kręcącego się hacka, aby znaleźć największą potęgę dwóch, która jest dzielnikiem
(n-1 & 1-n)
. Ponieważ p-1 jest koniecznie nawet dla dowolnej liczby pierwszej p> 2 , wynika z tego, że 2 musi być jednym z odrębnych czynników pierwszych. To, co pozostaje, jest wysyłane dois_prime_power
funkcji, która robi to, co sugeruje jej nazwa. Sprawdzanie, czy wartość jest siłą pierwszą, jest „prawie wolne”, ponieważ odbywa się to jednocześnie z kontrolą pierwotności, przy co najwyżej O (log p n) operacjach, gdzie p jest najmniejszym pierwszym czynnikiem n. Podział próbny może wydawać się nieco naiwny, ale według moich testów jest to najszybsza metoda dla wartości mniejszych niż 2 32 . Oszczędzam trochę, ponownie wykorzystując koło z sita. W szczególności:poprzez iterację na kole o długości 48,
p*p < n
czek zostanie pominięty tysiące razy, przy niskiej, niskiej cenie nie większej niż 48 dodatkowych operacji modulo. Pomija również ponad 77% wszystkich kandydatów, a nie 50%, biorąc tylko szanse.Ostatnie kilka wyników to:
Kod jest również prawidłowym Pythonem i powinien osiągnąć 3588 ~ 3900, gdy jest uruchamiany z najnowszym interpreterem PyPy.
RPython (PyPy 4.0.1), 22596
To przesłanie różni się nieco od innych opublikowanych do tej pory, ponieważ nie sprawdza wszystkich dobrych liczb pierwszych, ale wykonuje stosunkowo duże skoki. Wadą tego jest to, że nie można użyć sit [Stoję skorygowany?] , Więc trzeba polegać całkowicie na testach pierwotności, które w praktyce są nieco wolniejsze. Istnieje również szczęśliwy środek między stopą wzrostu a liczbą wartości sprawdzanych za każdym razem. Mniejsze wartości są znacznie szybsze do sprawdzenia, ale większe wartości mają większe luki.
Aby uspokoić bogów matematyki, postanowiłem zastosować sekwencję podobną do Fibonacciego, mając następny punkt początkowy jako sumę dwóch poprzednich. Jeśli po sprawdzeniu 10 par nie zostaną znalezione żadne nowe rekordy, skrypt przejdzie do następnego.
Ostatnie kilka wyników to:
Podczas kompilacji używane są 64-bitowe liczby całkowite, chociaż w kilku miejscach zakłada się, że można dodać dwie liczby całkowite bez przepełnienia, więc w praktyce można użyć tylko 63 liczb całkowitych. Po osiągnięciu 62 bitów znaczących wartość prądu jest dwukrotnie zmniejszana o połowę, aby uniknąć przepełnienia obliczeń. Rezultatem jest to, że tasuje skryptów poprzez wartości na 2 60 - 2 62 przedziale. Nieprzekraczanie natywnej precyzji liczb całkowitych powoduje również, że skrypt jest interpretowany szybciej.
Do potwierdzenia tego wyniku można użyć następującego skryptu PARI / GP:
źródło
Prawdopodobnie 4032, mieszane sito Atkin-Bernstein i „deterministyczny” Miller-Rabin
Rozkład na koła i dobre liczby pierwsze
Jest bardzo oczywiste, że z wyjątkiem 2, 3 i 5, każda liczba pierwsza jest chroniona prawem autorskim do 2 * 3 * 5 = 60. Istnieje 16 klas równoważności modulo 60, które są coprime do 60, więc każdy test pierwotności musi tylko sprawdzić te 16 przypadków.
Kiedy jednak szukamy „dobrych” liczb pierwszych, możemy jeszcze bardziej rozrzedzić stado. Jeśli
gcd(x, 60) = 1
okaże się, że w większości przypadkówgcd(x-1, 60)
jest to 6 lub 10. Istnieje 6 wartości,x
dla których jest to 2:Dlatego możemy wstępnie obliczyć „dobre” liczby pierwsze formy
2^a 3^b + 1
i2^a 5^b + 1
scalić je w wynik generatora liczb pierwszych, który uznaje tylko 10% liczb za nawet potencjalne liczby pierwsze.Uwagi dotyczące wdrożenia
Ponieważ miałem już implementację Java sita Atkin-Bernstein, która używa już koła jako kluczowego elementu, naturalnie wydawało się, że usuwam niepotrzebne szprychy i dostosowuję kod. Początkowo próbowałem wykorzystać architekturę producent-konsument w celu wykorzystania 8 rdzeni, ale zarządzanie pamięcią było zbyt nieuporządkowane.
Aby sprawdzić, czy liczba pierwsza jest „dobrą” liczbą pierwszą, używam „deterministycznego” testu Millera-Rabina (co tak naprawdę oznacza test Millera-Rabina, który ktoś wcześniej sprawdził na podstawie listy generowanej deterministycznie). Można to z pewnością przepisać, aby użyć również Atkina-Bernsteina, z pewnym buforowaniem, aby pokryć zakresy odpowiadające sqrt, cbrt itp., Ale nie jestem pewien, czy byłoby to ulepszenie (ponieważ testowałoby wiele liczb, które Nie muszę testować), więc to eksperyment na kolejny dzień.
Na moim dość starym komputerze to działa
a właściwie dokładnie 2 minuty
odpowiednio o 3:10, 3:20 i 3:30.
Zapisz jako
PPCG65876.java
, skompiluj jakojavac PPCG65876.java
i uruchom jakojava -Xmx1G PPCG65876
.źródło
isGood
kontroli.C ++, 2754 (wszystkie wartości, test pierwotności siły brutalnej)
To brutalna siła, ale jest to początek, zanim nasi matematycy mogą zacząć pracować z czymś bardziej wydajnym.
W razie potrzeby mogę dodać więcej wyjaśnień, ale prawdopodobnie jest to bardzo oczywiste z kodu. Ponieważ jeśli
p
jest liczbą pierwszą inną niż 2, wiemy, żep - 1
jest parzysta, a jednym z dwóch czynników jest zawsze 2. Wyliczamy liczby pierwsze, zmniejszamyp - 1
o wszystkie czynniki 2 i sprawdzamy, czy pozostała wartość jest liczbą pierwszą, czy że wszystkie jego czynniki są takie same.Kod:
Program drukuje różnicę oraz odpowiadające jej dwie dobre liczby pierwsze za każdym razem, gdy zostanie znaleziona nowa maksymalna różnica. Przykładowe dane wyjściowe z testu uruchomionego na moim komputerze, gdzie zgłoszona wartość 2754 zostaje znaleziona po około 1:20 minutach:
źródło
C ++, 14226 (tylko wysokie wartości, test Millera-Rabina)
Publikowanie tego osobno, ponieważ całkowicie różni się od mojego początkowego rozwiązania i nie chciałem całkowicie zastępować postu, który otrzymał wiele pozytywnych opinii.
Dzięki @primo za wskazanie problemu z oryginalną wersją. W teście liczby pierwszej nastąpiło przepełnienie dużych liczb.
Wykorzystuje to niektóre spostrzeżenia uzyskane podczas ewolucji innych rozwiązań. Główne obserwacje to:
Na tej podstawie zastosowana tutaj metoda jest dość prosta:
Wyniki:
Kod:
źródło
PyPy-2.4.0
Python-2
x
Pliks...Odcinek: „Spójrz mamo! Ani jednej dywizji!”
;-)
Testowałem to na Debian8 z PyPy-2.4.0, a Python2 zaczął się tak:
Jeśli naprawdę jest dużo pamięci RAM,
del L[n]
wiersz może zostać usunięty.Podstawowy generator liczb pierwszych jest następujący:
Zasadniczo robi dokładnie to, co robi sito Eratostenesa, ale w innej kolejności.
L
jest słownikiem, ale może być postrzegany jako lista (taśma) list liczb. Nieistniejące komórkiL[n]
są interpretowane tak, jak do tejn
pory nie są znane podstawowe dzielniki.while
Pętla robi prime lub nie prime decission na każdym nawrocie doL[n]
.Jeśli
L[n]
istnieje (to samo con in L
),P = L[n]
to lista różnych głównych dzielnikówn
. Więcn
nie jest liczbą pierwszą.Jeśli
L[n]
nie istnieje, nie znaleziono głównego dzielnika. Więcn
musi być liczbą pierwszą,P = [n]
będąc znanym dzielnikiem.Teraz
P
jest lista znanych głównych dzielników dla obu przypadków.for p in P
Pętli porusza każdy wjazdP
do przodu o odstęp od jego wartość na taśmie liczb.W ten sposób dzielniki skaczą na taśmę i to jest powód, dla którego te skaczące liczby muszą być pierwsze. Nowe liczby
else
pojawiają się na taśmie tylko w wyniku powyższej decyzji i są to liczby bez znanych dzielników innych niż one same. Organizacje nonprime nigdy nie trafiają na te listyL[n]
.Liczby pierwsze przeskakujące na liście są różne, ponieważ każda liczba
n
jest sprawdzana tylko raz i jest dodawana tylko jako dzielnik (jeśli nie liczba pierwsza :)0
lub (jeśli liczba pierwsza :)1
razy. Tylko znane dzielniki główne będą się poruszać do przodu, ale nigdy się nie powielą. Tak więcL[n]
zawsze będą miały wyraźne główne dzielniki lub będą puste.Powrót do górnego programu dla dobrych luk w liczbach pierwszych:
... utrzymuje prime dzielniki
n
wB
kiedyn
wiadomo, że nie będzie pierwsza.Jeśli
n
zostanie uznana za pierwszą,B
zawiera listę głównych dzielników poprzedniego przejścia w pętli, patrząc nan-1
:...
len(B) == 2
oznacza to, żen - 1
ma dwa różne główne dzielniki.g
po prostu pamięta ostatnią dobrąM
dobrą liczbę pierwszą przed nową, jest długością poprzedniej maksymalnej dobrej pierwszej prime im
długością nowo znalezionej luki.Szczęśliwe zakończenie.
źródło
C #, prawdopodobnie 1932
Przekonałem się, że im szybciej algorytm znajduje liczby pierwsze, tym lepszy wynik. Jestem też całkiem pewien, że mój algorytm nie jest najbardziej optymalną metodą wyszukiwania podstawowego.
źródło
Python 3, 546
... za dwie minuty na moim komputerze, który moim zdaniem jest znacznie mniej wydajny niż twój.
Prawdopodobnie mógłbym to usprawnić, optymalizując
x=2
skrzynkę, ale eh. Wystarczająco dobry. : Pźródło
p: 2, q: 3, |q-p|: 1
dla mnie wychodzi.Idź, prawdopodobnie 756
Wstyd! Jestem takim nowicjuszem, że tylko naiwnie użyłem starego kodu i oczekiwałem, że zadziała i będzie szybki! Gdybym to zaimplementował i zbudował w oparciu o dobre liczby pierwsze, byłoby to o wiele szybsze, ale niestety, uczę się. (Prawdopodobnie jutro odpowiem ponownie w pełni przebudowanym rozwiązaniem, które jest specjalnie zaprojektowane).
Oczywiście wykorzystuje współbieżność.
źródło
Java, 4224 (99,29 s)
Mocno spersonalizowane sito Eratostenesa z przewagą BitSet
Czas potrzebny zależy od maksymalnego limitu liczb pierwszych, które będą obliczane.
Dla
źródło
Python 3, 1464
Z pomocą Lembika , którego pomysłem było po prostu sprawdzenie pierwszych dwóch dobrych liczb pierwszych po potędze dwóch, a gdy zostanie znaleziony, natychmiast przejdź do następnej potęgi dwóch. Jeśli ktoś może użyć tego jako punktu przeskoku, nie krępuj się. Część moich wyników znajduje się poniżej po uruchomieniu tego w IDLE. Kod następuje.
Podziękowania dla primo, gdy złapałem ich listę małych liczb pierwszych dla tego kodu.
Edycja: Zredagowałem kod, aby pasował do rzeczywistej specyfikacji problemu (dwa różne dzielniki główne nie do końca dwa odrębne dzielniki główne), i wdrożyłem, aby nie przechodzić do następnej potęgi dwóch, dopóki dobre liczby pierwsze znalezione przez program nie mają luka większa niż z ostatnich dwóch dobrych liczb pierwszych, które znalazła. Powinienem również podziękować Peterowi Taylorowi , ponieważ wykorzystałem jego pomysł, że dobre liczby pierwsze mogą być tylko kilkoma wartościami mod 60.
Ponownie uruchomiłem to na wolnym komputerze w trybie IDLE, więc wyniki mogą być szybsze w przypadku PyPy, ale nie byłem w stanie tego sprawdzić.
Próbka moich wyników (p, q, qp, czas):
Mój kod:
źródło
j
,4
czy nie2
? I wydaje się bezwarunkowo odrzucić, jeślij-1
nie jest najlepszym razy potęgą dwójki, gdzie powinno być testowanie czy to się doskonałą moc razy potęgą dwójki.Idź: Wszystkie liczby całkowite: 5112
good.go
:Wydajność:
Dla porównania: peterSO max 5112 w 41,04s kontra Coder max 4176 w 51,97s.
Koder: max | qp | 4176 q 1964330609 p 1964326433
Wydajność:
źródło