Je recherche un implémentation ou algorithme clair pour obtenir la factorisation principale de [~ # ~] n [~ # ~] en python, pseudocode ou toute autre chose bien lisible. Il y a quelques demandes/faits:
J'ai besoin d'un algorithme de factorisation rapide rapide, non seulement pour lui-même, mais pour une utilisation dans de nombreux autres algorithmes comme le calcul de l'Euler phi (n) .
J'ai essayé d'autres algorithmes de Wikipedia et autres, mais soit je ne pouvais pas les comprendre (ECM), soit je ne pouvais pas créer une implémentation fonctionnelle à partir de l'algorithme (Pollard-Brent).
Je suis vraiment intéressé par l'algorithme Pollard-Brent, donc plus d'informations/implémentations sur ce serait vraiment sympa.
Merci!
MODIFIER
Après un peu déconner, j'ai créé un module d'amorçage/factorisation assez rapide. Il combine un algorithme de division d'essai optimisé, l'algorithme Pollard-Brent, un test de primalité miller-rabin et le filtre à tamis le plus rapide que j'ai trouvé sur Internet. gcd est une implémentation GCD standard d'Euclid (le GCD binaire d'Euclid est beaucoup plus lent que le standard).
Oh joie, une prime peut être acquise! Mais comment puis-je le gagner?
La réponse qui est la plus complète/constructive obtient la prime.
Et enfin le module lui-même:
import random
def primesbelow(N):
# http://stackoverflow.com/questions/2068372/fastest-way-to-list-all-primes-below-n-in-python/3035188#3035188
#""" Input N>=6, Returns a list of primes, 2 <= p < N """
correction = N % 6 > 1
N = {0:N, 1:N-1, 2:N+4, 3:N+3, 4:N+2, 5:N+1}[N%6]
sieve = [True] * (N // 3)
sieve[0] = False
for i in range(int(N ** .5) // 3 + 1):
if sieve[i]:
k = (3 * i + 1) | 1
sieve[k*k // 3::2*k] = [False] * ((N//6 - (k*k)//6 - 1)//k + 1)
sieve[(k*k + 4*k - 2*k*(i%2)) // 3::2*k] = [False] * ((N // 6 - (k*k + 4*k - 2*k*(i%2))//6 - 1) // k + 1)
return [2, 3] + [(3 * i + 1) | 1 for i in range(1, N//3 - correction) if sieve[i]]
smallprimeset = set(primesbelow(100000))
_smallprimeset = 100000
def isprime(n, precision=7):
# http://en.wikipedia.org/wiki/Miller-Rabin_primality_test#Algorithm_and_running_time
if n < 1:
raise ValueError("Out of bounds, first argument must be > 0")
Elif n <= 3:
return n >= 2
Elif n % 2 == 0:
return False
Elif n < _smallprimeset:
return n in smallprimeset
d = n - 1
s = 0
while d % 2 == 0:
d //= 2
s += 1
for repeat in range(precision):
a = random.randrange(2, n - 2)
x = pow(a, d, n)
if x == 1 or x == n - 1: continue
for r in range(s - 1):
x = pow(x, 2, n)
if x == 1: return False
if x == n - 1: break
else: return False
return True
# https://comeoncodeon.wordpress.com/2010/09/18/pollard-rho-brent-integer-factorization/
def pollard_brent(n):
if n % 2 == 0: return 2
if n % 3 == 0: return 3
y, c, m = random.randint(1, n-1), random.randint(1, n-1), random.randint(1, n-1)
g, r, q = 1, 1, 1
while g == 1:
x = y
for i in range(r):
y = (pow(y, 2, n) + c) % n
k = 0
while k < r and g==1:
ys = y
for i in range(min(m, r-k)):
y = (pow(y, 2, n) + c) % n
q = q * abs(x-y) % n
g = gcd(q, n)
k += m
r *= 2
if g == n:
while True:
ys = (pow(ys, 2, n) + c) % n
g = gcd(abs(x - ys), n)
if g > 1:
break
return g
smallprimes = primesbelow(1000) # might seem low, but 1000*1000 = 1000000, so this will fully factor every composite < 1000000
def primefactors(n, sort=False):
factors = []
for checker in smallprimes:
while n % checker == 0:
factors.append(checker)
n //= checker
if checker > n: break
if n < 2: return factors
while n > 1:
if isprime(n):
factors.append(n)
break
factor = pollard_brent(n) # trial division did not fully factor, switch to pollard-brent
factors.extend(primefactors(factor)) # recurse to factor the not necessarily prime factor returned by pollard-brent
n //= factor
if sort: factors.sort()
return factors
def factorization(n):
factors = {}
for p1 in primefactors(n):
try:
factors[p1] += 1
except KeyError:
factors[p1] = 1
return factors
totients = {}
def totient(n):
if n == 0: return 1
try: return totients[n]
except KeyError: pass
tot = 1
for p, exp in factorization(n).items():
tot *= (p - 1) * p ** (exp - 1)
totients[n] = tot
return tot
def gcd(a, b):
if a == b: return a
while b > 0: a, b = b, a % b
return a
def lcm(a, b):
return abs((a // gcd(a, b)) * b)
Pollard-Brent est implémenté en Python:
https://comeoncodeon.wordpress.com/2010/09/18/pollard-rho-brent-integer-factorization/
Si vous ne voulez pas réinventer la roue, utilisez la bibliothèque sympy
pip install sympy
Utilisez la fonction sympy.ntheory.factorint
>>> from sympy.ntheory import factorint
>>> factorint(10**20+1)
{73: 1, 5964848081: 1, 1676321: 1, 137: 1}
Vous pouvez factoriser de très grands nombres:
>>> factorint(10**100+1)
{401: 1, 5964848081: 1, 1676321: 1, 1601: 1, 1201: 1, 137: 1, 73: 1, 129694419029057750551385771184564274499075700947656757821537291527196801: 1}
Il n'est pas nécessaire de calculer smallprimes
en utilisant primesbelow
, utilisez smallprimeset
pour cela.
smallprimes = (2,) + Tuple(n for n in xrange(3,1000,2) if n in smallprimeset)
Divisez votre primefactors
en deux fonctions pour gérer smallprimes
et autre pour pollard_brent
, cela peut économiser quelques itérations car toutes les puissances des petits nombres seront divisées par n.
def primefactors(n, sort=False):
factors = []
limit = int(n ** .5) + 1
for checker in smallprimes:
print smallprimes[-1]
if checker > limit: break
while n % checker == 0:
factors.append(checker)
n //= checker
if n < 2: return factors
else :
factors.extend(bigfactors(n,sort))
return factors
def bigfactors(n, sort = False):
factors = []
while n > 1:
if isprime(n):
factors.append(n)
break
factor = pollard_brent(n)
factors.extend(bigfactors(factor,sort)) # recurse to factor the not necessarily prime factor returned by pollard-brent
n //= factor
if sort: factors.sort()
return factors
En considérant les résultats vérifiés de Pomerance, Selfridge et Wagstaff et Jaeschke, vous pouvez réduire les répétitions dans isprime
qui utilise le test de primalité de Miller-Rabin. De Wiki .
Modifier 1 : appel de retour corrigé de if-else
pour ajouter des facteurs importants aux facteurs dans primefactors
.
Même sur l'actuel, il y a plusieurs taches à remarquer.
checker*checker
À chaque boucle, utilisez s=ceil(sqrt(num))
et checher < s
divmod
au lieu de %
Et //
Il y a une bibliothèque python avec une collection de tests de primalité (y compris des tests incorrects pour ce qu'il ne faut pas faire). Cela s'appelle pyprimes . ne pensez pas qu'il inclut les algorithmes que vous avez mentionnés.
Vous devriez probablement faire une détection principale que vous pourriez regarder ici, Algorithme rapide pour trouver des nombres premiers?
Vous devriez lire tout ce blog cependant, il énumère quelques algorithmes pour tester la primalité.
Vous pouvez factoriser jusqu'à une limite, puis utiliser le brent pour obtenir des facteurs plus élevés
from fractions import gcd
from random import randint
def brent(N):
if N%2==0: return 2
y,c,m = randint(1, N-1),randint(1, N-1),randint(1, N-1)
g,r,q = 1,1,1
while g==1:
x = y
for i in range(r):
y = ((y*y)%N+c)%N
k = 0
while (k<r and g==1):
ys = y
for i in range(min(m,r-k)):
y = ((y*y)%N+c)%N
q = q*(abs(x-y))%N
g = gcd(q,N)
k = k + m
r = r*2
if g==N:
while True:
ys = ((ys*ys)%N+c)%N
g = gcd(abs(x-ys),N)
if g>1: break
return g
def factorize(n1):
if n1==0: return []
if n1==1: return [1]
n=n1
b=[]
p=0
mx=1000000
while n % 2 ==0 : b.append(2);n//=2
while n % 3 ==0 : b.append(3);n//=3
i=5
inc=2
while i <=mx:
while n % i ==0 : b.append(i); n//=i
i+=inc
inc=6-inc
while n>mx:
p1=n
while p1!=p:
p=p1
p1=brent(p)
b.append(p1);n//=p1
if n!=1:b.append(n)
return sorted(b)
from functools import reduce
#n= 2**1427 * 31 #
n= 67898771 * 492574361 * 10000223 *305175781* 722222227*880949 *908909
li=factorize(n)
print (li)
print (n - reduce(lambda x,y :x*y ,li))
Je viens de rencontrer un bogue dans ce code lors de la factorisation du nombre 2**1427 * 31
.
File "buckets.py", line 48, in prettyprime
factors = primefactors.primefactors(n, sort=True)
File "/private/tmp/primefactors.py", line 83, in primefactors
limit = int(n ** .5) + 1
OverflowError: long int too large to convert to float
Cet extrait de code:
limit = int(n ** .5) + 1
for checker in smallprimes:
if checker > limit: break
while n % checker == 0:
factors.append(checker)
n //= checker
limit = int(n ** .5) + 1
if checker > limit: break
devrait être réécrit en
for checker in smallprimes:
while n % checker == 0:
factors.append(checker)
n //= checker
if checker > n: break
qui fonctionnera probablement plus rapidement sur des entrées réalistes de toute façon. La racine carrée est lente - essentiellement l'équivalent de nombreuses multiplications -, smallprimes
n'a que quelques dizaines de membres, et de cette façon, nous supprimons le calcul de n ** .5
De la boucle interne serrée, ce qui est certainement utile lors de la factorisation de nombres comme 2**1427
. Il n'y a tout simplement aucune raison de calculer sqrt(2**1427)
, sqrt(2**1426)
, sqrt(2**1425)
, etc. etc., alors que tout ce qui nous intéresse c'est " dépasse n
".
Le code réécrit ne lève pas d'exceptions lorsqu'il est présenté avec de grands nombres, et est environ deux fois plus rapide selon timeit
(sur les exemples d'entrées 2
Et 2**718 * 31
).
Notez également que isprime(2)
renvoie le mauvais résultat, mais c'est correct tant que nous ne nous appuyons pas sur lui. À mon humble avis, vous devriez réécrire l'intro de cette fonction comme
if n <= 3:
return n >= 2
...