• Miller_Rabin素数测试【学习笔记】


    引语:在数论中,对于素数的研究一直就很多,素数测试的方法也是非常多,如埃式筛法,6N±1法,或者直接暴力判(试除法)。但是如果要判断比较大的数是否为素数,那么传统的试除法和筛法都不再适用。所以我们需要学习Miller_Rabin算法。

    知识准备 + 算法推导:

    1.威尔逊定理:若p是素数,则 (p-1) !≡ -1(mod p).

    2.有趣的是,威尔逊定理的逆命题也是正确的:设n是正整数且 n ≥ 2 ,若 (n-1) !≡ -1(mod n),则是素数.

    很多朋友可能在学习的时候会碰到威尔逊定理,它主要是告诉我们,它的逆定理给出了一种素性检验的方法(其实用的少,原因在后),遗憾的是,这不是一个实用的检验法,因为这需要进行(n-2)次模n的乘法运算才能得到 (n-1)!n的值,运算量达到了O( n(log2n)2 ) 次位运算。

    行吧,那我们只能另谋出路了。

    3.费马小定理:设是一个素数,a是一个正整数且p不整除a ,则 ap-≡ 1(mod p).

    4.伪素数:令b是一个正整数. 若n是一个正合数且 bn ≡ b(mod n),则称n为以b为基的伪素数(有时也称费马伪素数)。(唉~,这是虚伪的素数,它爱着费马测试,却是合数)。

    辣个男人,它来了!

    那么基于费马小定理,Miller检验:假如n是素数,且gcd(a,n) = 1,那么 an-1 ≡ 1(mod n).如果 an-1 ≡ 1(mod n)(a为任意小于n的正整数),则可近似认为n是素数。取多个底进行试验,次数越多,n为素数的概率越大。

    5.【重头戏】卡迈尔数:一个合数 n 若对所有满足 gcd(b, n) = 1 的正整数b都有 bn-1 ≡ 1(mod n)成立,则称为卡迈尔(Carmichael)数或者称为绝对伪素数(不得不服,6601)。

    6.二次探测定理:如果p是一个素数,且 0 < x < p,则方程 x2%p = 1的解为 x = 1 x = p - 1.

    辣个男人,它又来了!

    既然有卡迈尔数的存在,那么需要排除卡迈尔数,可以根据二次定理,在利用费马小定理计算 bn-1%的过程中增加对整数n的二次探测,一旦发现违背二次探测条件,即得出n不是素数的结论。

    这里,令 n - 1 = 2rs,其中s是一个奇数,随机选择一个a1 ≤ a ≤ n-1 ,如果  a2s ≡ 1(mod n) 并且 a≡ 1 (mod n)a≡ (n-1) (mod n),则通过了测试,但如果后面的测试,在a的指数不断乘2的过程中,如果出现没有通过测试,则不是素数。如果取了几次底a,都通过了测试,那么我们就可以接近100%的认为n是素数。

    代码:

    
    #define LL long long
    //这里可以采用随机数,我用的是准备好的基数
    const int Test[] = {2, 3, 5, 7, 11, 13, 17, 19};
    const int Times = 8;  //可以调整
    
    LL Multi(LL a, LL b, LL mod)
    {
        LL ans = 0;
        while(b)
        {
            if(b&1)
            {
                ans = (ans + a)%mod;
            }
            a = (a+a)%mod;  //要这么写
            b>>=1;
        }
        return ans;
    }
    
    LL Pow(LL a, LL b, LL mod)
    {
        LL ans = 1;
        while(b)
        {
            if(b&1)
            {
                ans = Multi(ans, a, mod);
            }
            b>>=1;
            a = Multi(a, a, mod);
        }
        return ans;
    }
    
    bool Miller_Rabin(LL n)
    {
        if(n < 2)  return false;
        LL s = n-1, t = 0;
        while( !(s&1) )
        {
            t++;
            s>>=1;
        }
        for(int i = 0; i < Times; i++)
        {
            if(n == Test[i])
                return true;
            LL x = Pow(Test[i], s, n);
            LL next = x;
            for(int j = 0; j < t; j++)
            {
                next = Multi(x, x, n);
                if(next == 1 && x != 1 && x != n-1)
                    return false;
                x = next;
            }
            if(x != 1)
                return false;
        }
        return true;
    }
    

      

     

  • 相关阅读:
    【(高职专科组)第十一届蓝桥杯省模拟赛答案】给定一个数列,请问找出元素之间最大的元素距离。
    【(高职专科组)第十一届蓝桥杯省模拟赛答案】给定一个数列,请问数列中最长的递增序列有多长。
    POJ 2391 二分+最大流
    HDU 4529 状压dp
    NYOJ 747贪心+dp
    NYOJ 745 dp
    HDU 2686 / NYOJ 61 DP
    HDU 4313树形DP
    HDU 4303 树形DP
    POJ 2342 树形DP
  • 原文地址:https://www.cnblogs.com/dybala21/p/9741605.html
Copyright © 2020-2023  润新知