Écrivez un programme pour factoriser un nombre semi-premier en un minimum de temps.
À des fins de test, utilisez ceci: 38! +1 (523022617466601111760007224100074291200000001)
Il est égal à: 14029308060317546154181 × 37280713718589679646221
fastest-code
primes
Soham Chowdhury
la source
la source
12259243
cela sera utilisé pour tester la vitesse des programmes, les résultats seront si petits que vous n'obtiendrez aucune différence statistiquement significative.Réponses:
Python (avec PyPy JIT v1.9) ~ 1,9 s
Utilisation d'un tamis quadratique polynomial multiple . J'ai considéré cela comme un défi de code, j'ai donc choisi de ne pas utiliser de bibliothèques externes (autres que la
log
fonction standard , je suppose). Lors du chronométrage, le PyPy JIT doit être utilisé, car il se traduit par des synchronisations 4 à 5 fois plus rapides que celles de cPython .Mise à jour (2013-07-29):
depuis la publication initiale, j'ai apporté plusieurs modifications mineures, mais importantes, qui augmentent la vitesse globale d'un facteur d'environ 2,5 fois.
Mise à jour (2014-08-27):
Étant donné que ce message continue de recevoir de l'attention, j'ai mis à jour la
my_math.py
correction de deux erreurs, pour tous ceux qui pourraient l'utiliser:isqrt
était défectueux, produisant parfois une sortie incorrecte pour des valeurs très proches d'un carré parfait. Cela a été corrigé et les performances ont augmenté en utilisant une bien meilleure graine.is_prime
a été mis à jour. Ma tentative précédente de supprimer un carré parfait de 2 sprps était au mieux timide. J'ai ajouté une vérification 3 sprp - une technique utilisée par Mathmatica - pour s'assurer que la valeur testée est sans carré.Mise à jour (2014-11-24):
Si à la fin du calcul aucune congruence non triviale n'est trouvée, le programme tamise maintenant des polynômes supplémentaires. Cela était auparavant marqué dans le code comme
TODO
.mpqs.py
my_math.py
Exemple d'E / S:
Remarque: ne pas utiliser l'
--verbose
option donnera des délais légèrement meilleurs:Concepts de base
En général, un tamis quadratique est basé sur l'observation suivante: tout composite impair n peut être représenté comme:
Ce n'est pas très difficile à confirmer. Puisque n est impair, la distance entre deux cofacteurs quelconques de n doit être pair 2d , où x est le point médian entre eux. De plus, la même relation vaut pour tout multiple de n
Notez que si de tels x et d peuvent être trouvés, il en résultera immédiatement un facteur (pas nécessairement premier) de n , car x + d et x - d divisent tous deux n par définition. Cette relation peut être encore affaiblie - en conséquence de permettre des congruences triviales potentielles - à la forme suivante:
Donc en général, si nous pouvons trouver deux carrés parfaits qui sont équivalents mod n , alors il est assez probable que nous puissions produire directement un facteur de n à la gcd (x ± d, n) . Semble assez simple, non?
Sauf que ce n'est pas le cas. Si nous avions l'intention de mener une recherche exhaustive sur tous les x possibles , nous aurions besoin de rechercher toute la plage de [ √ n , √ ( 2n ) ], qui est légèrement plus petite que la division d'essai complète, mais nécessite également une
is_square
opération coûteuse à chaque itération jusqu'à confirmer la valeur de d . À moins que l'on sache à l'avance que n a des facteurs très proches de √ n , la division d'essai sera probablement plus rapide.Peut-être pouvons-nous affaiblir encore plus cette relation. Supposons que nous choisissions un x , tel que pour
une factorisation complète de y est facilement connue. Si nous avions suffisamment de telles relations, nous devrions pouvoir construire un d adéquat , si nous choisissons un nombre de y tel que leur produit soit un carré parfait; c'est-à-dire que tous les facteurs premiers sont utilisés un nombre pair de fois. En fait, si nous avons plus de tels y que le nombre total de facteurs premiers uniques qu'ils contiennent, une solution est garantie d'exister; Il devient un système d'équations linéaires. La question devient maintenant, comment choisissons-nous un tel x ? C'est là que le tamisage entre en jeu.
Le tamis
Considérez le polynôme:
Alors pour tout p premier et entier k , ce qui suit est vrai:
Cela signifie qu'après avoir résolu les racines du mod polynomial p - c'est-à-dire que vous avez trouvé un x tel que y (x) ≡ 0 (mod p) , ergo y est divisible par p - alors vous avez trouvé un nombre infini de tels x . De cette façon, vous pouvez passer au crible une plage de x , en identifiant les petits facteurs premiers de y , en espérant trouver certains pour lesquels tous les facteurs premiers sont petits. Ces nombres sont connus sous le nom de k-smooth , où k est le plus grand facteur premier utilisé.
Il y a cependant quelques problèmes avec cette approche. Toutes les valeurs de x ne sont pas adéquates, en fait, il n'y en a que très peu, centrées autour de √ n . Les valeurs plus petites deviendront largement négatives (en raison du terme -n ), et les valeurs plus grandes deviendront trop grandes, de sorte qu'il est peu probable que leur factorisation principale ne soit constituée que de petits nombres premiers. Il y aura un certain nombre de ces x , mais à moins que le composite que vous factorisez soit très petit, il est très peu probable que vous trouviez suffisamment de lissages pour entraîner une factorisation. Et donc, pour un n plus grand , il devient nécessaire de tamiser plusieurs polynômes d'une forme donnée.
Polynômes multiples
Il nous faut donc plus de polynômes pour tamiser? Que dis-tu de ça:
Ça va marcher. Notez que A et B peuvent littéralement être n'importe quelle valeur entière, et les calculs sont toujours valables. Tout ce que nous devons faire est de choisir quelques valeurs aléatoires, de résoudre la racine du polynôme et de tamiser les valeurs proches de zéro. À ce stade, nous pourrions simplement dire que c'est assez bon: si vous jetez suffisamment de pierres dans des directions aléatoires, vous risquez de briser une fenêtre tôt ou tard.
Sauf qu'il y a aussi un problème avec ça. Si la pente du polynôme est grande à l'ordonnée à l'origine, ce qu'elle sera si elle n'est pas relativement plate, il n'y aura que quelques valeurs appropriées à tamiser par polynôme. Cela fonctionnera, mais vous finirez par tamiser beaucoup de polynômes avant d'obtenir ce dont vous avez besoin. Pouvons-nous faire mieux?
On peut faire mieux. Une observation, à la suite de Montgomery est la suivante: si A et B sont choisis de telle sorte qu'il existe un certain C satisfaisant
alors le polynôme entier peut être réécrit
De plus, si A est choisi pour être un carré parfait, le premier terme A peut être négligé pendant le tamisage, ce qui donne des valeurs beaucoup plus petites et une courbe beaucoup plus plate. Pour qu'une telle solution existe, n doit être un résidu quadratique mod √ A , qui peut être connu immédiatement en calculant le symbole de Legendre :
( n | √A ) = 1 . Notez que pour résoudre B , une factorisation complète de √A doit être connue (afin de prendre la racine carrée modulaire √n (mod √A) ), c'est pourquoi √A est généralement choisi pour être premier.
On peut alors montrer que si , alors pour toutes les valeurs de x ∈ [ -M, M ] :
Et maintenant, enfin, nous avons tous les composants nécessaires pour mettre en œuvre notre tamis. Ou le faisons-nous?
Pouvoirs des primes comme facteurs
Notre tamis, comme décrit ci-dessus, a un défaut majeur. Il peut identifier quelles valeurs de x résulteront en un y divisible par p , mais il ne peut pas identifier si oui ou non ce y est divisible par une puissance de p . Afin de déterminer cela, nous aurions besoin d'effectuer une division d'essai sur la valeur à tamiser, jusqu'à ce qu'elle ne soit plus divisible par p . Nous semblions avoir atteint une impasse: tout l'intérêt du tamis était de ne pas avoir à le faire. Il est temps de vérifier le playbook.
Cela semble assez utile. Si la somme de ln de tous les petits facteurs premiers de y est proche de la valeur attendue de ln (y) , alors il est presque certain que y n'a pas d'autres facteurs. De plus, si nous ajustons un peu la valeur attendue, nous pouvons également identifier des valeurs aussi lisses qui ont plusieurs puissances de nombres premiers comme facteurs. De cette façon, nous pouvons utiliser le tamis comme un processus de «présélection» et ne prendre en compte que les valeurs susceptibles d'être lisses.
Cela présente également quelques autres avantages. Notez que les petits nombres premiers contribuent très peu à la somme ln , mais pourtant ils nécessitent le plus de temps de tamisage. Le tamisage de la valeur 3 nécessite plus de temps que 11, 13, 17, 19 et 23 combinés . Au lieu de cela, nous pouvons simplement ignorer les premiers nombres premiers et ajuster le seuil en conséquence, en supposant qu'un certain pourcentage d'entre eux aurait passé.
Un autre résultat est qu'un certain nombre de valeurs seront autorisées à passer, qui sont pour la plupart lisses, mais contiennent un seul grand cofacteur. Nous pourrions simplement supprimer ces valeurs, mais supposons que nous ayons trouvé une autre valeur généralement lisse, avec exactement le même cofacteur. Nous pouvons alors utiliser ces deux valeurs pour construire un y utilisable ; puisque leur produit contiendra ce grand cofacteur au carré, il n'est plus nécessaire de le considérer.
Mettre tous ensemble
La dernière chose que nous devons faire est d'utiliser ces valeurs de y pour construire un x et un d adéquats . Supposons que nous ne considérions que les facteurs non carrés de y , c'est-à-dire les facteurs premiers d'une puissance impaire. Ensuite, chaque y peut être exprimé de la manière suivante:
qui peut s'exprimer sous forme matricielle:
Le problème devient alors de trouver un vecteur v tel que vM = ⦳ (mod 2) , où ⦳ est le vecteur nul. C'est, pour résoudre l'espace null gauche de M . Cela peut être fait de plusieurs manières, la plus simple étant d'effectuer une élimination gaussienne sur M T , en remplaçant l'opération d'addition de ligne par un xor de ligne . Il en résultera un certain nombre de vecteurs de base d'espace nul, dont toute combinaison produira une solution valide.
La construction de x est assez simple. C'est simplement le produit de Ax + B pour chacun des y utilisés. La construction de d est un peu plus compliquée. Si nous devions prendre le produit de tout y , nous finirons avec une valeur de 10s de milliers, sinon de 100s de milliers de chiffres, pour laquelle nous devons trouver la racine carrée. Cette calcination est peu coûteuse. Au lieu de cela, nous pouvons suivre les puissances paires des nombres premiers pendant le processus de tamisage, puis utiliser les opérations et et xor sur les vecteurs de facteurs non carrés pour reconstruire la racine carrée.
Il me semble avoir atteint la limite de 30000 caractères. Ahh bien, je suppose que c'est assez bon.
la source
Eh bien, votre 38! +1 a cassé mon script php, je ne sais pas pourquoi. En fait, tout semi-prime de plus de 16 chiffres rompt mon script.
Cependant, en utilisant 8980935344490257 (86028157 * 104395301), mon script a réussi un temps de 25,963 secondes sur mon ordinateur personnel (2,61 GHz AMD Phenom 9950). Beaucoup plus rapide que mon ordinateur de travail qui était de près de 31 secondes à 2,93 GHz Core 2 Duo.
php - 757 caractères incl. nouvelles lignes
Je serais intéressé de voir ce même algorithme en c ou dans un autre langage compilé.
la source
lcm(2, 3, 5, 7) == 210
, le schéma des nombres éliminés par ces facteurs se répétera tous les 210 nombres, et seulement 48 restent. De cette façon, vous pouvez éliminer 77% de tous les numéros de la division d'essai, au lieu des 50% en ne prenant que des cotes.