2011-01-10 24 views
55

szukam dla realizacji lub jasnego algorytmu dla uzyskania czynniki pierwsze N zarówno w Pythonie, Pseudokod lub cokolwiek innego dobrze czytelny. Istnieje kilka żądaniami/fakty:Szybka główny moduł faktoryzacji

  • N jest między 1 i ~ 20 cyfr
  • No sprzed oblicza tabeli odnośników, memoization jest jednak w porządku.
  • nie muszą być matematycznie udowodnione (np mogłaby polegać na domysłach Goldbach razie potrzeby)
  • nie muszą być precyzyjne, może mieć charakter probabilistyczny/deterministyczny razie potrzeby

muszę szybki doskonałą faktoryzacji Algorytm, nie tylko sam w sobie, ale do wykorzystania w wielu innych algorytmach, takich jak obliczanie Euler phi (n).

Próbowałem innych algorytmów z Wikipedii i takich, ale albo nie mogłem ich zrozumieć (ECM) lub nie mogłem stworzyć działającej implementacji z algorytmu (Pollard-Brent).

Jestem naprawdę zainteresowany w algorytmie Pollard-Brent, więc nic więcej informacji/implementacje na to byłoby naprawdę miłe.

Dzięki!

EDIT

Po aprowizacji trochę Stworzyłem całkiem szybki moduł prime/faktoryzacji. Łączy w sobie zoptymalizowany algorytm podziału prób, algorytm Pollard-Brent, test pierwowzoru millera-rabina i najszybszą primeieve, jaką znalazłem w Internecie. gcd jest regularną implementacją GCD Euclida (binarne GCD Euclida to znacznie wolniej niż zwykłe).

Bounty

O, radość, można zdobyć nagrodę! Ale jak mogę to wygrać?

  • Znajdź optymalizację lub błąd w moim module.
  • Zapewnij alternatywne/lepsze algorytmy/implementacje.

Odpowiedź, która jest najbardziej kompletna/konstruktywna, otrzymuje nagrodę.

I wreszcie moduł sam:

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) 
+1

@wheaties - że byłoby to, co podczas sprawdzania '* kontroler <= num' jest tam. – Amber

+0

Ten wątek może być przydatny: http://stackoverflow.com/questions/1024640/calculating-phik-for-1kn/1134851#1134851 – RBarryYoung

+0

Dlaczego takie rzeczy nie są dostępne w standardowej bibliotece? Kiedy przeszukuję, wszystko, co znajduję, to milion propozycji rozwiązania Projektu Euler i inne osoby wskazujące na braki w nich. Czy nie do tego służą biblioteki i raporty o błędach? – endolith

Odpowiedz

12
+1

Tak, już to znalazłem. Zwraca losowy współczynnik N, który przekazujesz. Nie jestem pewien, jak zrobić z tego algorytm * prime * factorization. – orlp

+3

Cóż, możesz rekursywnie wywoływać to na zwracanych czynnikach, aż zwróci współczynnik "1". – Amber

+2

Po tym, jak trochę z nim zepsułeś, czy nie zraniłoby się włożenie czeku na młynarza-rabina przed wezwaniem Brenta? Bo jeśli zadzwonię do Brenta na 11-cyfrowym numerze pierwszym, to jest zajęty przez 1,3 sekundy. – orlp

4

Nawet na bieżącym, istnieje kilka miejsc do zauważenia.

  1. Nie rób checker*checker każdą pętlę, należy s=ceil(sqrt(num)) i checher < s
  2. Checher powinien plus 2 za każdym razem, nawet ignorować wszystkie numery z wyjątkiem 2
  3. użytkowania divmod zamiast % i //
+0

Muszę wykonać checker * checker, ponieważ num stale się zmniejsza. Wprowadzę parzyste liczby pomijając. Divmod znacznie zmniejsza funkcję (obliczy // w każdej pętli, zamiast tylko wtedy, gdy kontroler dzieli n). – orlp

+0

@night, musisz po prostu ponownie wyliczyć 's' gdy zmienisz' num', a następnie –

+0

Prawda, wyobraziłem sobie, że podczas ingerowania :) Wygląda na to, że szybciej przeliczę sqrt, a następnie checker * checker. – orlp

10

Nie ma potrzeby obliczania wartości smallprimes przy użyciu primesbelow, należy użyć do tego celu smallprimeset.

smallprimes = (2,) + tuple(n for n in xrange(3,1000,2) if n in smallprimeset)

Podzielić się swoimi primefactors na dwie funkcje do obsługi smallprimes i drugi dla pollard_brent, to może zaoszczędzić kilka iteracji jak wszystkie moce smallprimes zostanie podzielony od 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 

