判断质数 _ Miller Rabin算法
算法作用
判断一个数是否是素数
算法依据
费马小定理
如果P是素数,且整数a不是p的倍数有:
费马定理只是n是素数的必要条件。即费马定理不成立,n一定是合数;费马定理成立,n可能是素数。
点击查看证明
性质 1:$p-1$个整数 $a,2a,3a,...(p-1)a$中没有一个是 $p$的倍数 性质 2:$a,2a,3a,...(p-1)a$中没有任何两个同余与模 $p$的 所以 $a,2a,3a,...(p-1)a$对模 $p$的同余既不为零,也没有两个同余相同 因此,这 $p-1$个数模 $p$的同余一定是 $a,2a,3a,...(p-1)a$的某一种排列 即 $a*2a*3a*...*(p-1)a \equiv {1*2*3*...*(p-1)} \pmod p$ 化简为 $a^{p-1}*(p-1)! \equiv {p-1}! \pmod p$ 根据威尔逊定理可知 $(p-1)!$与 $p$互质,所以同时约去 $(p-1)!$ 即得到 $a^{p-1}\equiv 1 \pmod P$
二次探测定理
若 为素数,对于:
求解的解只有两种可能:a = 1 或者 a = p-1
点击查看证明
$a^{2}\equiv 1 \pmod P$ $a^{2}-1\equiv 0 \pmod P$ $(a+1)*(a-1)\equiv 0 \pmod P$ 那么 $(a+1)\equiv 0 \pmod P$ 或者 $(a-1)\equiv 0 \pmod P$ (此处可根据唯一分解定理证明) 即 $a\equiv \pm 1 \pmod P$ $a^{2}\equiv 1 \pmod P$ $a^{2}-1\equiv 0 \pmod P$ $(a+1)*(a-1)\equiv 0 \pmod P$ 那么 $(a+1)\equiv 0 \pmod P$ 或者 $(a-1)\equiv 0 \pmod P$ (此处可根据唯一分解定理证明) 即 $a\equiv \pm 1 \pmod P$
推理定理
Miller Rabin 算法的过程
假设需要判断的数是
我们把 分解为 的形式
当 是素数,有
然后随机选择一个数 ,计算出
让其不断的乘 ,同时结合二次探测定理进行判断
如果
那么这个数就是合数 (违背了二次探测定理)
假如p是素数,那么自乘后的数应该== 1或n-1
这样乘 次,最后得到的数就是
那么如果最后计算出的数不为 ,这个数也是合数 (费马小定理)
正确性判断
老祖宗告诉我们,若 通过一次测试,则 不是素数的概率为 %
那么经过 轮测试,不是素数的概率为
我习惯用 这几个数进行判断
在信息学范围内出错率为 %(不带高精)
算法流程
- 首先对于要判断的数 n,先判断是不是 2,是就直接返回 true。
- 判断是不是小于 2 或者为偶数,是就返回 false。
- 令 n-1 = m*2t,求出 u 和 t。
- 利用 rand 随机找一个 a, a 属于 [1, n-1]
- 令 x = (am)%n
- 然后进行 t 次循环,为什么要循环 t 次呢,上面证明是从 2t 倒着推到 20,我们写程序完全可以正着来,上面的 x 其实就是
- 那么在循环中每次都进行 y =(x*x)%n, x = y 的操作,最终会变为 an-1,也就是
又因为 x 在循环时是一定小于 n 的,所以可以用二次探测定理
运算过程中如果 (x2)%n = 1, 也就是 y = 1,假如 n 是素数,那么 x == 1 或者 x == n-1,否则 n 就一定不是素数,直接返回 false。 - 整个循环结束后,程序运行到最后 x = an-1, 根据费马小定理,如果 x 还是不等于 1,那么肯定不是素数,直接返回 false。
- Miller-Rabin 算法正确率为 75%,所以要进行大约 4~8 次来提高正确率。
代码
#include <iostream> #include <cstdlib> using namespace std; typedef long long ll; ll qul_mul(ll a, ll b, ll n) //快速积运算,快速求出a^m^*a^m^并且能防止爆掉 { ll num = 0; while (b) { if (b&1) { num=(num+a)%n; } a = (a+a)%n; b>>=1; } return num; } ll qul_pow(ll a, ll b, ll n) //快速幂计算快速求出a^m^的值 { ll sum = 1; while (b) { if (b&1) { sum = sum * a % n; } a=a*a%n; b>>=1; } return sum; } bool Miller_Rabin(ll n) { int m = n-1; int t = 0; if (n==2) { return true; } else if (n<2 || !(n&1)) { return false; } //求出n-1 = m*2^t的m和t。 while (!(m&1)) { m>>=1; t++; } for (int i=0; i<20; i++) { //随机数a ll a = rand()%(n-1) + 1; // 计算a^m ll x = qul_pow(a, m, n), y; for (int j=0; j<t; j++) { y = qul_mul(x, x, n); //进行(x*x)%n操作。 //不满足二次探测定理,也就是y得1了但是x并不等于1或者n-1,那么n就一定不是质数。 if (y==1 && x!=1 && x!=n-1) { return false; } x = y; } //上面循环跑完了,如果最后x都不等于1,那么一定是一个合数了。 if (x!=1) { return false; } } return true; } int main() { ll n; while (cin >> n) { cout << Miller_Rabin(n) << endl; } return 0; }
代码
注意在进行素数判断的时候需要用到快速幂。。
#include<cstdio> #define LL long long inline int read() { char c = getchar(); int x = 0, f = 1; while(c < '0' || c > '9') {if(c == '-') f = -1; c = getchar();} while(c >= '0' && c <= '9') x = x * 10 + c - '0', c = getchar(); return x * f; } int N, M, Test[10] = {2, 3, 5, 7, 11, 13, 17}; int pow(int a, int p, int mod) { int base = 1; for(; p; p >>= 1, a = (1ll * a * a) % mod) if(p & 1) base = (1ll * base * a) % mod; return base % mod; } bool Query(int P) { if(P == 1) return 0; int t = P - 1, k = 0; while(!(t & 1)) k++, t >>= 1; for(int i = 0; i < 4; i++) { if(P == Test[i]) return 1; LL a = pow(Test[i], t, P), nxt = a; for(int j = 1; j <= k; j++) { nxt = (a * a) % P; if(nxt == 1 && a != 1 && a != P - 1) return 0; a = nxt; } if(a != 1) return 0; } return 1; } main() { N = read(); M = read(); while(M--) puts(Query(read()) ? "Yes" : "No"); }
本文作者:kingwzun
本文链接:https://www.cnblogs.com/kingwz/p/15924890.html
版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步