2012-11-23 44 views
3

我不知道我在做什麼錯誤,試圖使用Pollard的rho算法來計算素數因子分解。從Pollard的rho算法實現中得不到合適的輸出

#include<stdio.h> 
#define f(x) x*x-1 

int pollard(int); 
int gcd(int, int); 

int main(void) { 
    int n; 
    scanf("%d",&n); 
    pollard(n); 
    return 0; 
} 

int pollard(int n) { 
    int i=1,x,y,k=2,d; 
    x = rand()%n; 
    y = x; 

    while(1) { 
     i++; 
     x = f(x) % n; 
     d = gcd(y-x, n); 

     if(d!=1 && d!=n) 
      printf("%d\n", d); 

     if(i == k) { 
      y = x; 
      k = 2 * k; 
     } 
    } 
} 
int gcd(int a, int b) { 

    if(b == 0) 
     return a; 
    else 
     return gcd(b, a % b); 
} 
+3

不確定是否導致問題,但#define f(x)看起來很奇怪。嘗試添加((x)*(x)-1)括號以避免宏中的運算符優先級問題。 –

回答

5

一個直接的問題是,as Peter de Rivaz suspected

#define f(x) x*x-1 

因此行

x = f(x)%n; 

變成

x = x*x-1%n; 

%的優先級比的-較高,因此,表達含蓄parenthesised爲

x = (x*x) - (1%n); 

這相當於x = x*x - 1;(我假設n > 1,反正它的x = x*x - constant;),如果你開始值爲x >= 2,則在發現某個因子之前您已經發生溢出:

2→2 * 2-1 = 3→3 * 3-1 = 8→8 * 8-1 = 63 - > 3968 - > 15745023 - >如果int爲32位,則溢出

雖然,這並不能立即使gcd(y-x,n)成爲不可能的因素。它只是使得在理論上你會找到一個因子的階段,溢出破壞了數學上可能存在的共同因素 - 比溢出引入的公共因素更可能。

有符號整數的溢出是未定義的行爲,所以不能保證程序是如何工作的,但通常它的行爲是一致的,因此f的迭代仍然會產生一個定義明確的序列,原則上該算法可以工作。

另一個問題是,y-x將經常是負面的,然後計算的gcd也可以是負數 - 通常是-1。在這種情況下,您可以打印-1

然後,它是一種不太罕見的發生,從起始值迭代f因爲循環模兩個素數因子(對於n兩個不同的質數的積的例子)具有相等的未檢測的一個共同因素長度並且同時輸入。你不會嘗試檢測這種情況;每當gcd(|y-x|, n) == n,任何進一步的工作在那個序列是毫無意義的,所以你應該breakd == n出循環。

此外,你永遠不會檢查n是否是首要的,在這種情況下,試圖找到一個因素從一開始就是徒勞的。

此外,固定f(x),使% n適用於f(x)完整的結果後,你有x*x仍然溢出比較小x(與標準符號32位int S,對於x >= 46341)的問題,所以保較大的n可能因溢出而失敗。至少,您應該使用unsigned long long進行計算,以避免n < 2^32溢出。然而,通過試行分工,通常可以更有效地分解如此小的數字。 Pollard的Rho方法和其他高級分解算法適用於較大的數字,試驗分區不再有效或甚至不可行。

+1

+1:看起來我錯過了很多問題,很好的答案! (順便說一下,我完全被stackoverflow.com/questions/13461239難倒了,看起來像你可能會感興趣的那種問題) –

0

據我所看到的,波拉德的Rho通常使用f(x)(x*x+1)(例如,在這些lecture notes)。

,因爲它似乎經常陷入一個循環您所選擇的x*x-1似乎沒有那麼好:

x=0 
f(x)=-1 
f(f(x))=0 
3

我只是C++的新手,我是Stack Overflow的新手,所以我寫的一些內容看起來很sl,,但這應該讓你朝着正確的方向前進。這裏發佈的程序通常應該找到並返回您在提示中輸入的數字的一個不重要的因素,否則它會道歉,如果它找不到這樣的因素。

我測試了一些半數字的數字,它爲我工作。對於371156167103,在我敲入回車鍵後,它找到607619,沒有任何可檢測的延遲。我沒有用比這更大的數字來檢查它。我使用了無符號long long變量,但如果可能的話,您應該得到並使用提供更大整數類型的庫。

