Bestenliste
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
Hintergrund
Wenn Sie an einem 2D-Gitter mit Ganzzahlkoordinaten arbeiten, möchten Sie manchmal wissen, ob zwei Vektoren (mit Ganzzahlkomponenten) die gleiche Größe haben. Natürlich ist in der euklidischen Geometrie die Größe eines Vektors (x,y)
gegeben durch
√(x² + y²)
Daher könnte eine naive Implementierung diesen Wert für beide Vektoren berechnen und die Ergebnisse vergleichen. Dies verursacht nicht nur eine unnötige Quadratwurzelberechnung, sondern auch Probleme mit Gleitkommaungenauigkeiten, die zu falsch positiven Ergebnissen führen können: Vektoren, deren Größen unterschiedlich sind, bei denen jedoch alle signifikanten Ziffern in der Gleitkommadarstellung identisch sind.
Für die Zwecke dieser Herausforderung, definieren wir eine falsche positive als ein Paar von Koordinatenpaaren (a,b)
und (c,d)
für die gilt :
- Ihre quadratische Größe unterscheidet sich, wenn sie als 64-Bit-Ganzzahlen ohne Vorzeichen dargestellt werden.
- Ihre Größe ist identisch, wenn sie als 64-Bit-Gleitkommazahl dargestellt und über eine 64-Bit-Quadratwurzel (gemäß IEEE 754 ) berechnet wird .
Beispielsweise ist bei Verwendung von 16-Bit-Darstellungen (anstelle von 64) das kleinste 1- Paar von Vektoren, das ein falsches Positiv ergibt, gleich
(25,20) and (32,0)
Ihre quadratischen Beträge sind 1025
und 1024
. Die Quadratwurzelerträge nehmen
32.01562118716424 and 32.0
Aber in 16-Bit-Floats werden beide abgeschnitten 32.0
.
Ebenso ist das kleinste 2- Paar , das für 32-Bit-Darstellungen ein falsches Positiv ergibt,
(1659,1220) and (1951,659)
1 "Kleinste" gemessen an der 16-Bit-Fließkomma-Größe.
2 "Kleinste" gemessen an der 32-Bit-Gleitkomma-Größe.
Zum Schluss noch eine Handvoll gültiger 64-Bit-Fälle:
(51594363,51594339) and (54792160,48184783)
(54356775,54353746) and (54620742,54088476)
(54197313,46971217) and (51758889,49645356)
(67102042, 956863) and (67108864, 6) *
*
Der letzte Fall ist einer von mehreren mit der kleinstmöglichen Größe für 64-Bit-Fehlalarme.
Die Herausforderung
In weniger als 10.000 Byte Code finden Sie mit einem einzigen Thread so viele falsch-positive Ergebnisse für 64-Bit-Gleitkommazahlen (binär) im Koordinatenbereich 0 ≤ y ≤ x
(dh nur innerhalb des ersten Oktanten der Euklidischen Ebene). so dass innerhalb von 10 Minuten . Wenn zwei Einsendungen die gleiche Anzahl von Paaren ergeben, ist der Gleichstand die tatsächliche Zeit, die benötigt wird, um das letzte dieser Paare zu finden.x² + y² ≤ 253
Ihr Programm darf zu keinem Zeitpunkt mehr als 4 GB Speicher belegen (aus praktischen Gründen).
Es muss möglich sein, Ihr Programm in zwei Modi auszuführen: einen, der jedes gefundene Paar ausgibt, und einen, der nur die Anzahl der gefundenen Paare am Ende ausgibt. Der erste wird verwendet, um die Gültigkeit Ihrer Paare zu überprüfen (indem Sie sich einige Beispiele von Ausgaben ansehen), und der zweite wird verwendet, um Ihre Einreichung tatsächlich zeitlich zu steuern. Beachten Sie, dass der Druck der einzige Unterschied sein muss. Insbesondere kann das Zählen Programm nicht die Anzahl der Paare codiert es könnte finden. Es muss immer noch dieselbe Schleife ausgeführt werden, die zum Drucken aller Zahlen verwendet wird, und der Druck selbst muss nur weggelassen werden!
Ich werde alle Einsendungen auf meinem Windows 8-Laptop testen. Bitte fragen Sie in den Kommentaren, ob Sie eine nicht allzu verbreitete Sprache verwenden möchten.
Beachten Sie, dass Paare beim Umschalten des ersten und zweiten Koordinatenpaars nicht zweimal gezählt werden dürfen .
Beachten Sie auch, dass ich Ihren Prozess über einen Ruby-Controller ausführen werde, der Ihren Prozess beendet, wenn er nicht nach 10 Minuten beendet ist. Stellen Sie sicher, dass Sie die Anzahl der bis dahin gefundenen Paare ausgeben. Sie können entweder die Zeit selbst verfolgen und das Ergebnis kurz vor Ablauf der 10 Minuten ausdrucken, oder Sie geben nur die Anzahl der sporadisch gefundenen Paare aus, und ich nehme die letzte Zahl als Ihre Punktzahl.
quelle
Antworten:
C ++, 275.000.000+
Wir bezeichnen Paare, deren Größe genau darstellbar ist, wie z. B. (x, 0) , als ehrliche Paare und alle anderen Paare als unehrliche Paare der Größe m , wobei m die falsch angegebene Größe des Paares ist. Das erste Programm im vorherigen Beitrag verwendete eine Reihe eng verwandter Paare von ehrlichen und unehrlichen Paaren:
(x, 0) bzw. (x, 1) für x , das groß genug ist. Das zweite Programm verwendete dieselbe Menge unehrlicher Paare, erweiterte jedoch die Menge ehrlicher Paare, indem es nach allen ehrlichen Paaren mit ganzzahliger Größe suchte. Das Programm wird nicht innerhalb von zehn Minuten beendet, findet jedoch den größten Teil seiner Ergebnisse sehr früh. Dies bedeutet, dass ein Großteil der Laufzeit verschwendet wird. Anstatt immer seltener nach ehrlichen Paaren zu suchen, nutzt dieses Programm die freie Zeit, um die nächste logische Sache zu tun: die Menge der unehrlichen Paare zu erweitern.
Von der früheren Post wissen wir , dass für alle groß genug , um ganze Zahlen r , sqrt (r 2 + 1) = r , wobei sqrt die Fließkommaquadratwurzelfunktion ist. Unser Angriffsplan besteht darin, Paare P = (x, y) zu finden, so dass x 2 + y 2 = r 2 + 1 für eine ausreichend große ganze Zahl r gilt . Das ist einfach genug, aber naiv nach solchen Paaren zu suchen, ist zu langsam, um interessant zu sein. Wir möchten diese Paare in großen Mengen finden, so wie wir es im vorherigen Programm für ehrliche Paare getan haben.
Sei { v , w } ein orthonormales Vektorpaar. Für alle reellen Skalare gilt r , || r v + w || 2 = r 2 + 1 . In ℝ 2 ist dies ein direktes Ergebnis des Satzes von Pythagoras:
Wir suchen nach Vektoren v und w, so dass es eine ganze Zahl r gibt, für die x und y auch ganze Zahlen sind. Als Randnotiz sei angemerkt, dass die Menge der unehrlichen Paare, die wir in den beiden vorhergehenden Programmen verwendet haben, lediglich ein Sonderfall davon war, wobei { v , w } die Standardbasis von ℝ 2 war ; Dieses Mal möchten wir eine allgemeinere Lösung finden. Hier sind pythagoreische Tripletts (ganzzahlige Tripletts (a, b, c), die a 2 + b 2 = c 2 erfüllen, die wir im vorherigen Programm verwendet haben) machen ihr Comeback.
Sei (a, b, c) ein pythagoreisches Triplett. Die Vektoren v = (b / c, a / c) und w = (-a / c, b / c) (und auch
w = (a / c, -b / c) ) sind orthonormal, wie leicht zu überprüfen ist . Wie sich herausstellt, existiert für jede Wahl des pythagoreischen Triplets eine ganze Zahl r, so dass x und y ganze Zahlen sind. Um dies zu beweisen und r und P effektiv zu finden , brauchen wir eine kleine Zahl / Gruppentheorie; Ich werde die Details schonen. Angenommen, wir haben unser Integral r , x und y . Wir haben noch ein paar Kleinigkeiten: Wir brauchen rum groß genug zu sein und wir wollen eine schnelle Methode, um noch viele ähnliche Paare von diesem abzuleiten. Glücklicherweise gibt es einen einfachen Weg, dies zu erreichen.
Man beachte , dass die Projektion von P auf v ist r v , also R = P · V = (x, y) · (b / c, a / c) = XB / c + YA / c , alles dies , dass sagen xb + ya = rc . Als Ergebnis für alle ganzen Zahlen 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. Mit anderen Worten, die quadratische Größe von Paaren der Form
(x + bn, y + an) ist (r + cn) 2 + 1 , was genau die Art von Paaren ist, nach denen wir suchen! Für groß genug n sind dies unehrliche Größenpaare r + cn .
Es ist immer schön, sich ein konkretes Beispiel anzuschauen. Wenn wir das pythagoreische Triplett (3, 4, 5) nehmen , dann haben wir bei r = 2 P = (1, 2) (Sie können überprüfen, dass (1, 2) · (4/5, 3/5) = 2 und 1 2 + 2 2 = 2 2 + 1. ) Addieren von 5 zu r und (4, 3) zu P führt uns zu r '= 2 + 5 = 7 und P' = (1 + 4, 2 + 3) = (5, 5) . Siehe da, 5 2 + 5 2 = 7 2 + 1. Die nächsten Koordinaten sind r '' = 12 und P '' = (9, 8) und wieder 9 2 + 8 2 = 12 2 + 1 und so weiter und so fort ...
Sobald r groß genug ist, erhalten wir unehrliche Paare mit Größeninkrementen von 5 . Das sind ungefähr 27.797.402 / 5 unehrliche Paare.
Jetzt haben wir also viele unehrliche Paare mit ganzzahliger Größe. Wir können sie leicht mit den ehrlichen Paaren des ersten Programms koppeln, um False-Positives zu bilden, und mit der gebotenen Sorgfalt können wir auch die ehrlichen Paare des zweiten Programms verwenden. Dies ist im Grunde, was dieses Programm tut. Wie das vorherige Programm findet auch es die meisten seiner Ergebnisse sehr früh - es erreicht innerhalb weniger Sekunden 200.000.000 falsch positive Ergebnisse - und verlangsamt sich dann beträchtlich.
Kompilieren mit
g++ flspos.cpp -oflspos -std=c++11 -msse2 -mfpmath=sse -O3
. Fügen Sie hinzu, um die Ergebnisse zu überprüfen-DVERIFY
(dies ist erheblich langsamer).Laufen Sie mit
flspos
. Ein beliebiges Befehlszeilenargument für den ausführlichen Modus.quelle
Python, 27.797.402
Nur um die Messlatte etwas höher zu legen ...
Es ist leicht zu überprüfen, ob für alle 67.108.864 <= x <= 94.906.265 = floor (sqrt (2 53 )) die Paare (x, 0) und (x, 1) falsch positiv sind.
Warum es funktioniert : 67.108.864 = 2 26 . Daher haben alle Zahlen x im obigen Bereich die Form 2 26 + x ' für einige 0 <= x' <2 26 . Für alle positiven e ist (x + e) 2 = x 2 + 2xe + e 2 = x 2 + 2 27 e + 2x'e + e 2 . Wenn wir
(x + e) 2 = x 2 + 1 haben wollen, brauchen wir mindestens 2 27 e <= 1 , dh e <= 2 -27 Da jedoch die Mantisse von Gleitkommazahlen mit doppelter Genauigkeit 52 Bit breit ist, ist das kleinste e, so dass x + e> x ist, e = 2 26 - 52 = 2 -26 . Mit anderen Worten, die kleinste darstellbare Zahl, die größer als x ist, ist x + 2 -26, während das Ergebnis von sqrt (x 2 + 1) höchstens x + 2 -27 ist . Da der standardmäßige IEEE-754-Rundungsmodus auf die nächste Runde eingestellt ist; Unentschieden, es wird immer auf x und nie auf x + 2 -26 gerundet (wobei der Unentschieden wirklich nur für x = 67,108,864 relevant ist, wenn überhaupt. Jede größere Zahl wird unabhängig davon auf x gerundet .
C ++, 75.000.000+
Denken Sie daran, dass 3 2 + 4 2 = 5 2 . Dies bedeutet, dass der Punkt (4, 3) auf dem Kreis mit dem Radius 5 liegt, der um den Ursprung zentriert ist. Eigentlich für alle ganzen Zahlen n , (4n, 3n) liegt auf einem solchen Kreis mit dem Radius 5N . Für ausreichend großes n (nämlich so, dass 5n> = 2 26 ) kennen wir bereits ein falsch positives Ergebnis für alle Punkte auf diesem Kreis: (5n, 1) . Groß! Das sind weitere 27.797.402 / 5 freie falsch-positive Paare genau dort! Aber warum hier aufhören? (3, 4, 5) ist nicht das einzige derartige Triplett.
Dieses Programm sucht nach allen positiven ganzzahligen Triplets (a, b, c), so dass a 2 + b 2 = c 2 , und zählt auf diese Weise falsch-positive. Es kommt ziemlich schnell zu 70.000.000 Fehlalarmen, verlangsamt sich dann aber beträchtlich, wenn die Zahlen wachsen.
Kompilieren mit
g++ flspos.cpp -oflspos -std=c++11 -msse2 -mfpmath=sse -O3
. Fügen Sie hinzu, um die Ergebnisse zu überprüfen-DVERIFY
(dies ist erheblich langsamer).Laufen Sie mit
flspos
. Ein beliebiges Befehlszeilenargument für den ausführlichen Modus.quelle
2**53
Grenze gewählt wurde, um dies auszuschließen, aber ich denke nicht.C ++ 11 - 100,993,667
EDIT: Neues Programm.
Der alte benutzte zu viel Speicher. Dieser halbiert die Speichernutzung, indem ein riesiges Vektorarray anstelle einer Hash-Tabelle verwendet wird. Außerdem wird die zufällige Fadenkruft entfernt.
Führen Sie ein
-P
Argument aus, um die Punkte anstelle der Anzahl auszudrucken.Bei mir dauert es im Zählmodus weniger als 2 Minuten und beim Drucken auf eine Datei (~ 4 GB) ungefähr 5 Minuten, sodass es nicht ganz zu einer I / O-Beschränkung kam.
Mein ursprüngliches Programm war ordentlich, aber ich ließ das meiste davon fallen, da es nur in der Größenordnung von 10 ^ 5 Ergebnissen produzieren konnte. Es wurde nach Parametrisierungen der Form (x ^ 2 + Axe + B, x ^ 2 + Cx + D), (x ^ 2 + Axe + b, x ^ 2 + cx + d) gesucht, so dass für jede x, (x ^ 2 + Ax + B) ^ 2 + (x ^ 2 + Cx + D) ^ 2 = (x ^ 2 + Ax + b) ^ 2 + (x ^ 2 + cx + d) ^ 2 + 1. Als ein solcher Satz von Parametern {a, b, c, d, A, B, C, D} gefunden wurde, wurden alle x-Werte unter dem Maximum geprüft. Bei der Betrachtung meiner Debug-Ausgabe dieses Programms fiel mir eine bestimmte Parametrisierung der Parametrisierung auf, die es mir ermöglichte, auf einfache Weise viele Zahlen zu erzeugen. Ich habe mich dafür entschieden, Ell's Nummern nicht auszudrucken, da ich genug eigene hatte. Hoffentlich druckt jetzt jemand nicht beide Zahlen aus und behauptet, der Gewinner zu sein :)
quelle
g++ (GCC) 4.8.1
. Okay, ich habe die Thread-Bits entfernt, aber es wirdstricmp
aus irgendeinem Grund immer noch nicht erkannt .Java, Bresenham-ähnlicher Kreisscan
Aus heuristischer Sicht erwarte ich mehr Kollisionen, wenn ich am breiteren Ende des Rings beginne. Ich erwartete eine gewisse Verbesserung, indem ich für jede Kollision einen Scan durchführte, bei dem Werte
surplus
zwischen0
undr2max - r2
einschließlich aufgezeichnet wurden, aber in meinen Tests erwies sich dies als langsamer als diese Version. In ähnlicher Weise wird versucht, einen einzelnenint[]
Puffer zu verwenden, anstatt viele Arrays und Listen mit zwei Elementen zu erstellen. Leistungsoptimierung ist in der Tat ein seltsames Biest.Führen Sie mit einem Befehlszeilenargument für die Ausgabe der Paare und ohne für einfache Zählungen aus.
quelle
Java - 27.817.255
Die meisten davon sind die gleichen wie die, die Ell zeigt , und der Rest basiert auf
(j,0) (k,l)
. Für jedesj
gehe ich ein paar Felder zurück und überprüfe, ob der Rest falsch positiv ist. Dies nimmt im Grunde genommen die gesamte Zeit in Anspruch, mit nur 25.000 (ca. 0,1%) Gewinn(j,0) (j,1)
, aber ein Gewinn ist ein Gewinn.Dies wird in weniger als zehn Minuten auf meinem Computer abgeschlossen sein, aber ich weiß nicht, was Sie haben. Wenn es nicht vor Ablauf der Zeit beendet wird, hat es eine drastisch schlechtere Punktzahl. In diesem Fall können Sie den Divisor in Zeile 8 so einstellen, dass er pünktlich endet (dies bestimmt einfach, wie weit er für jede Zeile zurückgeht
j
). Für einige verschiedene Teiler lauten die Punkte:Um die Ausgabe für jedes Match einzuschalten (und, Gott, es ist langsam, wenn Sie das tun), entfernen Sie einfach die Kommentare in den Zeilen 10 und 19.
Als Referenz sind die ersten 20 Ausgaben (für Divisor = 7, ausgenommen
(j,0)(j,1)
Typen):quelle
Julia, 530 Fehlalarme
Hier ist eine sehr naive Brute-Force-Suche, die Sie als Referenzimplementierung ansehen können.
Sie können die Paare (und ihre exakten quadratischen Größen) ausdrucken, indem Sie die
@printf
Zeile auskommentieren .Grundsätzlich startet dies die Suche
x = y = 6e7
nach dem ersten Koordinatenpaar und tastet ungefähr 1% des Weges zur x-Achse ab, bevor x dekrementiert wird. Dann prüft es für jedes derartige Koordinatenpaar den gesamten Bogen der gleichen Größe (Auf- und Abrunden) auf eine Kollision.Der Code geht davon aus, dass er auf einem 64-Bit-System ausgeführt wird, sodass die standardmäßigen Ganzzahl- und Gleitkommatypen 64-Bit-Typen sind (andernfalls können Sie sie mit
int64()
undfloat64()
Konstruktoren erstellen ).Das ergibt magere 530 Ergebnisse.
quelle