Classement
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
Contexte
Lorsque vous travaillez sur une grille 2D de coordonnées entières, vous voulez parfois savoir si deux vecteurs (avec des composants entiers) ont la même amplitude. Bien sûr, dans la géométrie euclidienne, la grandeur d'un vecteur (x,y)
est donnée par
√(x² + y²)
Une implémentation naïve pourrait donc calculer cette valeur pour les deux vecteurs et comparer les résultats. Non seulement cela entraîne un calcul inutile de la racine carrée, mais cela cause également des problèmes avec les inexactitudes en virgule flottante, qui pourraient produire des faux positifs: des vecteurs dont les amplitudes sont différentes, mais où les chiffres significatifs de la représentation en virgule flottante sont tous identiques.
Aux fins de ce défi, nous définissons un faux positif comme une paire de paires de coordonnées (a,b)
et (c,d)
pour lequel:
- Leur ampleur au carré est différente lorsqu'elle est représentée sous forme d'entiers non signés 64 bits.
- Leur amplitude est identique lorsqu'elle est représentée sous la forme d'un nombre à virgule flottante binaire 64 bits et calculée via une racine carrée 64 bits (selon IEEE 754 ).
Par exemple, en utilisant des représentations 16 bits (au lieu de 64), la plus petite 1 paire de vecteurs qui donne un faux positif est
(25,20) and (32,0)
Leurs grandeurs carrées au carré sont 1025
et 1024
. Prendre les rendements des racines carrées
32.01562118716424 and 32.0
Mais en 16 bits, les deux flottants sont tronqués 32.0
.
De même, la plus petite paire de 2 donnant un faux positif pour les représentations 32 bits est
(1659,1220) and (1951,659)
1 "Plus petit" mesuré par leur amplitude à virgule flottante 16 bits.
2 "Plus petit" mesuré par leur amplitude à virgule flottante 32 bits.
Enfin, voici quelques cas valides 64 bits:
(51594363,51594339) and (54792160,48184783)
(54356775,54353746) and (54620742,54088476)
(54197313,46971217) and (51758889,49645356)
(67102042, 956863) and (67108864, 6) *
*
Le dernier cas est l'un des nombreux avec la plus petite amplitude possible pour les faux positifs 64 bits.
Le défi
En moins de 10 000 octets de code, à l'aide d'un seul thread, vous devez trouver autant de faux positifs pour les nombres à virgule flottante 64 bits (binaires) dans la plage de coordonnées 0 ≤ y ≤ x
(c'est-à-dire uniquement dans le premier octant du plan euclidien) de telle sorte que dans les 10 minutes . Si deux soumissions sont à égalité pour le même nombre de paires, le bris d'égalité est le temps réel nécessaire pour trouver la dernière de ces paires.x² + y² ≤ 253
Votre programme ne doit pas utiliser plus de 4 Go de mémoire à tout moment (pour des raisons pratiques).
Il doit être possible d'exécuter votre programme en deux modes: un qui génère chaque paire comme il le trouve, et un qui ne produit que le nombre de paires trouvées à la fin. Le premier sera utilisé pour vérifier la validité de vos paires (en regardant un échantillon de sorties) et le second sera utilisé pour chronométrer réellement votre soumission. Notez que l'impression doit être la seule différence. En particulier, le programme de comptage peut ne pas coder en dur le nombre de paires qu'il a pu trouver. Il doit toujours effectuer la même boucle qui serait utilisée pour imprimer tous les nombres, et ne supprimer que l'impression elle-même!
Je vais tester toutes les soumissions sur mon ordinateur portable Windows 8, veuillez donc demander dans les commentaires si vous souhaitez utiliser un langage pas trop courant.
Notez que les paires ne doivent pas être comptées deux fois lors de la commutation des première et deuxième paires de coordonnées.
Notez également que je vais exécuter votre processus via un contrôleur Ruby, qui tuera votre processus s'il n'est pas terminé après 10 minutes. Assurez-vous de sortir le nombre de paires trouvées d'ici là. Vous pouvez soit garder une trace du temps vous-même et imprimer le résultat juste avant les 10 minutes, soit vous pouvez simplement afficher le nombre de paires trouvées sporadiquement, et je prendrai le dernier nombre comme votre score.
la source
Réponses:
C ++, 275 000 000+
Nous ferons référence à des paires dont la magnitude est représentable avec précision, telles que (x, 0) , en tant que paires honnêtes et à toutes les autres paires comme des paires malhonnêtes de magnitude m , où m est la magnitude mal déclarée de la paire. Le premier programme dans le post précédent a utilisé un ensemble de couples étroitement liés de paires honnêtes et malhonnêtes:
(x, 0) et (x, 1) , respectivement, pour assez grand x. Le deuxième programme a utilisé le même ensemble de paires malhonnêtes mais a étendu l'ensemble de paires honnêtes en recherchant toutes les paires honnêtes de magnitude intégrale. Le programme ne s'arrête pas dans les dix minutes, mais il trouve la grande majorité de ses résultats très tôt, ce qui signifie que la plupart du temps d'exécution est gaspillé. Au lieu de continuer à chercher des paires honnêtes de moins en moins fréquentes, ce programme utilise le temps libre pour faire la prochaine chose logique: étendre l'ensemble des paires malhonnêtes .
De l'article précédent, nous savons que pour tous les entiers suffisamment grands r , sqrt (r 2 + 1) = r , où sqrt est la fonction racine carrée à virgule flottante. Notre plan d'attaque est de trouver des paires P = (x, y) telles que x 2 + y 2 = r 2 + 1 pour un entier r assez grand . C'est assez simple à faire, mais chercher naïvement de telles paires individuelles est trop lent pour être intéressant. Nous voulons trouver ces paires en vrac, tout comme nous l'avons fait pour les paires honnêtes dans le programme précédent.
Soit { v , w } une paire orthonormée de vecteurs. Pour tous les scalaires réels r , || r v + w || 2 = r 2 + 1 . Dans ℝ 2 , c'est un résultat direct du théorème de Pythagore:
Nous recherchons des vecteurs v et w tels qu'il existe un entier r pour lequel x et y sont également des entiers. En remarque, notons que l'ensemble des paires malhonnêtes que nous avons utilisées dans les deux programmes précédents n'était qu'un cas particulier de ceci, où { v , w } était la base standard de ℝ 2 ; cette fois, nous souhaitons trouver une solution plus générale. C'est là que les triplets de Pythagore (triplets entiers (a, b, c) satisfaisant a 2 + b 2 = c 2, que nous utilisions dans le programme précédent) font leur retour.
Soit (a, b, c) un triplet pythagoricien. Les vecteurs v = (b / c, a / c) et w = (-a / c, b / c) (et aussi
w = (a / c, -b / c) ) sont orthonormés, comme cela est facile à vérifier . En fait, pour tout choix de triplet de Pythagore, il existe un entier r tel que x et y sont des entiers. Pour le prouver et pour trouver efficacement r et P , nous avons besoin d'une petite théorie des nombres / groupes; Je vais épargner les détails. Quoi qu'il en soit, supposons que nous ayons nos r , x et y intégraux . Nous sommes encore à court de quelques choses: nous avons besoin de rêtre assez grand et nous voulons une méthode rapide pour dériver beaucoup plus de paires similaires de celle-ci. Heureusement, il existe un moyen simple d'y parvenir.
Notez que la projection de P sur v est r v , donc r = P · v = (x, y) · (b / c, a / c) = xb / c + ya / c , tout cela pour dire que xb + ya = rc . Par conséquent, pour tous les entiers 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. En d'autres termes, la magnitude au carré des paires de la forme
(x + bn, y + an) est (r + cn) 2 + 1 , ce qui est exactement le type de paires que nous recherchons! Pour n assez grand , ce sont des paires de magnitude malhonnêtes r + cn .
C'est toujours agréable de regarder un exemple concret. Si nous prenons le triplet de Pythagore (3, 4, 5) , alors à r = 2 nous avons P = (1, 2) (vous pouvez vérifier que (1, 2) · (4/5, 3/5) = 2 et, clairement, 1 2 + 2 2 = 2 2 + 1. ) Ajouter 5 à r et (4, 3) à P nous amène à r '= 2 + 5 = 7 et P' = (1 + 4, 2 + 3) = (5, 5) . Et voilà, 5 2 + 5 2 = 7 2 + 1. Les coordonnées suivantes sont r '' = 12 et P '' = (9, 8) , et encore, 9 2 + 8 2 = 12 2 + 1 , et ainsi de suite, et ainsi de suite ...
Une fois que r est assez grand, nous commençons à obtenir des paires malhonnêtes avec des incréments de magnitude de 5 . Cela représente environ 27 797 402/5 paires malhonnêtes.
Alors maintenant, nous avons beaucoup de paires malhonnêtes de magnitude intégrale. Nous pouvons facilement les coupler avec les paires honnêtes du premier programme pour former des faux positifs, et avec prudence, nous pouvons également utiliser les paires honnêtes du deuxième programme. C'est essentiellement ce que fait ce programme. Comme le programme précédent, il trouve lui aussi la plupart de ses résultats très tôt --- il atteint 200 000 000 de faux positifs en quelques secondes --- puis ralentit considérablement.
Compilez avec
g++ flspos.cpp -oflspos -std=c++11 -msse2 -mfpmath=sse -O3
. Pour vérifier les résultats, ajoutez-DVERIFY
(ce sera notamment plus lent.)Courez avec
flspos
. Tout argument de ligne de commande pour le mode détaillé.la source
Python, 27 797 402
Juste pour mettre la barre un peu plus haut ...
Il est facile de vérifier que pour tous les 67 108 864 <= x <= 94 906 265 = étage (sqrt (2 53 )), les paires (x, 0) et (x, 1) sont des faux positifs.
Pourquoi ça marche : 67.108.864 = 2 26 . Par conséquent, tous les nombres x dans la plage ci-dessus sont de la forme 2 26 + x ' pour quelque 0 <= x' <2 26 . Pour tout e positif , (x + e) 2 = x 2 + 2xe + e 2 = x 2 + 2 27 e + 2x'e + e 2 . Si nous voulons avoir
(x + e) 2 = x 2 + 1 nous avons besoin d'au moins 2 27 e <= 1 , c'est-à-dire e <= 2 -27 Cependant, comme la mantisse des nombres à virgule flottante double précision a une largeur de 52 bits, le plus petit e tel que x + e> x est e = 2 26 - 52 = 2 -26 . En d'autres termes, le plus petit nombre représentable supérieur à x est x + 2 -26 tandis que le résultat de sqrt (x 2 + 1) est au plus x + 2 -27 . Étant donné que le mode d'arrondi IEEE-754 par défaut est arrondi au plus proche; égal à égal, il arrondira toujours à x et jamais à x + 2 -26 (où le bris d'égalité n'est vraiment pertinent que pour x = 67,108,864, le cas échéant. Tout nombre plus grand arrondira à x indépendamment).
C ++, 75 000 000+
Rappelons que 3 2 + 4 2 = 5 2 . Cela signifie que le point (4, 3) se trouve sur le cercle de rayon 5 centré autour de l'origine. En fait, pour tous les entiers n , (4n, 3n) se trouve sur un tel cercle de rayon 5n . Pour n assez grand (à savoir tel que 5n> = 2 26 ), nous connaissons déjà un faux positif pour tous les points de ce cercle: (5n, 1) . Génial! C'est encore 27 797 402/5 paires de faux positifs gratuites! Mais pourquoi s'arrêter ici? (3, 4, 5) n'est pas le seul de ces triplets.
Ce programme recherche tous les triplets entiers positifs (a, b, c) tels que a 2 + b 2 = c 2 , et compte les faux positifs de cette façon. Il atteint assez rapidement 70 000 000 de faux positifs, puis ralentit considérablement à mesure que les chiffres augmentent.
Compilez avec
g++ flspos.cpp -oflspos -std=c++11 -msse2 -mfpmath=sse -O3
. Pour vérifier les résultats, ajoutez-DVERIFY
(ce sera notamment plus lent.)Courez avec
flspos
. Tout argument de ligne de commande pour le mode détaillé.la source
2**53
limite avait été choisie pour exclure cela, mais je suppose que non.C ++ 11 - 100 993 667
EDIT: Nouveau programme.
L'ancien utilisait trop de mémoire. Celui-ci réduit de moitié l'utilisation de la mémoire en utilisant un tableau vectoriel géant au lieu d'une table de hachage. En outre, il supprime la rupture de fil aléatoire.
Exécutez avec un
-P
argument pour imprimer les points au lieu de leur nombre.Pour moi, cela prend moins de 2 minutes en mode comptage et environ 5 minutes avec une impression dirigée vers un fichier (~ 4 Go), donc cela n'a pas tout à fait limité les E / S.
Mon programme d'origine était soigné, mais j'en ai abandonné la majeure partie car il ne pouvait produire que de l'ordre de 10 ^ 5 résultats. Il a cherché des paramétrisations de la forme (x ^ 2 + Ax + B, x ^ 2 + Cx + D), (x ^ 2 + ax + b, x ^ 2 + cx + d) telles que pour tout x, (x ^ 2 + Ax + B) ^ 2 + (x ^ 2 + Cx + D) ^ 2 = (x ^ 2 + ax + b) ^ 2 + (x ^ 2 + cx + d) ^ 2 + 1. Lorsqu'il a trouvé un tel ensemble de paramètres {a, b, c, d, A, B, C, D}, il a procédé à la vérification de toutes les valeurs x sous le maximum. En regardant ma sortie de débogage de ce programme, j'ai remarqué une certaine paramétrisation de la paramétrisation de la paramétrisation qui m'a permis de produire beaucoup de nombres facilement. J'ai choisi de ne pas imprimer les numéros d'Ell car j'en avais plein. Espérons que maintenant, personne n'imprimera nos deux séries de chiffres et ne prétendra être le gagnant :)
la source
g++ (GCC) 4.8.1
. D'accord, j'ai supprimé les bits de fil, mais il ne reconnaît toujours passtricmp
pour une raison quelconque.Analyse du cercle Java, Bresenham-esque
Heuristiquement, je m'attends à obtenir plus de collisions en commençant à l'extrémité la plus large de l'anneau. Je m'attendais à obtenir une certaine amélioration en effectuant un balayage pour chaque collision, en enregistrant des valeurs
surplus
entre0
etr2max - r2
inclusives, mais dans mes tests, cela s'est avéré plus lent que cette version. De même, tente d'utiliser un seulint[]
tampon plutôt que de créer de nombreux tableaux et listes à deux éléments. L'optimisation des performances est une étrange bête en effet.Exécutez avec un argument de ligne de commande pour la sortie des paires et sans pour les comptes simples.
la source
Java - 27 817 255
La plupart d'entre eux sont les mêmes que ce qu'Ell montre , et les autres sont basés sur
(j,0) (k,l)
. Pour chacunj
, je recule quelques carrés et vérifie si le reste donne un faux positif. Cela prend essentiellement tout le temps avec seulement un gain de 25k (environ 0,1%) sur juste(j,0) (j,1)
, mais un gain est un gain.Cela se terminera en moins de dix minutes sur ma machine, mais je ne sais pas ce que vous avez. Pour des raisons, s'il ne se termine pas avant la fin du temps imparti, il aura un score considérablement pire. Dans ce cas, vous pouvez modifier le diviseur de la ligne 8 pour qu'il se termine dans le temps (cela détermine simplement la distance parcourue pour chacun
j
). Pour certains diviseurs divers, les scores sont les suivants:Pour activer la sortie pour chaque match (et, mon Dieu, c'est lent si vous le faites), décommentez simplement les lignes 10 et 19.
Pour référence, les 20 premières sorties qu'il donne (pour diviseur = 7, hors
(j,0)(j,1)
types) sont:la source
Julia, 530 faux positifs
Voici une recherche de force brute très naïve, que vous pouvez voir comme une implémentation de référence.
Vous pouvez imprimer les paires (et leurs amplitudes exactes au carré) en décommentant la
@printf
ligne.Fondamentalement, cela démarre la recherche
x = y = 6e7
de la première paire de coordonnées et balaye environ 1% du chemin jusqu'à l'axe des x avant de décrémenter x. Ensuite, pour chacune de ces paires de coordonnées, il vérifie l'arc entier de même amplitude (arrondi vers le haut et vers le bas) pour une collision.Le code suppose qu'il est exécuté sur un système 64 bits, de sorte que les types entier et virgule flottante par défaut sont ceux de 64 bits (sinon, vous pouvez les créer avec
int64()
et lesfloat64()
constructeurs).Cela donne un maigre 530 résultats.
la source