編輯添加,對X和2這種調用Y的單個調用是有意的,並且與算法的工作方式一致。我想在另一個這樣的呼叫中嵌入Y的呼叫,以保持一行,但我決定這樣做,所以它更容易遵循。

#include "stdafx.h" 
#include <stdio.h> 
#include <iostream> 
typedef unsigned long long ULL; 

ULL pollard(ULL numberToFactor); 
ULL gcd(ULL differenceBetweenCongruentFunctions, ULL numberToFactor); 
ULL f(ULL x, ULL numberToFactor); 

int main(void) 
{ 
    ULL factor; 
    ULL n; 
    std::cout<<"Enter the number for which you want a prime factor: "; 
    std::cin>>n; 
    factor = pollard(n); 
    if (factor == 0) std::cout<<"No factor found. Your number may be prime, but it is  not certain.\n\n"; 
    else std::cout<<"One factor is: "<<factor<<"\n\n"; 
} 

ULL pollard(ULL n) 
{ 
    ULL x = 2ULL; 
    ULL y = 2ULL; 
    ULL d = 1ULL; 

    while(d==1||d==n) 
    { 
     x = f(x,n); 
     y = f(y,n); 
     y = f(y,n); 
     if (y>x) 
     { 
      d = gcd(y-x, n); 
     } 
     else 
     { 
      d = gcd(x-y, n); 
     } 
    } 

    return d; 

} 


ULL gcd(ULL a, ULL b) 
{ 
    if (a==b||a==0) 
     return 0; // If x==y or if the absolute value of (x-y) == the number  to be factored, then we have failed to find 
        // a factor. I think this is not proof of  primality, so the process could be repeated with a new function. 
        // For example, by replacing x*x+1 with x*x+2, and  so on. If many such functions fail, primality is likely. 

    ULL currentGCD = 1; 
    while (currentGCD!=0) // This while loop is based on Euclid's algorithm 
    { 
     currentGCD = b % a; 
     b=a; 
     a=currentGCD; 
    } 

    return b; 
} 

ULL f(ULL x, ULL n) 
{ 
    return (x * x + 1) % n; 
} 
+0

我的帖子是爲了回覆avinashse關注的問題,因爲無法獲得Pollard的Rho算法在程序中工作。所以我編輯了原始代碼以使其工作。我希望我正確地張貼我的回覆,但是開始想也許我沒有。如果不是,請道歉。 – WDS

+0

對不起,您對了!我會刪除我的評論。和+1對不起,:-) –

+0

@WDS感謝您的回覆:)我還有一個查詢,如果假設我必須找到n的主要因素。在從pollard(n)函數返回後,我將不得不檢查它是否爲素數,如果它是素數,那麼n = n/factor,再次factor = pollard(n),但如果factor不是素數,請引導我.. – avinashse

1

對不起,很長的延遲迴到這。正如我在第一個回答中所提到的,我是C++的新手,這在我過度使用全局變量,過度使用BigInteger和BigUnsigned,其他類型可能更好,缺少錯誤檢查和其他編程習慣顯示技術人員可能無法展示的內容。這就是說,讓我解釋我做了什麼,然後會發布代碼。

我在第二個答案中這樣做,因爲第一個答案作爲一個非常簡單的演示非常有用,一旦您瞭解它的作用,Pollard的Rho算法將如何實施。它的作用是先取2個變量,稱它們爲x和y,然後將它們的起始值賦值爲2.然後它通過一個函數運行x,通常爲(x^2 + 1)%n,其中n是你的數字想要因素。它每個週期運行兩次相同的函數。然後計算x和y之間的差值,最後找到這個差值和n的最大公約數。如果這個數字是1,那麼你再次運行x和y。

繼續這個過程,直到GCD不是1或直到x和y再次相等。如果發現GCD不是1,那麼該GCD是n的非平凡因子。如果x和y相等,則(x^2 + 1)%n函數失敗。在這種情況下,您應該再次嘗試使用另一個函數,也許(x^2 + 2)%n等等。

