2 条题解
-
2
Miller-Rabin 的推导本质上是把费马小定理和二次探测定理串联起来,形成一个可高效验证的判定链。
费马小定理告诉我们:若 是素数,,则
我们想用它的逆否命题来判合数:若 ,则 是合数。
问题:存在合数(Carmichael 数)使得 对某些 成立。仅靠费马小定理会漏判。
若 是奇素数,且 ,则
$$x \equiv 1 \pmod p \quad \text{或} \quad x \equiv -1 \pmod p$$逆否命题:若在模 下算出 ,但 ,则 一定是合数。
为了把二次探测嵌入费马测试,我们将 分解为:
这样 可以写成一个逐步平方的链:
$$a^{n-1} = a^{2^s \cdot d} = \underbrace{(\cdots((a^d)^2)^2\cdots)^2}_{s \text{ 次平方}}$$定义序列:
$$t_0 = a^d, \quad t_1 = t_0^2, \quad t_2 = t_1^2, \quad \dots, \quad t_s = t_{s-1}^2 = a^{n-1}$$-
若满足 。此时费马/二次探测直接判定。
-
若满足 。但我们知道最终 (费马小定理)。既然序列从"非 "变到了"",那么在某个位置必然发生了 从 到 的跳变(因为 是 在模素数下唯一的另一个平方根)。
即:存在某个 ,使得
$$t_{j-1} \equiv n-1 \pmod n \quad \text{且} \quad t_j = t_{j-1}^2 \equiv 1 \pmod n$$
合并结论:若 是素数,则对任意基底 ,序列 中要么 ,要么存在某个 ()。
对上述结论取逆否命题,就得到了 Miller-Rabin 的合数判定条件:
若对某个基底 ,序列 满足:
- 且 ;
- 对所有 ,;
则 一定是合数。
若所有选定基底都不触发上述合数条件,则 为素数(在确定性基底集下可严格证明)。
算法逻辑:
对每个选定的基底 ,令 ,d 为 去掉所有因子 后剩下的奇数部分:
- 若 或 ,本轮通过。
- 否则,将 反复平方最多 次。若某次平方后得到 ,本轮通过。
- 若始终没得到 ,则 是合数。
- 若所有选定基底都通过,则 是素数。
代码中选用了前 15 个素数
{2,3,5,7,11,13,17,19,23,29,31,37,41,43,47}作为测试基底。前 15 个素数的基底已被数学证明:在__int128范围内该测试是绝对安全的。算法时间复杂度为 ,其中 为基底。
代码
#include<bits/stdc++.h> #define int __int128 // 扩展精度,支持约1.7×10^38范围 using namespace std; // 确定性测试基底,覆盖__int128范围 const int prime[]={2,3,5,7,11,13,17,19,23,29,31,37,41,43,47}; template<typename T>inline void qr(T &x){ int f=1;x=0;char c=getchar(); for(;!isdigit(c);c=getchar()) if(c=='-') f=-1; for(;isdigit(c);c=getchar()) x=x*10+c-'0'; x*=f; } template<typename T>inline void qw(T x){ if(x<0) putchar('-'),x=-x; if(x/10) qw(x/10); putchar(x%10+'0'); } inline int qpow(int a,int b,int m){ int res=1; a%=m; for(;b;b>>=1,a=(a*a)%m) if(b&1) res=(res*a)%m; return res; } // Miller-Rabin确定性素性测试 inline bool Is_prime(int x){ if(x<2) return false; // 小数直接查表 if(x<=37){ for(int p:prime) if(x==p) return true; return false; } // 分解x-1 = 2^s * d int d=x-1,s=0; while(!(d&1)) d>>=1,s++; // 逐个基底测试 for(int y:prime){ if(y>=x) break; int t=qpow(y,d,x); // 首项为1或x-1,本轮通过 if(t==1||t==x-1) continue; // 二次探测:反复平方检查是否出现x-1 bool flg=false; for(int j=1;j<=s;j++){ t=t*t%x; if(t==x-1){ flg=true; break; } } // 未通过二次探测,必为合数 if(!flg) return false; } return true; // 所有基底通过,确定为素数 } signed main(){ ios::sync_with_stdio(false); cin.tie(0),cout.tie(0); int T; qr(T); while(T--){ int n; qr(n); puts(Is_prime(n)?"Yes":"No"); } } -
- 1
信息
- ID
- 3250
- 时间
- 1000ms
- 内存
- 1024MiB
- 难度
- 7
- 标签
- (无)
- 递交数
- 45
- 已通过
- 10
- 上传者