Tabela liderów
User Language Score
=========================================
Ell C++11 293,619,555
feersum C++11 100,993,667
Ell C++11 78,824,732
Geobits Java 27,817,255
Ell Python 27,797,402
Peter Taylor Java 2,468
<reference> Julia 530
tło
Pracując na siatce 2D współrzędnych całkowitych, czasami chcesz wiedzieć, czy dwa wektory (ze składowymi liczb całkowitych) mają tę samą wielkość. Oczywiście w geometrii euklidesowej wielkość wektora (x,y)
podaje
√(x² + y²)
Zatem naiwna implementacja może obliczyć tę wartość dla obu wektorów i porównać wyniki. Nie tylko powoduje to niepotrzebne obliczanie pierwiastka kwadratowego, ale także powoduje problemy z niedokładnościami zmiennoprzecinkowymi, które mogą dawać fałszywie dodatnie: wektory o różnej wielkości, ale w których wszystkie cyfry znaczące w reprezentacji zmiennoprzecinkowej są identyczne.
Na potrzeby tego wyzwania definiujemy wartość fałszywie dodatnią jako parę par współrzędnych (a,b)
i (c,d)
dla których:
- Ich kwadratowa wielkość jest różna, gdy jest reprezentowana jako 64-bitowe liczby całkowite bez znaku.
- Ich wielkość jest identyczna, gdy jest reprezentowana jako 64-bitowa binarna liczba zmiennoprzecinkowa i obliczana na podstawie 64-bitowego pierwiastka kwadratowego (zgodnie z IEEE 754 ).
Na przykład, używając reprezentacji 16-bitowych (zamiast 64), najmniejsza 1 para wektorów, która daje wynik fałszywie dodatni, to
(25,20) and (32,0)
Ich kwadratowe wielkości kwadratowe są 1025
i 1024
. Biorąc pierwiastek kwadratowy daje
32.01562118716424 and 32.0
Ale w pływakach 16-bitowych oba te zostają obcięte 32.0
.
Podobnie, najmniejsza 2 para dająca fałszywie dodatni dla reprezentacji 32-bitowych to
(1659,1220) and (1951,659)
1 „Najmniejszy” mierzony za pomocą 16-bitowej wielkości zmiennoprzecinkowej.
2 „Najmniejszy” mierzony ich 32-bitową wielkością zmiennoprzecinkową.
Na koniec kilka garści prawidłowych 64-bitowych przypadków:
(51594363,51594339) and (54792160,48184783)
(54356775,54353746) and (54620742,54088476)
(54197313,46971217) and (51758889,49645356)
(67102042, 956863) and (67108864, 6) *
*
Ostatni przypadek jest jednym z kilku o najmniejszej możliwej wielkości dla 64-bitowych wyników fałszywie dodatnich.
Wyzwanie
W mniej niż 10 000 bajtów kodu, przy użyciu jednego wątku, znajdziesz tyle fałszywych wyników dodatnich dla liczb zmiennoprzecinkowych 64-bitowych (binarnych) w zakresie współrzędnych 0 ≤ y ≤ x
(to znaczy tylko w obrębie pierwszej oktany płaszczyzny euklidesowej) tak, że w ciągu 10 minut . Jeśli dwa zgłoszenia wiążą się dla tej samej liczby par, przerywnikiem remisu jest faktyczny czas potrzebny na znalezienie ostatniej z tych par.x² + y² ≤ 253
Twój program nie może zużywać więcej niż 4 GB pamięci (ze względów praktycznych).
Musi istnieć możliwość uruchomienia programu w dwóch trybach: jeden, który wypisuje każdą znalezioną parę, i jeden, który wypisuje tylko liczbę znalezionych par na końcu. Pierwszy zostanie użyty do zweryfikowania poprawności twoich par (poprzez sprawdzenie próbki wyników), a drugi zostanie wykorzystany do faktycznego pomiaru czasu przesłania. Pamiętaj, że druk musi być jedyną różnicą. W szczególności program zliczający może nie zakodować na stałe liczby par, które może znaleźć. Musi nadal wykonywać tę samą pętlę, która byłaby użyta do wydrukowania wszystkich liczb, i pomijać sam wydruk!
Przetestuję wszystkie zgłoszenia na moim laptopie z systemem Windows 8, więc w komentarzach zapytaj, czy chcesz użyć niezbyt popularnego języka.
Należy pamiętać, że par nie można liczyć dwukrotnie podczas przełączania pierwszej i drugiej pary współrzędnych.
Zauważ też, że uruchomię twój proces za pośrednictwem kontrolera Ruby, który zabije twój proces, jeśli nie zakończy się po 10 minutach. Pamiętaj, aby podać liczbę znalezionych par do tego czasu. Możesz albo samodzielnie śledzić czas i wydrukować wynik tuż przed upływem 10 minut, albo po prostu wypisywać sporadycznie liczbę znalezionych par, a ja wezmę ostatnią taką liczbę jako twój wynik.
źródło
Odpowiedzi:
C ++, 275 000 000+
Będziemy odnosić się do par, których wielkość jest dokładnie reprezentowalna, takich jak (x, 0) , jako uczciwe pary, a do wszystkich innych par jako nieuczciwe pary wielkości m , gdzie m jest błędnie zgłoszoną wielkością pary. Pierwszy program w poprzednim poście wykorzystywał zestaw ściśle ze sobą powiązanych par uczciwych i nieuczciwych par: odpowiednio
(x, 0) i (x, 1) dla wystarczająco dużego x. Drugi program wykorzystywał ten sam zestaw nieuczciwych par, ale rozszerzył zestaw uczciwych par, szukając wszystkich uczciwych par integralnej wielkości. Program nie kończy się w ciągu dziesięciu minut, ale większość swoich wyników znajduje bardzo wcześnie, co oznacza, że większość czasu działania marnuje się. Zamiast szukać coraz rzadziej uczciwych par, ten program wykorzystuje wolny czas, aby zrobić następną logiczną rzecz: rozszerzenie zestawu nieuczciwych par.
Z poprzedniego postu wiemy, że dla wszystkich wystarczająco dużych liczb całkowitych r , sqrt (r 2 + 1) = r , gdzie sqrt jest zmiennoprzecinkową funkcją pierwiastka zmiennoprzecinkowego. Nasz plan ataku polega na znalezieniu par P = (x, y) takich, że x 2 + y 2 = r 2 + 1 dla pewnej dużej liczby całkowitej r . To dość proste, ale naiwne szukanie pojedynczych takich par jest zbyt wolne, aby było interesujące. Chcemy znaleźć te pary masowo, tak jak robiliśmy to dla uczciwych par w poprzednim programie.
Niech { v , w } będzie parą wektorów ortonormalnych. Dla wszystkich rzeczywistych skalarów r , || r v + w || 2 = r 2 + 1 . W ℝ 2 jest to bezpośredni wynik twierdzenia Pitagorasa:
Szukamy wektorów v i w takich, że istnieje liczba całkowita r, dla której x i y są również liczbami całkowitymi. Na marginesie, zauważmy, że zestaw nieuczciwych par, których użyliśmy w poprzednich dwóch programach, był po prostu szczególnym przypadkiem tego, gdzie { v , w } było standardową podstawą ℝ 2 ; tym razem chcemy znaleźć bardziej ogólne rozwiązanie. To jest, gdy pitagorejską tryplety (liczby całkowite tryplety (a, b, c) , spełniających w 2 + b 2 = C 2, które wykorzystaliśmy w poprzednim programie) powracają.
Niech (a, b, c) będzie triolą pitagorejską. Wektory v = (b / c, a / c) i w = (-a / c, b / c) (a także
w = (a / c, -b / c) ) są ortonormalne, co łatwo zweryfikować . Jak się okazało, dla każdego wyboru Pitagorasa triplet, istnieje całkowita R w taki sposób, x i y są liczbami całkowitymi. Aby to udowodnić i skutecznie znaleźć r i P , potrzebujemy małej teorii liczb / grup; Oszczędzę szczegóły. Tak czy inaczej, załóżmy, że mamy naszą całkę r , x i y . Wciąż brakuje nam kilku rzeczy: potrzebujemy rbyć wystarczająco duże i chcemy szybkiej metody, aby uzyskać z niej wiele podobnych par. Na szczęście istnieje prosty sposób na osiągnięcie tego.
Zauważ, że rzut P na v jest r v , stąd r = P · v = (x, y) · (b / c, a / c) = xb / c + ya / c , wszystko to oznacza, że xb + ya = rc . W rezultacie dla wszystkich liczb całkowitych n , (x + bn) 2 + (y + an) 2 = (x 2 + y 2 ) + 2 (xb + ya) n + (a 2 + b 2 ) n 2 = ( r 2 + 1) + 2 (rc) n + (c 2 ) n 2 = (r + cn) 2 + 1. Innymi słowy, kwadratowa wielkość par postaci
(x + bn, y + an) wynosi (r + cn) 2 + 1 , a dokładnie takich par szukamy! Dla wystarczająco dużego n są to nieuczciwe pary wielkości r + cn .
Zawsze miło jest spojrzeć na konkretny przykład. Jeśli weźmiemy triplet Pitagorasa (3, 4, 5) , to przy r = 2 mamy P = (1, 2) (możesz sprawdzić, że (1, 2) · (4/5, 3/5) = 2 i oczywiście 1 2 + 2 2 = 2 2 + 1 ). Dodanie 5 do r oraz (4, 3) do P prowadzi nas do r '= 2 + 5 = 7 i P' = (1 + 4, 2 + 3) = (5, 5) . Lo i oto 5 2 + 5 2 = 7 2 + 1. Kolejne współrzędne to r '' = 12 i P '' = (9, 8) , i znowu 9 2 + 8 2 = 12 2 + 1 itd. I tak dalej ...
Gdy r jest wystarczająco duże, zaczynamy otrzymywać nieuczciwe pary o przyrostach wielkości 5 . To mniej więcej 27 797 402/5 nieuczciwych par.
Więc teraz mamy wiele nieuczciwych par integralnej wielkości. Możemy z łatwością połączyć je z uczciwymi parami pierwszego programu, aby utworzyć fałszywie dodatnie, a przy należytej staranności możemy również użyć uczciwych par drugiego programu. Zasadniczo to robi ten program. Podobnie jak poprzedni program, większość wyników bardzo wcześnie wykrywa - osiąga 200 000 000 fałszywych trafień w ciągu kilku sekund --- a następnie znacznie zwalnia.
Kompiluj z
g++ flspos.cpp -oflspos -std=c++11 -msse2 -mfpmath=sse -O3
. Aby zweryfikować wyniki, dodaj-DVERIFY
(będzie to znacznie wolniejsze).Uruchom z
flspos
. Dowolny argument wiersza polecenia dla trybu pełnego.źródło
Python, 27 797 402
Wystarczy ustawić poprzeczkę nieco wyżej ...
Łatwo jest zweryfikować, że dla wszystkich 67 108 864 <= x <= 94 906 265 = piętro (sqrt (2 53 )) pary (x, 0) i (x, 1) są fałszywie dodatnie.
Dlaczego to działa : 67 108 864 = 2 26 . W związku z tym wszystkie liczby x w powyższym zakresie mają postać 2 26 + x ' dla niektórych 0 <= x' <2 26 . Dla wszystkich dodatnich e , (x + e) 2 = x 2 + 2xe + e 2 = x 2 + 2 27 e + 2x'e + e 2 . Jeśli chcemy mieć
(x + e) 2 = x 2 + 1 , potrzebujemy co najmniej 2 27 e <= 1 , to znaczy e <= 2-27 Ponieważ jednak mantysa liczb zmiennoprzecinkowych o podwójnej precyzji ma szerokość 52-bitową, najmniejsze e takie, że x + e> x wynosi e = 2 26–52 = 2–26 . Innymi słowy, najmniejszą reprezentowalną liczbą większą niż x jest x + 2-26, podczas gdy wynik sqrt (x 2 + 1) wynosi najwyżej x + 2-27 . Ponieważ domyślny tryb zaokrąglania IEEE-754 jest zaokrąglany do najbliższego; remis -do-parzystego, zawsze będzie zaokrąglać do x, a nigdy do x + 2-26 (gdzie remis jest tak naprawdę istotny tylko dla x = 67 108 864, Jeżeli w ogóle. Każda większa liczba zaokrągli się do x niezależnie).
C ++, 75 000 000+
Przypomnij sobie, że 3 2 + 4 2 = 5 2 . Oznacza to, że punkt (4, 3) leży na okręgu o promieniu 5 wyśrodkowanym wokół początku. W rzeczywistości dla wszystkich liczb całkowitych n , (4n, 3n) leży na takim kole o promieniu 5n . Dla wystarczająco dużego n (mianowicie takiego, że 5n> = 2 26 ), znamy już fałszywie dodatni dla wszystkich punktów na tym okręgu: (5n, 1) . Wspaniały! To kolejne 27 797 402/5 bezpłatnych par fałszywie dodatnich właśnie tutaj! Ale po co się tu zatrzymywać? (3, 4, 5) nie jest jedyną taką trojaczką.
Ten program wyszukuje wszystkie dodatnie trojaczki całkowite (a, b, c) tak, że a 2 + b 2 = c 2 , i w ten sposób liczy fałszywie dodatnie. Szybko osiąga 70 000 000 fałszywie dodatnich wyników, ale następnie znacznie zwalnia, gdy liczba rośnie.
Kompiluj z
g++ flspos.cpp -oflspos -std=c++11 -msse2 -mfpmath=sse -O3
. Aby zweryfikować wyniki, dodaj-DVERIFY
(będzie to znacznie wolniejsze).Uruchom z
flspos
. Dowolny argument wiersza polecenia dla trybu pełnego.źródło
2**53
granica została wybrana, aby to wykluczyć, ale nie sądzę.C ++ 11-100,993,667
EDYCJA: Nowy program.
Stary zużywał za dużo pamięci. Ten zmniejsza zużycie pamięci o połowę, używając gigantycznej tablicy wektorowej zamiast tablicy mieszającej. Usuwa również losowy wątek cruft.
Uruchom z
-P
argumentem, aby wydrukować punkty zamiast ich liczby.Dla mnie w trybie zliczania zajmuje to mniej niż 2 minuty i około 5 minut z drukowaniem skierowanym do pliku (~ 4 GB), więc nie do końca było ograniczone we / wy.
Mój oryginalny program był schludny, ale większość z niego zrezygnowałem, ponieważ mógł wygenerować tylko wyniki rzędu 10 ^ 5. Poszukiwano parametryzacji postaci (x ^ 2 + Axe + B, x ^ 2 + Cx + D), (x ^ 2 + ax + b, x ^ 2 + cx + d) tak, aby dla dowolnego x, (x ^ 2 + Axe + B) ^ 2 + (x ^ 2 + Cx + D) ^ 2 = (x ^ 2 + ax + b) ^ 2 + (x ^ 2 + cx + d) ^ 2 + 1. Po znalezieniu takiego zestawu parametrów {a, b, c, d, A, B, C, D} przystąpił do sprawdzenia wszystkich wartości x poniżej maksimum. Patrząc na moje wyniki debugowania z tego programu, zauważyłem pewną parametryzację parametryzacji, która pozwoliła mi z łatwością wygenerować wiele liczb. Zdecydowałem się nie drukować numerów Ella, ponieważ miałem wiele własnych. Mam nadzieję, że teraz ktoś nie wydrukuje obu naszych zestawów liczb i nie ogłosi się zwycięzcą :)
źródło
g++ (GCC) 4.8.1
. Okej, usunąłem bity wątku, alestricmp
z jakiegoś powodu nadal nie rozpoznaje .Skanowanie koła Java, Bresenham-esque
Heurystycznie spodziewam się więcej kolizji, zaczynając od szerszego końca pierścienia. Spodziewałem się ulepszenia, wykonując jeden skan dla każdego kolizji, rejestrując wartości, które
surplus
są pomiędzy0
ir2max - r2
włącznie, ale w moich testach okazało się wolniejsze niż ta wersja. Podobnie próbuje użyć pojedynczegoint[]
bufora, a nie wielu tworzenia tablic i list dwuelementowych. Optymalizacja wydajności jest naprawdę dziwną bestią.Uruchom z argumentem wiersza polecenia dla danych wyjściowych par i bez prostych obliczeń.
źródło
Java - 27 817 255
Większość z nich jest taka sama, jak pokazuje Ell , a pozostałe oparte są na
(j,0) (k,l)
. Dla każdegoj
wracam kilka kwadratów i sprawdzam, czy reszta daje fałszywie dodatni wynik. Zajmuje to w zasadzie cały czas z jedynie 25k (około 0,1%) zyskiem w stosunku do zwykłego(j,0) (j,1)
, ale zysk jest zyskiem.To skończy się za dziesięć minut na moim komputerze, ale nie wiem, co masz. Ponieważ powody, jeśli nie skończy się, zanim skończy się czas, będzie miał znacznie gorszy wynik. W takim przypadku możesz dostosować dzielnik na linii 8, aby skończył się w czasie (to po prostu określa, jak daleko wraca dla każdego
j
). W przypadku niektórych różnych dzielników wyniki są następujące:Aby włączyć wyjście dla każdego meczu (i, na Boga, jest to powolne, jeśli to zrobisz), po prostu odkomentuj linie 10 i 19.
Dla porównania pierwsze 20 wyników, jakie daje (dla dzielnika = 7, wyłączając
(j,0)(j,1)
typy) to:źródło
Julia, 530 fałszywych trafień
Oto bardzo naiwne wyszukiwanie siłowe, które można traktować jako implementację referencyjną.
Możesz wydrukować pary (i ich dokładną kwadratową wielkość), usuwając komentarz z
@printf
linii.Zasadniczo rozpoczyna się wyszukiwanie
x = y = 6e7
pierwszej pary współrzędnych i skanowanie około 1% drogi do osi x przed zmniejszeniem x. Następnie dla każdej takiej pary współrzędnych sprawdza cały łuk tej samej wielkości (zaokrąglając w górę i w dół) pod kątem kolizji.Kod zakłada, że jest prowadzony w systemie 64-bitowym, tak że typy domyślne całkowite i zmiennoprzecinkowe są te, 64-bitowe (jeśli nie, możesz utworzyć je
int64()
ifloat64()
konstruktorów).To daje skromne 530 wyników.
źródło