2015-03-30 72 views
3

我想讓這段代碼更快。它返回一個長整數的所有因素(素數)。如果longNumber是特定的,那麼顯然需要執行幾分鐘。改進快速分解代碼?

int div = 2; 
String factors = ""; 

while (longNumer != 1) 
{ 
    if (longNumer % div == 0) 
    { 
     longNumer /= div; 
     factors += div + " "; 
    } 
    else { div++; } 
} 

//longNumber = 10, gives: 2 5. 
//longNumber = 150, gives: 3 5 7. 
//longNumber = 10523, gives: 17 619. 

它需要太長的數字,如「7544222046562688368」,並且它不好,你會建議什麼?

+4

對素數使用記憶,代碼將提升性能。 – 2015-03-30 20:18:54

+1

如果你的算法真的把'3,5,7'作爲'150'的因素,我建議你在浪費時間讓它更快之前就做好了。在性能方面, – 2015-04-02 21:13:41

回答

2

您可以使用以下步驟來代替 -

找到所有質數< =開方(longNumber)。並將它們保存在一個數組中 - primes
2.現在逐漸使用數組的元素 - primes作爲除數來查找該因子。

+0

沒有循環遍歷數組是非常糟糕的? – OPK 2015-03-30 20:23:10

+1

@HashMap不是。這取決於數組中有10個或1個元素是什麼決定了好壞表現。 – 2015-03-30 20:26:05

+2

我會說,你應該尋找素數,當且僅當他們沒有存儲在數組中。您可以使用二進制搜索快速查找(假設您將素數以排序的方式存儲在數組中)。 – 2015-03-30 20:27:22

4

對於大的號碼,你可以使用Sieve of Eratosthenes algorithm先找到素數最多的sqrt(N),然後你必須檢查是否這些素數因素

+0

這對64位數字沒什麼幫助,'2^32'下面還有很多素數可以檢查,而生成這些數字會增加收益遞減。 – IVlad 2015-03-30 20:57:15

+0

使用Eratosthenes的Sieve獲取第一個sqrt(n)素數需要大約sqrt(n)loglog n操作。審判分部需要sqrt(n)操作。你通過篩選獲得什麼? – 2015-03-31 01:36:13

+0

@DouglasZare是的,一個篩子比審判分部需要更多的操作。但是有關的行動是非常不同的。對於一個CPU來說,除了加法之外,分區大約慢10倍,log(log(n))在整個值的範圍內是有效的。 (並且所述常數顯着小於10.) 您選擇的語言可能會增加足夠的開銷來掩蓋此性能差異。但是用一種高效的語言來說,一個執行得很好的篩選可能會超過任何可以放在很長時間裏的任何事物的審判分割。 – btilly 2015-03-31 04:02:49

2

暗示埃拉托色尼的篩的答案將不爲你描述的數字做很多事情。對於64位數sqrt(2^64) = 2^32,這仍然很多。

對於那些,最好的賭注Pollard's Rho algorithm或更復雜的整數分解方法列出here

代數組因數分解算法,其中有波拉德是P& - 1種算法,Williams的P + 1算法,和倫斯特拉橢圓曲線分解

Fermat的分解方法

歐拉因式分解法

特殊數域篩

1

一個好辦法因素的64位整數,這是既簡單編程,並在實踐中合理有效,綜合審判庭與波拉德的RHO算法。這裏是僞代碼:

function factors(n) 
    wheel := [1,2,2,4,2,4,2,4,6,2,6] 
    w, f, fs := 0, 2, [] 
    while f*f <= n and f < 10000 
     while n % f == 0 
      fs, n := f :: fs, n/f 
     f, w := f + wheel[w], w+1 
     if w = 11 then w = 3 
    if n == 1 return fs 
    h, t, g, c := 1, 1, 1, 1 
    while not isPrime(n) 
     repeat 
      h := (h*h+c) % n # the hare runs 
      h := (h*h+c) % n # twice as fast 
      t := (t*t+c) % n # as the tortoise 
      g := gcd(t-h, n) 
     while g == 1 
     if isPrime(g) 
      while n % g == 0 
       fs, n := g :: fs, n/g 
     h, t, g, c := 1, 1, 1, c+1 
    return n :: fs 

這使用2,3,5輪審判分爲10000然後簡單的實施rho算法;它應該在幾毫秒內將您的樣本數量設爲7544222046562688368 = 2 * 2 * 2 * 2 * 7 * 7 * 14618561 * 658254407。改進是可能的,但這應該足以讓你開始。

2

在實施其中一種分解算法比試劃更快之前,一個容易糾正的錯誤是避免在最後一塊的sqrt之後進行試劃分。

while (longNumber != 1) { 
    if (longNumber % div == 0) { 
     longNumber /= div; 
     factors += div + " "; 
    } 
    else { 
     if (div*div>longNumber) { 
      if (longNumber > 1) 
       factors += longNumber + " "; 
      break; // leave the while loop. 
     } 
     div++; 
    } 
} 

設兩個最大的素因子是P1和P2。在你的版本中,你可以做一些關於c P1的操作。在修改後的版本中,你可以做c Max(sqrt(P1),P2)。在7544222046562688368上,改進應該是45的因子。

另一個改進是更改div ++行。你不需要用大於2的偶數進行試劃,也不需要用大於3的3整除。避免這些因素可以使計算速度提高2倍以上,並且可以通過避免測試其他小素數的倍數來做得更好。但是,你不想花時間用小素數對div進行審判。相反,你要跟蹤當前的和允許的餘數mod 2 * 3 * 5 * 7。這被稱爲使用小素數的輪子。

一些其他的答案已經討論過使用篩子來找到所有小素數,然後僅通過這些試算來進行試用。如果你只考慮一個數字,這不會起作用,因爲篩選素數需要很長的時間。生成sqrt(n)的素數列表大約需要c sqrt(n)loglog n操作,而對sqrt(n)進行的所有事情的劃分都需要大約c sqrt(n)的操作。如果您需要計算許多大數,則執行一次篩選並存儲結果可能會有所幫助。

+0

我不認爲'div * div> longNumber'是真的,因爲在那種情況下,你已經找到了所有的素因素,'longNumber'已經減少到1. – 2015-03-31 03:27:16

+0

@匿名:div * div當您只剩下一個素數因子時,可能會比longNumber大。假設longNumber從7開始。你測試div = 2,然後div = 3和3 * 3> 7,然後你知道7是素數,所以你不需要測試任何更高的div值。如果longNumber從14開始,你會找到2的因子,將它減少到7,再次測試2,然後在3你可以停止。 – 2015-03-31 03:37:22

+0

啊,是的,聰明。 – 2015-03-31 03:39:29