没有得到Pollard的rho算法实现的正确输出

时间:2012-11-23 17:17:57

标签: c algorithm recursion prime-factoring

我不知道在使用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);
}

4 个答案:

答案 0 :(得分:5)

一个直接问题是,as Peter de Rivaz suspected

#define f(x)  x*x-1

因此行

x = f(x)%n;

变为

x = x*x-1%n;

并且%的优先级高于-的优先级,因此表达式隐式括号为

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

相当于x = x*x - 1;(我假设为n > 1,无论如何是x = x*x - constant;),如果你以值x >= 2开头,那么在你有一个现实之前就会溢出找到一个因素的机会:

2 - &gt; 2 * 2-1 = 3 - &gt; 3 * 3 - 1 = 8 - &gt; 8 * 8 - 1 = 63 - &gt; 3968 - &gt; 15745023 - &gt;如果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 (对于int,标准的带符号32位x >= 46341),因此,因为溢出而将较大的n分解可能会失败。至少,您应该使用unsigned long long进行计算,以避免n < 2^32溢出。然而,通过试验分割通常可以更有效地分解如此小的数字。 Pollard的Rho方法和其他先进的保理算法适用于较大的数字,其中试验分割不再有效甚至可行。

答案 1 :(得分:3)

我只是C ++的新手,而且我是Stack Overflow的新手,所以我写的一些内容看起来很草率,但这应该让你朝着正确的方向前进。这里发布的程序通常应该找到并返回您在提示时输入的数字的一个非平凡因素,否则如果找不到这样的因素,它会道歉。

我用几个半数字测试它,它对我有用。对于371156167103,在我按下回车键后,它找到607619没有任何可检测到的延迟。我没有用比这更大的数字检查它。我使用了无符号长long变量,但是如果可能的话,你应该使用一个提供更大整数类型的库。

要添加的编辑,对X的方法f的单次调用和对Y的2次此类调用是有意的,并且与算法的工作方式一致。我想在另一个这样的调用中嵌入对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;
}

答案 2 :(得分:1)

很抱歉长时间的延迟回到这里。正如我在第一个回答中提到的,我是C ++的新手,这在我过度使用全局变量,过度使用BigIntegers和BigUnsigned时会很明显,其他类型可能更好,缺少错误检查以及其他编程习惯。显示技术人员可能不会展示的内容。话虽如此,让我解释一下我做了什么,然后发布代码。

我在第二个答案中这样做是因为第一个答案对于一个Pollard的Rho算法在你理解它的作用后如何实现的非常简单的演示非常有用。它的作用是首先取两个变量,称之为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.我将走过Pollard Rho并告诉你它是如何找到一个非平凡的因素。

循环#1:X从2开始。然后使用函数(x ^ 2 + 1)%n,(2 ^ 2 + 1)%35,我们得到5为x。 Y也从2开始,经过一个函数后,它的值也是5.但是y总是经过两次函数,所以第二次运行是(5 ^ 2 + 1)%35,或者26。 x和y之间的差值是21. 21的GCD(差值)和35(n)是7.我们已经找到了35的素因子!请注意,任何2个数字的GCD,甚至是非常大的指数,都可以通过使用Euclid算法的公式很快找到,这就是我将在这里发布的程序。

关于GCD功能的主题,我使用的是我为此程序下载的一个库,一个允许我使用BigIntegers和BigUnsigned的库。该库还内置了GCD功能,我可以使用它。但我决定继续使用手写的GCD功能用于教学目的。如果你想改进程序的执行时间,那么使用库的GCD函数可能是一个好主意,因为有比Euclid更快的方法,并且可以编写库来更快地使用它们之一方法

另一方面说明。 .Net 4.5库也支持使用BigIntegers和BigUnsigned。我决定不将它用于这个程序,因为我想用C ++编写整个文件,而不是C ++ / CLI。您可以从.Net库中获得更好的性能,或者您可能没有。我不知道,但我想分享这也是一种选择。

我在这里跳了一下,所以让我现在开始大致解释程序的功能,最后我将解释如何使用Visual Studio 11(也称为Visual Studio)在计算机上进行设置2012)。

该程序分配3个数组,用于存储您为其处理的任何数字的因子。这些数组的宽度为1000个元素,这可能是过多的,但它确保任何数量都不超过1000个素数因子。

在提示符处输入数字时,它假定数字是复合数并将其放在compositeFactors数组的第一个元素中。然后它通过一些公认的低效的while循环,使用Miller-Rabin来检查数字是否是复合的。请注意,此测试可以说数字是100%置信度的复合数,或者可以说数字是素数且具有极高(但不是100%)的置信度。置信度可通过程序中的变量confidenceFactor进行调整。该程序将对2和confidenceFactor之间的每个值进行一次检查(包括两者),因此总检查次数少于confidenceFactor本身的值。

我对confidenceFactor的设置是101,它进行100次检查。如果它说一个数字是素数,它实际上是复合的几率是1/4 100,或者与连续200次正确调用公平硬币的翻转的几率相同。简而言之,如果它表示数字是素数,那么可能是,但可以增加置信因子数,以便以更快的速度获得更大的信心。

这可能是一个很好的地方,尽管如此,虽然Pollard的Rho算法可以非常有效地分解较小数量的长long类型,Miller-Rabin测试看看数字是否是复合的将是没有BigInteger和BigUnsigned类型,或多或少没用。 BigInteger库几乎是一个要求,能够将大数字一直可靠地分解为这样的素数因素。

当Miller Rabin说这个因子是复合因素时,它是因子,存储在临时数组中的因子,以及复合数组中的原始因子除以相同的因子。当数字被识别为可能的素数时,它们被移入素数因子数组并输出到屏幕。这个过程一直持续到没有复合因素为止。这些因素往往以升序排列,但这是巧合。该程序不会按升序列出它们,但只会在找到它们时列出它们。

请注意,我找不到任何函数(x ^ 2 + c)%n,它会将数字4计算在内,无论我给出的值是多少。 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;
}

答案 3 :(得分:0)

据我所知,Pollard Rho通常使用f(x)作为(x*x+1)(例如在这些lecture notes中)。

您对x*x-1的选择似乎并不像通常似乎陷入困境一样好:

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