這裏是一個例子。以35,我們知道主要因素是5和7.我會穿過波拉德Rho,並告訴你它是如何找到一個不平凡的因素。循環#1:X從2開始。然後使用函數(x^2 + 1)%n,(2^2 + 1)%35,我們對x得到5。 Y也從2開始,在函數運行一次後,它的值也是5.但是y總是經過兩次函數,所以第二次運行是(5^2 + 1)%35或26。 x和y之間的差值是21. 21(差值)和35(n)的GCD值是7.我們已經找到了35的主因子!請注意,任何2個數的GCD,甚至是非常大的指數,都可以通過使用Euclid算法的公式快速找到,這就是我將在此處發佈的程序的功能。

關於GCD函數的主題,我使用了一個我爲該程序下載的庫,一個允許使用BigIntegers和BigUnsigned的庫。該庫還內置了GCD功能,我可以使用它。但是我決定留下手寫的GCD功能來達到教學目的。如果你想提高程序的執行時間,使用庫的GCD函數可能是一個好主意,因爲有比Euclid更快的方法,並且可以編寫庫來使用其中一種更快的方法。

另一方面的說明。 .Net 4.5庫也支持使用BigInteger和BigUnsigned。我決定不在這個程序中使用它,因爲我想用C++而不是C++/CLI編寫整個程序。你可以從.Net庫中獲得更好的性能,或者你可能不會。我不知道,但我想分享這也是一種選擇。

我在這裏跳了一下,現在讓我從廣泛的筆觸開始解釋程序的功能,最後我將解釋如何在計算機上設置它,如果您使用Visual Studio 11(也稱爲Visual Studio 2012)。

該程序分配3個數組用於存儲您給它處理的任何數字的因子。這些陣列的寬度爲1000個元素,可能過多,但它可以確保1000個素數因子或更少的任何數字都適合。

當您在提示中輸入數字時,它會假定數字是合成的,並將其放入compositeFactors數組的第一個元素中。然後它經歷了一些公認效率低下的循環,這些循環使用Miller-Rabin來檢查數字是否合成。注意這個測試可以說一個數字是100%置信度的複合數字,也可以說這個數字是非常高的(但不是100%)置信度。信心可以通過程序中的變量confidenceFactor進行調整。程序將檢查2和confidenceFactor之間的每個值(包括),因此比confidenceFactor自身的值少一個總檢查。

我對confidenceFactor的設置是101,它執行100次檢查。如果它表示一個數字是主數字,那麼它真正合成的可能性是4^100中的1,或者與連續200次正確調用公平硬幣翻轉的可能性相同。簡而言之,如果它說這個數字是素數,它可能是,但可以增加confidenceFactor數來獲得更高的置信度。

這裏可能是一個很好的地方,任何提及的是,雖然Pollard的Rho算法可以非常有效地分解較長的long long類型的數目,但Miller-Rabin測試看看一個數字是否合成會更多或更少沒有BigInteger和BigUnsigned類型就沒用了。 BigInteger庫是一個能夠將大數字可靠地分解到它們的主要因素的要求。

當Miller Rabin說這個因子是複合因子時,它是因子,因子存儲在一個臨時數組中,而複合數組中的原始因子除以相同的因子。當數字被識別爲可能的素數時,它們被移入素數因子數組並輸出到屏幕。這個過程一直持續到沒有複合因素。這些因素傾向於按升序排列,但這是巧合。程序不會按升序列出它們,但只會在找到它們時列出它們。

請注意,我無法找到任何函數(x^2 + c)%n,它將對數字4進行分解,無論我給出了什麼值c。 Pollard Rho似乎對所有完美的方格都非常困難,但是4是我發現的唯一合成編號,它完全不受使用所描述格式函數的影響。因此,我在pollard方法中添加了一個4的n,如果是的話,立即返回2。

所以要設置這個程序,這裏是你應該做的。轉到https://mattmccutchen.net/bigint/並下載bigint-2010.04.30.zip。解壓縮並將所有.hh文件和所有C++源文件放入〜\ Program Files \ Microsoft Visual Studio 11.0 \ VC \ include目錄中,但不包括Sample和C++ TestSuite源文件。然後在Visual Studio中創建一個空的項目。在解決方案資源管理器中,右鍵單擊資源文件文件夾並選擇添加...現有項目。在我剛剛提到的目錄中添加所有C++源文件。然後在解決方案expolorer中,右鍵單擊Source Files文件夾並添加一個新項目,選擇C++文件,命名它,然後將下面的源代碼粘貼到它中,它應該適用於您。

