web-dev-qa-db-fra.com

Module de factorisation à prime rapide

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:

  • [~ # ~] n [~ # ~] est compris entre 1 et ~ 20 chiffres
  • Aucune table de recherche pré-calculée, la mémorisation est très bien cependant.
  • Ne doit pas être prouvé mathématiquement (par exemple, pourrait s'appuyer sur la conjecture de Goldbach si nécessaire)
  • N'a pas besoin d'être précis, peut être probabiliste/déterministe si nécessaire

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).

Prime

Oh joie, une prime peut être acquise! Mais comment puis-je le gagner?

  • Trouvez une optimisation ou un bug dans mon module.
  • Fournir des algorithmes/implémentations alternatifs/meilleurs.

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)
67
orlp
13
Amber

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}
45
Colonel Panic

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 .

  • si n <1 373 653, il suffit de tester a = 2 et 3;
  • si n <9 080 191, il suffit de tester a = 31 et 73;
  • si n <4,759,123,141, il suffit de tester a = 2, 7 et 61;
  • si n <2.152.302.898.747, il suffit de tester a = 2, 3, 5, 7 et 11;
  • si n <3,474,749,660,383, il suffit de tester a = 2, 3, 5, 7, 11 et 13;
  • si n <341,550,071,728,321, il suffit de tester a = 2, 3, 5, 7, 11, 13 et 17.

Modifier 1 : appel de retour corrigé de if-else pour ajouter des facteurs importants aux facteurs dans primefactors.

14
Rozuur

Même sur l'actuel, il y a plusieurs taches à remarquer.

  1. Ne faites pas checker*checker À chaque boucle, utilisez s=ceil(sqrt(num)) et checher < s
  2. checher devrait plus 2 à chaque fois, ignorer tous les nombres pairs sauf 2
  3. Utilisez divmod au lieu de % Et //
4
Kabie

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.

3
Ehtesh Choudhury

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é.

3
milkypostman

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))
1
Antoni Gual Via

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
...
0
Quuxplusone