Miller-Rabin 与 Pollard-Rho
1 Miller-Rabin 算法
1.1 引入
Miller-Rabin 的主要作用就是判断一个较大的数是不是质数。
那么根据基础数论中提到过的试除法,我们知道朴素去判断一个数是否是质数的复杂度是
而 Miller-Rabin 则是基于费马小定理进行的素性测试,所以首先我们需要知道费马小定理是什么:
当
为质数时,对于任意整数 ,会有 。
那么如果对于所有的
事实上本文介绍的两个算法看上去都是基于随机的,但是都有很高的正确率。
1.2 算法实现
在了解 Miller-Rabin 的实现过程之前,需要了解二次探测定理。
1.2.1 二次探测定理
二次探测定理:若
证明:
。
。
或 。
1.2.2 算法流程
加入我们当前要判断的数是
那么我们就算出
那么现在的问题就只在于
代码如下:
int prim[15] = {0, 2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41};
bool Miller_Rabin(int x) {
if(x == 2) return 1;
int t = x - 1, k = 0;
while(!(t & 1)) t >>= 1, k++;
for(int i = 1; i <= 13; i++) {
if(x == prim[i]) return 1;
int a = qpow(prim[i] % x, t, x), nxt;
for(int j = 1; j <= k; j++) {
nxt = a * a % x;
if(nxt == 1 && a != 1 && a != x - 1) return 0;
a = nxt;
}
if(a != 1) return 0;
}
return 1;
}
2 Pollard-Rho 算法
2.1 引入
首先考虑这样一个问题:给定一个正整数
我们在基础数论中提到过可以利用试除法来求解,只需要枚举所有
这个时候考虑我们的玄学方法,也就是随机化。我们在
事实上有,而这就是 Pollard-Rho 的基本思想。
2.2 生日悖论
首先我们需要引入一个东西叫做生日悖论。
我们考虑这样一个问题:假如一个房间中有
我们考虑正难则反,显然
上述数学模型与我们的实际经验严重不符,因此被称作生日悖论。
这个东西给了我们什么启发?考虑生日悖论的实质,实际上就是利用了 “组合随机采样” 的方法,满足答案的组合比单个个体要多,以此来提高正确率。那么怎么将这种思想运用到上面的分解因数中呢?
2.3 算法实现
2.3.1 随机算法的优化
我们利用 “组合随机采样” 的思想,考虑怎样进行组合。显然
那么 Pollard-Rho 算法使用了一个随机函数生成一个序列
如果你注意力较高,会发现一件事:由于

(发现它长得很像一个字母
根据生日悖论可知,这个数列中不同值的数量约为
下面介绍 Pollard-Rho 算法的两种实现方式。
2.3.2 具体流程
2.3.2.1 Floyd 判环
考虑经典的小学奥数:两个人在同一个圆上跑,一快一慢,经过一段时间后两者必会相遇。那我们在
然后我们每一次去记录两个
代码如下:
int f(int x, int c, int n) {
return (mul(x, x, n) + c) % n;
}
int Pollard_Rho(int n, int c) {
int a, b;
int x = rnd() % (n - 1) + 1;
a = f(x, c, n), b = f(f(x, c, n), c, n);
while(a != b) {
int d = __gcd(abs(a - b), n);
if(d > 1) return d;
a = f(a, c, n);
b = f(f(b, c, n), c, n);
}
return n;
}
int main() {
ios::sync_with_stdio(0);
cin.tie(0), cout.tie(0);
//...
int p = n;
while(p >= n) {
p = Pollard_Rho(n, rnd() % (n - 1) + 1);
}
//...
return 0;
}
(实测证明,上述 Floyd 判环代码正确率较下面做法要低)
2.3.2.2 倍增优化
上述过程我们是在同时跑
代码如下:
int f(int x, int c, int n) {
return (mul(x, x, n) + c) % n;
}
int Pollard_Rho(int n, int c) {
int a, b, i = 1, k = 2;
a = rnd() % (n - 1) + 1, b = a;
while(1) {
a = f(a, c, n);
int d = __gcd(abs(a - b), n);
if(d > 1) return d;
if(a == b) return n;
if(++i == k) {
k <<= 1;
b = a;
}
}
return n;
}
我们还有一个小优化称作
int Pollard_Rho(int n, int c) {
int a, b, i = 1, k = 2, val = 1;
a = rnd() % (n - 1) + 1, b = a;
while(1) {
a = f(a, c, n);
val = val * abs(a - b) % n;
if(i % 127 == 0) {
int d = __gcd(val, n);
if(d > 1) return d;
}
if(a == b) return n;
if(++i == k) {
k <<= 1;
b = a;
int d = __gcd(val, n);
if(d > 1) return d;
}
}
}
3 综合应用
模板题:【模板】Pollard-Rho,实际上就是上面两个算法结合起来。代码如下:
#include <bits/stdc++.h>
#define ll long long
#define int __int128
using namespace std;
const ll Maxn = 2e5 + 5;
const ll Inf = 2e9;
ll T, n;
int qpow(int a, int b, int p) {
int res = 1;
while(b) {
if(b & 1) {
res = res * a % p;
}
a = a * a % p;
b >>= 1;
}
return res;
}
int prim[15] = {0, 2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41};
bool Miller_Rabin(int x) {
if(x == 2) return 1;
int t = x - 1, k = 0;
while(!(t & 1)) t >>= 1, k++;
for(int i = 1; i <= 13; i++) {
if(x == prim[i]) return 1;
int a = qpow(prim[i] % x, t, x), nxt;
for(int j = 1; j <= k; j++) {
nxt = a * a % x;
if(nxt == 1 && a != 1 && a != x - 1) return 0;
a = nxt;
}
if(a != 1) return 0;
}
return 1;
}
mt19937 rnd(time(0));
int f(int x, int c, int n) {
return (x * x % n + c) % n;
}
#define abs(x) (x > 0 ? x : -x)
int Pollard_Rho(int n, int c) {
int a, b, i = 1, k = 2, val = 1;
a = rnd() % (n - 1) + 1, b = a;
while(1) {
a = f(a, c, n);
val = val * abs(a - b) % n;
if(i % 127 == 0) {
int d = __gcd(val, n);
if(d > 1) return d;
}
if(a == b) return n;
if(++i == k) {
k <<= 1;
b = a;
int d = __gcd(val, n);
if(d > 1) return d;
}
}
}
#define max(x, y) (x > y ? x : y)
ll ans = 0;
void Div(int x) {
if(x == 1) return ;
if(Miller_Rabin(x)) {
ans = max(ans, x);
return ;
}
int p = x;
while(p >= x) {
p = Pollard_Rho(x, rnd() % (n + 1) + 1);
}
Div(p);
Div(x / p);
}
signed main() {
ios::sync_with_stdio(0);
cin.tie(0), cout.tie(0);
cin >> T;
while(T--) {
cin >> n;
ans = 0;
Div(n);
if(ans == n) {
cout << "Prime\n";
}
else {
cout << ans << '\n';
}
}
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 单元测试从入门到精通
· 上周热点回顾(3.3-3.9)
· winform 绘制太阳,地球,月球 运作规律