不要恭維太多,但是Stack Overflow的人們對C++的瞭解比我更多,如果他們修改我的代碼以使其更好,那太棒了。但即使不是這樣,代碼也是按原樣運作的,它應該有助於說明以編程方式查找中等數字的主要因素所涉及的原則。它不會威脅到通用數字篩選器,但它可以在相當短的時間內對12-14位數的素數進行因子分解,即使是在我使用的舊Core2 Duo計算機上也是如此。

代碼如下。祝你好運。

#include <string> 
#include <stdio.h> 
#include <iostream> 
#include "BigIntegerLibrary.hh" 

typedef BigInteger BI; 
typedef BigUnsigned BU; 

using std::string; 
using std::cin; 
using std::cout; 

BU pollard(BU numberToFactor); 
BU gcda(BU differenceBetweenCongruentFunctions, BU numberToFactor); 
BU f(BU x, BU numberToFactor, int increment); 
void initializeArrays(); 
BU getNumberToFactor(); 
void factorComposites(); 
bool testForComposite (BU num); 

BU primeFactors[1000]; 
BU compositeFactors[1000]; 
BU tempFactors [1000]; 
int primeIndex; 
int compositeIndex; 
int tempIndex; 
int numberOfCompositeFactors; 
bool allJTestsShowComposite; 

int main() 
{ 
    while(1) 
    { 
     primeIndex=0; 
     compositeIndex=0; 
     tempIndex=0; 
     initializeArrays(); 
     compositeFactors[0] = getNumberToFactor(); 
     cout<<"\n\n"; 
     if (compositeFactors[0] == 0) return 0; 
     numberOfCompositeFactors = 1; 
     factorComposites(); 
    } 
} 

void initializeArrays() 
{ 
    for (int i = 0; i<1000;i++) 
    { 
     primeFactors[i] = 0; 
     compositeFactors[i]=0; 
     tempFactors[i]=0; 
    } 
} 

BU getNumberToFactor() 
{ 
    std::string s; 
    std::cout<<"Enter the number for which you want a prime factor, or 0 to quit: "; 
    std::cin>>s; 
    return stringToBigUnsigned(s); 
} 

void factorComposites() 
{ 
    while (numberOfCompositeFactors!=0) 
    { 
     compositeIndex = 0; 
     tempIndex = 0; 

     // This while loop finds non-zero values in compositeFactors. 
     // If they are composite, it factors them and puts one factor in tempFactors, 
     // then divides the element in compositeFactors by the same amount. 
     // If the element is prime, it moves it into tempFactors (zeros the element in compositeFactors) 
     while (compositeIndex < 1000) 
     { 
      if(compositeFactors[compositeIndex] == 0) 
      { 
       compositeIndex++; 
       continue; 
      } 
      if(testForComposite(compositeFactors[compositeIndex]) == false) 
      { 
       tempFactors[tempIndex] = compositeFactors[compositeIndex]; 
       compositeFactors[compositeIndex] = 0; 
       tempIndex++; 
       compositeIndex++; 
      } 
      else 
      { 
       tempFactors[tempIndex] = pollard (compositeFactors[compositeIndex]); 
       compositeFactors[compositeIndex] /= tempFactors[tempIndex]; 
       tempIndex++; 
       compositeIndex++; 
      } 
     } 
     compositeIndex = 0; 

     // This while loop moves all remaining non-zero values from compositeFactors into tempFactors 
     // When it is done, compositeFactors should be all 0 value elements 
     while (compositeIndex < 1000) 
     { 
      if (compositeFactors[compositeIndex] != 0) 
      { 
       tempFactors[tempIndex] = compositeFactors[compositeIndex]; 
       compositeFactors[compositeIndex] = 0; 
       tempIndex++; 
       compositeIndex++; 
      } 
      else compositeIndex++; 
     } 
     compositeIndex = 0; 
     tempIndex = 0; 

     // This while loop checks all non-zero elements in tempIndex. 
     // Those that are prime are shown on screen and moved to primeFactors 
     // Those that are composite are moved to compositeFactors 
     // When this is done, all elements in tempFactors should be 0 
     while (tempIndex<1000) 
     { 
      if(tempFactors[tempIndex] == 0) 
      { 
       tempIndex++; 
       continue; 
      } 
      if(testForComposite(tempFactors[tempIndex]) == false) 
      { 
       primeFactors[primeIndex] = tempFactors[tempIndex]; 
       cout<<primeFactors[primeIndex]<<"\n"; 
       tempFactors[tempIndex]=0; 
       primeIndex++; 
       tempIndex++; 
      } 
      else 
      { 
       compositeFactors[compositeIndex] = tempFactors[tempIndex]; 
       tempFactors[tempIndex]=0; 
       compositeIndex++; 
       tempIndex++; 
      } 
     } 
     compositeIndex=0; 
     numberOfCompositeFactors=0; 

     // This while loop just checks to be sure there are still one or more composite factors. 
     // As long as there are, the outer while loop will repeat 
     while(compositeIndex<1000) 
     { 
      if(compositeFactors[compositeIndex]!=0) numberOfCompositeFactors++; 
      compositeIndex ++; 
     } 
    } 
    return; 
} 

