2011-01-10 59 views
55

我要尋找一個實現或獲得的ñ的因式分解在任一蟒蛇,僞代碼或其他任何東西也可讀清晰算法。有幾個要求/事實:快速因式分解模塊

  • ñ是1和2之間〜20個位數
  • 沒有預先計算的查找表,記憶化是好的,但。
  • 不需要是數學上證明(如果需要時,如能依靠哥德巴赫猜想)
  • 不需要精確,允許是概率性/確定性,如果需要

我需要一個快速因式分解算法,不僅適用於其自身,還適用於許多其他算法,如計算歐拉phi(n)

我已經嘗試過維基百科等其他算法,但是我無法理解它們(ECM),或者我無法從算法(Pollard-Brent)創建工作實現。

我對Pollard-Brent算法很感興趣,所以對它的任何更多信息/實現都會非常好。

謝謝!

編輯

附近有一座小搞亂後,我已經創建了一個非常快的黃金/分解模塊。它結合了一個優化的試用分割算法,Pollard-Brent算法,miller-rabin素數測試和我在互聯網上找到的速度最快的primesieve。 gcd是一個普通的Euclid的GCD實現(二進制Euclid的GCD是,比常規的要慢很多,比較慢)。

賞金

哦,快樂,可以獲得賞金!但我怎樣才能贏得它?

  • 在我的模塊中查找優化或錯誤。
  • 提供替代/更好的算法/實現。

最完整/有建設性的答案獲得賞金。

最後模塊本身:

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 - 那會是什麼',而檢查*檢查<= num`是有。 – Amber 2011-01-10 04:30:11

+0

你可能會發現這個線程很有用:http://stackoverflow.com/questions/1024640/calculating-phik-for-1kn/1134851#1134851 – RBarryYoung 2011-01-18 05:51:20

+0

爲什麼在標準庫中沒有這樣的東西?當我搜索時,我發現的只有一百萬個歐拉項目解決方案提案,而其他人則指出它們存在缺陷。這不是圖書館和錯誤報告的目的嗎? – endolith 2013-09-30 23:15:23

回答

4

即使在目前的情況下,還有幾個地方需要注意。

  1. 不要做checker*checker每一個循環中使用s=ceil(sqrt(num))checher < s
  2. checher應加2各一次,忽略所有偶數除了2
  3. 使用divmod而不是%//
10

沒有必要使用primesbelow來計算smallprimes,對此使用smallprimeset

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

把你的primefactors成兩個函數處理smallprimes等爲pollard_brent,這可以幾次迭代保存爲smallprimes的一切權力會從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 

通過考慮Pomerance,塞爾弗裏奇和瓦格斯塔夫和Jaeschke的驗證結果,可以減少isprime它採用米勒 - 拉賓檢驗的重複。從Wiki

  • 如果n < 1,373,653,它足以測試a = 2和3;
  • if n < 9,080,191,它足以測試a = 31和73;
  • if n < 4,759,123,141,它足以測試a = 2,7和61;
  • 如果n < 2,152,302,898,747,它足以測試a = 2,3,5,7和11;
  • 如果n < 3,474,749,660,383,測試a = 2,3,5,7,11和13就足夠了;
  • 如果n < 341,550,071,728,321,它足以測試= 2,3,5,7,11,13,和17

編輯1:的if-else更正返回呼叫追加bigfactors到因素在primefactors

2

有一個python庫有一些素性測試(包括不正確的測試)。它被稱爲pyprimes。認爲值得一提的是後人的目的。我不認爲它包含你提到的算法。

0

我只是遇到了這個代碼中的錯誤,因爲編號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 

這段代碼:

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 

應該被改寫成

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

這將可能對現實的輸入執行快呢。平方根很慢 - 基本上相當於許多乘法運算 - ,smallprimes只有幾十個成員,這樣我們就可以從緊密的內部循環中移除n ** .5的計算,這在將因子分解爲2**1427等數字時非常有用。沒有理由計算sqrt(2**1427),sqrt(2**1426),sqrt(2**1425)等,當我們所關心的是「檢查器的[平方]是否超過n」。當與大數字呈現

重寫的代碼不會拋出異常,大致兩倍的速度根據timeit(上採樣輸入22**718 * 31)。

另請注意,isprime(2)返回的結果是錯誤的,但只要我們不依賴它,這沒關係。恕我直言,你應該重寫功能的介紹爲

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

你可以因式分解達到一個極限,然後使用布倫特得到更高的因素

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

如果你不想推倒重來,使用圖書館sympy

pip install sympy 

使用功能sympy.ntheory.factorint

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

那麼您可以一些非常大的數字:

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