Dzięki uwzględnieniu zweryfikowanych wyników Pomerance, Selfridge i Wagstaff i Jaeschke, można zmniejszyć powtórzeń w isprime który wykorzystuje Test Millera-Rabina. Od Wiki.

  • jeśli n < 1,373,653, wystarczy przetestować a = 2 i 3;
  • jeśli n < 9,080,191, wystarczy przetestować a = 31 i 73;
  • jeśli n < 4,759,123,141, wystarczy przetestować a = 2, 7 i 61;
  • jeśli n < 2 152 302 898,747, wystarczy przetestować a = 2, 3, 5, 7 i 11;
  • jeśli n < 3,474,749,660,383, wystarczy przetestować a = 2, 3, 5, 7, 11 i 13;
  • jeśli n < 341,550,071,728,321, wystarczy do testowania a = 2, 3, 5, 7, 11, 13 i 17.

Edycja 1: Poprawione połączenia powrót if-else do dołączania do bigfactors czynniki w primefactors.

+0

Ciesz się +100 (jesteś jedyny, który odpowiedział od czasu nagrody). Twoje "bigfactors" jest jednak strasznie nieefektywne, ponieważ 'factors.extend (bigfactors (factor)) powraca do bigfactors, który jest po prostu błędny (co jeśli pollard-brent znajdzie czynnik 234892, nie chcesz tego zrównywać z pollard-brent ponownie). Jeśli zmienisz wartość "factors.extend (bigfactors (factor))" na "factors.extend (primefactors (factor, sort)), to jest w porządku. – orlp

+0

Jeden primefactors nazywa bigfactors, a następnie jasne, że nie ma mocy małej liczby pierwotnej w następnych czynnikach uzyskanych przez pollard-brent. – Rozuur

+0

Gdyby to było nieskuteczne, nie odpowiedziałbym na to. Po wywołaniu od primefactors do bigfactors nie będzie żadnego czynnika n, który jest lessthan 1000 stąd pollard-brent nie może zwrócić liczby, której współczynniki będą mniejsze niż 1000. Jeśli nie jest to jasne, odpowiedz tak, że zmienię moją odpowiedź z większą liczbą wyjaśnień – Rozuur

2

Istnieje biblioteka Pythona z kolekcją testów pierwszości (w tym niepoprawne dla czego nie robić). Nazywa się pyprimes. Pomyślałem, że warto wspomnieć o celu potomności. Nie sądzę, żeby zawierał wspomniane algorytmy.

0

Po prostu napotkałem błąd w tym kodzie podczas obliczania liczby 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 

Ten fragment kodu:

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 

powinny być zapisane w

for checker in smallprimes: 
    while n % checker == 0: 
     factors.append(checker) 
     n //= checker 
    if checker > n: break 

który prawdopodobnie będzie działać szybciej na realistycznych wejść w każdym razie.Pierwiastek kwadratowy jest powolny - w zasadzie odpowiednik wielu multiplikacji -, smallprimes ma tylko kilkudziesięciu członków, i w ten sposób usuwamy obliczenia n ** .5 z ciasnej wewnętrznej pętli, co z pewnością jest pomocne przy obliczaniu liczb takich jak 2**1427. Nie ma po prostu powodu, aby obliczyć sqrt(2**1427),, sqrt(2**1425) itp., Gdy wszystko, na czym nam zależy, to "czy [kwadrat] kontrolera przekracza n".

kod przepisanej wersji nie generują wyjątki, gdy przedstawiane z wielkich liczb, i jest w przybliżeniu dwa razy szybciej według timeit (na wejściach przykładowych 2 i 2**718 * 31).

Należy również zauważyć, że isprime(2) zwraca nieprawidłowy wynik, ale jest to w porządku, o ile nie polegamy na nim. IMHO należy przepisać intro tej funkcji jako

if n <= 3: 
    return n >= 2 
... 
0

Można na czynniki do limitu następnie użyć brent, aby uzyskać wyższe współczynniki

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

Jeśli nie chcesz wyważać otwartych drzwi, korzystanie biblioteka sympy

pip install sympy 

Użyj funkcji sympy.ntheory.factorint

>>> from sympy.ntheory import factorint 
>>> factorint(10**20+1) 
{73: 1, 5964848081: 1, 1676321: 1, 137: 1} 

Można czynnik kilka bardzo dużych liczb:

>>> factorint(10**100+1) 
{401: 1, 5964848081: 1, 1676321: 1, 1601: 1, 1201: 1, 137: 1, 73: 1, 129694419029057750551385771184564274499075700947656757821537291527196801: 1}