// The following method uses the Miller-Rabin primality test to prove with 100% confidence a given number is  composite, 
// or to establish with a high level of confidence -- but not 100% -- that it is prime 

bool testForComposite (BU num) 
{ 
    BU confidenceFactor = 101; 
    if (confidenceFactor >= num) confidenceFactor = num-1; 
    BU a,d,s, nMinusOne; 
    nMinusOne=num-1; 
    d=nMinusOne; 
    s=0; 
    while(modexp(d,1,2)==0) 
    { 
     d /= 2; 
     s++; 
    } 
    allJTestsShowComposite = true; // assume composite here until we can prove otherwise 
    for (BI i = 2 ; i<=confidenceFactor;i++) 
    { 
     if (modexp(i,d,num) == 1) 
      continue; // if this modulus is 1, then we cannot prove that num is composite with this  value of i, so continue 
     if (modexp(i,d,num) == nMinusOne) 
     { 
      allJTestsShowComposite = false; 
      continue; 
     } 
     BU exponent(1);  
     for (BU j(0); j.toInt()<=s.toInt()-1;j++) 
     { 
      exponent *= 2; 
      if (modexp(i,exponent*d,num) == nMinusOne) 
      { 
       // if the modulus is not right for even a single j, then break and increment i. 
       allJTestsShowComposite = false; 
       continue; 
      } 
     } 
     if (allJTestsShowComposite == true) return true; // proven composite with 100% certainty, no need  to continue testing 
    } 
    return false; 
    /* not proven composite in any test, so assume prime with a possibility of error = 
    (1/4)^(number of different values of i tested). This will be equal to the value of the 
    confidenceFactor variable, and the "witnesses" to the primality of the number being tested will be all  integers from 
    2 through the value of confidenceFactor. 

    Note that this makes this primality test cryptographically less secure than it could be. It is  theoretically possible, 
    if difficult, for a malicious party to pass a known composite number for which all of the lowest n integers  fail to 
    detect that it is composite. A safer way is to generate random integers in the outer "for" loop and use  those in place of 
    the variable i. Better still if those random numbers are checked to ensure no duplicates are generated. 
    */ 
} 

BU pollard(BU n) 
{ 
    if (n == 4) return 2; 
    BU x = 2; 
    BU y = 2; 
    BU d = 1; 
    int increment = 1; 

    while(d==1||d==n||d==0) 
    { 
     x = f(x,n, increment); 
     y = f(y,n, increment); 
     y = f(y,n, increment); 
     if (y>x) 
     { 
      d = gcda(y-x, n); 
     } 
     else 
     { 
      d = gcda(x-y, n); 
     } 
     if (d==0) 
     { 
      x = 2; 
      y = 2; 
      d = 1; 
      increment++; // This changes the pseudorandom function we use to increment x and y 
     } 
    } 
    return d; 
} 


BU gcda(BU a, BU b) 
{ 
    if (a==b||a==0) 
     return 0; // If x==y or if the absolute value of (x-y) == the number to be factored, then we  have failed to find 
        // a factor. I think this is not proof of primality, so the process could  be repeated with a new function. 
        // For example, by replacing x*x+1 with x*x+2, and so on. If many such  functions fail, primality is likely. 

    BU currentGCD = 1; 
    while (currentGCD!=0) // This while loop is based on Euclid's algorithm 
    { 
     currentGCD = b % a; 
     b=a; 
     a=currentGCD; 
    } 
    return b; 
} 

BU f(BU x, BU n, int increment) 
{ 
    return (x * x + increment) % n; 
}