Miller-Rabin素数检测算法 acm模板

Miller-Rabin素数检测算法

其基于以下两个定理。

  1. Fermat小定理
    若n是素数,则 ∀ a ( a ̸ ≡ 0 ( m o d n ) ) \forall a(a \not\equiv 0 \pmod{n}) a(a̸0(modn)),有 a n − 1 ≡ 1 ( m o d n ) a^{n-1} \equiv 1 \pmod{n} an11(modn).

  2. 二次探测定理
    若n是素数,则 x 2 ≡ 1 ( m o d n ) x^2 \equiv 1 \pmod{n} x21(modn)只有平凡根 x = ± 1 x=\pm1 x=±1,即 x = 1 , x = n − 1 x=1,x=n-1 x=1,x=n1.

费马小定理鼎鼎有名,而二次探测定理由 Z p Z_p Zp是域,域中无零因子容易得到。

注意这两个定理都是叙述了素数的必要条件,而Miller-Rabin对于要检验的n,是选取若干个a,检验是否满足这两个必要条件。显然,如果某个必要条件不满足,那么断言不是素数是正确的。但是,选了好几个a,都满足这两个必要条件,n是质数还是合数是无法确定的,但是Miller-Rabin算法选择忽略这一点,直接断言n是素数。

换句话说,Miller-Rabin算法断言一个数不是素数一定是正确的,断言一个数是素数,则可能是错误的。但是,实际上,会被误判为素数的合数,是很少的。而且每选取一个符合条件的a,通过检验出错的概率不超过 1 2 \frac{1}{2} 21.因此实际应用中使用Miller-Rabin算法是可行的。

实际上,选取一个a,仅仅基于费马小定理给出的必要条件做断言的检测算法,被错误断言为素数的合数称作基于a的伪素数

而通过选取各个符合条件的a,仅仅基于费马小定理,进行断言的检测算法,被错误断言为素数的伪素数就是卡迈克尔数

具体算法

假设 n n n是奇数,令 n = m × 2 q ( q ≥ 1 ) n=m \times 2^q (q \geq 1) n=m×2q(q1),其中 m m m是奇数.
对于序列 a m   m o d   n , a 2 m   m o d   n , a 4 m   m o d   n , … , a 2 q × m   m o d   n a^m \bmod n, a^{2m} \bmod n, a^{4m} \bmod n,\ldots,a^{2^q \times m} \bmod n ammodn,a2mmodn,a4mmodn,,a2q×mmodn.
最后一项就是费马小定理中的 a n − 1 a^{n-1} an1, 并且每一项都是前一项的平方。
我们一项一项往后计算。

  • 若当前项为1,后面每一项显然都是1。而根据二次探测定理,n是素数必须前面一项是1或n-1.如果不符合,断言不是素数;符合,断言是素数。

  • 若当前项不是1,暂时不断言,接着往后算。除非当前是最后一项了,那么断言不是素数。

当然,如果第一项是1,由于不存在二次探测的方程,所以不检验前面一项(或者认为前面一项符合条件)。

Code

使用了快速幂模和快速幂加模板mod_sys。下面代码只是miller-rabin核心代码。

 // 如果只是int范围内,可以将pow_v2改为pow,mlt改为普通乘法
 bool miller_rabin(ll a, ll n, ll q, ll m, mod_sys& mod) {
     a = mod.pow_v2(a, m);
     bool is_ordinary = true;
     for (int i = 0; i < q; ++i) {
         if (a == 1) {
             return is_ordinary;
         } else {
             is_ordinary = (a == n-1);
             a = mod.mlt(a,a);
         }
     }
     return (a==1)&&(is_ordinary); // 最后一项
 }

 // 使用miller_rabin检测是否是素数
 const int kCheckCnt = 8;
 // 为了随机数
 random_device rd;
 mt19937_64 gen(rd());
 bool miller_rabin(ll n) {
     if (n == 2) return true;
     if ((n <= 2) || (n&1^1)) return false;
     // 2^q×m表示原本输入的n-1
     ll m = n, q = 0;
     do { m >>= 1; ++q; } while(m&1^1);
     // 随机数生成,[1,n-1] 均匀分布
     uniform_int_distribution<> dis(1, n-1);
     mod_sys mod;
     mod.set_mod(n);
     for (int i = 0; i < kCheckCnt; ++i)
         if (!miller_rabin(dis(gen), n, q, m, mod))
             return false;
     return true;
 }

模板题推荐hdu2138

你可能感兴趣的:(算法,Acm)