zoukankan      html  css  js  c++  java
  • 费马小定理,积模分解公式,高效幂,快速幂模 及 米勒拉宾检验

    转载自 http://www.cnblogs.com/Knuth/archive/2009/09/04/1559949.html
    好文章,整理收藏。
    1.费马小定理:

    有N为任意正整数,P为素数,且N不能被P整除(显然N和P互质),则有:
    N^P%P=N(即:N的P次方除以P的余数是N)   或  (N^(P-1))%P=1

    互相变形

    原式可化为:
    (N^P-N)%P=0

    (N*(N^(P-1)-1))%P=0
    所以,N*(N^(P-1)-1)是N和P的公倍数

    又因为 N与P互质,而互质数的最小公倍数为它们的乘积

    所以一定存在正整数M使得等式成立:N*(N^(P-1)-1)=M*N*P
    所以N^(P-1)-1=M*P
    因为M是整数
    所以(N^(P-1)-1)%P=0
    即:
    N^(P-1)%P=1 

    2.积模分解公式

    引理, 若:X%Z=0,则(X+Y)%Z=Y%Z

        设有X、Y和Z三个正整数,则必有:(X*Y)%Z=((X%Z)*(Y%Z))%Z

    证明:

    1. 当X和Y都比Z大时,必有整数A和B使下面的等式成立:
      X=Z*I+A(1)
      Y=Z*J+B(2)除模运算的性质
      将(1)和(2)代入(X*Y)modZ得:

      ((Z*I+A)(Z*J+B))%Z

      所以 (Z*(Z*I*J+I*A+I*B)+A*B)%Z(3)
      所以 Z*(Z*I*J+I*A+I*B)能被Z整除
      概据引理,(3)式可化简为:(A*B)%Z
      又因为:A=X%Z,B=Y%Z,代入上式,成为原式右边。

    2. 当X比Z大而Y比Z小时:
      X=Z*I+A
      代入(X*Y)%Z得:
      (Z*I*Y+A*Y)%Z
      根据引理,转化得:(A*Y)%Z
      因为A=X%Z,又因为Y=Y%Z,代入上式,即得到原式右边。
      同理,当X比Z小而Y比Z大时,原式也成立。

    3. 当X比Z小,且Y也比Z小时,X=X%Z,Y=Y%Z,所以原式成立。 

    3.快速乘方算法

    可参见http://www.cnblogs.com/bl4nk/archive/2011/04/20/2022998.html 中的power函数

     

    unsigned Power(unsigned n, unsigned p) //n^p
    {
    unsigned ans
    = 1;
    while (p > 1)
    {
    if (( p & 1 )!=0)  ans *= n;
    n
    *= n; 
    p
    /= 2;
    }
    return n * ans;
    }

     


    4."蒙格马利”快速幂模算法

     

    __int64 Montgomery(__int64 n, __int64 p, __int64 m)
    {
    // 快速计算 (n ^ p) % m 的值,与power算法极类似
    __int64 r = n % m; // 这里的r可不能省
    __int64 k = 1;
    while (p > 1)
    {
    if ((p & 1)!=0)
    {
    k
    = (k * r) % m; // 直接取模
    }
    r
    = (r * r) % m; // 同上
    p /= 2;
    }
    return (r * k) % m; // 还是同上 
    }

    蒙格马利极速版:

    unsigned Montgomery(unsigned n,unsigned p,unsigned m)
    {
    //快速计算(n^p)%m的值
    unsignedk=1;
    n
    %=m;
    while(p!=1)
    {
    if(0!=(p&1))k=(k*n)%m;
    n
    =(n*n)%m;
    p
    >>=1;
    }
    return(n*k)%m;
    }

    5.素数判断
      根据费马小定理,对于两个互质的素数N和P,必有:N^(P-1)%P=1  
    费马测试
      算法思路是这样的:

        对于N,从素数表中取出任意的素数对其进行费马测试,如果取了很多个素数,N仍未测试失败,那么则认为N是素数。当然,测试次数越多越准确,但一般来讲 50次就足够了。费马测试并不完全可靠。
        现在我们发现了重要的一点,费马定理是素数的必要条件而非充分条件。这种不是素数,但又能通过费马测试的数字还有不少,数学上把它们称为Carmichael数,现在 数学家们已经找到所有10 ^ 16以内的Carmichael数,最大的一个是9585921133193329。我们必须寻找更为有效的测试方法。数学家们通过对费马小定理的研究,并加以扩展, 总结出了多种快速有效的素数测试方法,目前最快的算法是米勒-拉宾检验算法。
    米勒-拉宾检验 wikipedia : Miller–Rabin primality test

        米勒-拉宾检验是一个不确定的算法,只能从概率意义上判定一个数可能是素数,测试用的素数表越多,准确率越高,但并不能确保。算法流程如下:
        1.选择T个随机数A,并且有A<N成立。
        2.找到R和M,使得N=2*R*M+1成立。
        快速得到R和M的方式:N用二进制数B来表示,令C=B-1。因为N为奇数(素数都是奇数),所以C的最低位为0,从C的最低位的0开始向高位统计,一直到遇到第一个1。这时0的个数即为R,M为B右移R位的值。
        3.如果A^M%N=1,则通过A对于N的测试,然后进行下一个A的测试
        4.如果A^M%N!=1,那么令i由0迭代至R,进行下面的测试
        5.如果A^((2^i)*M)%N=N-1则通过A对于N的测试,否则进行下一个i的测试
        6.如果i=r,且尚未通过测试,则此A对于N的测试失败,说明N为合数。
        7.进行下一个A对N的测试,直到测试完指定个数的A

        通过验证得知,当T为素数,并且A是平均分布的随机数,那么测试有效率为1 / ( 4 ^ T )。如果T > 8那么测试失误的机率就会小于10^(-5),这对于一般的应用是足够了。如果需要求的素数极大,或着要求更高的保障度,可以适当调高T的值。

    下面是代码:

    bool RabbinMillerTest( unsigned n )
    {
    if (n<2)
    {
    // 小于2的数即不是合数也不是素数
    throw 0;
    }

    const unsigned nPrimeListSize=sizeof(g_aPrimeList)/sizeof(unsigned);//求素数表元素个数
    for(int i=0;i<nPrimeListSize;++i)
    {
    // 按照素数表中的数对当前素数进行判断
    if (n/2+1<=g_aPrimeList[i])
    {
    // 如果已经小于当前素数表的数,则一定是素数
    return true;
    }
    if (0==n%g_aPrimeList[i])
    {
    // 余数为0则说明一定不是素数
    return false;
    }
    }
    // 找到r和m,使得n = 2^r * m + 1;
    int r = 0, m = n - 1; // ( n - 1 ) 一定是合数
    while ( 0 == ( m & 1 ) )
    {
    m
    >>= 1; // 右移一位
    r++; // 统计右移的次数
    }
    const unsigned nTestCnt = 8; // 表示进行测试的次数
    for ( unsigned i = 0; i < nTestCnt; ++i )
    {
    // 利用随机数进行测试,
    int a = g_aPrimeList[ rand() % nPrimeListSize ];
    if ( 1 != Montgomery( a, m, n ) )
    {
    int j = 0;
    int e = 1;
    for ( ; j < r; ++j )
    {
    if ( n - 1 == Montgomery( a, m * e, n ) )
    {
    break;
    }
    e
    <<= 1;
    }
    if (j == r)
    {
    return false;
    }
    }
    }
    return true;
    }
    在2^31-1以下,有一些伪素数可以通过{2,3,5,7,11}的测试,
    已知的有 29341 = 13 * 37 * 61 , 3215031751 = 151 * 751 * 28351
  • 相关阅读:
    游戏与必胜策略
    中国剩余定理
    中国剩余定理
    欧几里得和扩展欧几里得
    欧几里得和扩展欧几里得
    51nod 1028 大数乘法 V2
    51nod 1028 大数乘法 V2
    51nod 1029 大数除法
    51nod 1029 大数除法
    51nod 1166 大数开平方
  • 原文地址:https://www.cnblogs.com/bl4nk/p/2023074.html
Copyright © 2011-2022 走看看