@总结 - 10@ Miller-Rabin素性测试与Pollard-Rho因数分解
@1 - 素性测试:Miller-Rabin算法@
@1.1 - 算法来源@
假如我们需要检测一个数 x 是否为素数,我们应该怎么做?
最显然的就是从 2~n-1 尝试去找到 x 的另一个因数。
当然可以稍微优化一点:只需从 2~√x 中寻找,这个算法复杂度是 O(√x) 的。
那么是否有更优秀的算法?在密码学中所使用的数论,如果仅用 O(√x) 的算法是远远不足的。
更优的确定性算法的确存在,但过于复杂,在算法竞赛中不大适用。
我们不妨转换方向,设计一个更为简便的随机算法。
考虑费马小定理:
\]
它的逆定理为:
\]
这个定理不一定成立,但是很大概率上成立。
我们不妨选择一些 a,检测定理是否成立,以此判断 p 是否为素数。
于是这个算法正确的概率非常高。
看起来很有道理,但是——存在一些合数 x,对于所有的 a 都满足 \(a^{x-1} = 1 \mod x\)。
我们需要对这个算法进一步改进,而这个改进需要用到下面的二次探测定理:
\]
它的逆定理为:
\]
这个逆定理一样很大概率上成立。
可以证明,对于每一个合数 x,必然存在一个 a 不会同时满足上面两个逆定理。
@1.2 - 算法描述@
假如我们要检测的数为 x。
我们选取一些质数 a1, a2, ..., ak。
先排除掉 x = ai 以及 x 是 ai 的倍数的情况,以加快速度。
令 x - 1 = 2^k * m,且 m 是奇数(即提取 p - 1 中的 2 的幂)。
因为 x 为偶数的情况上面已经排掉了,所以 k >= 1。
接下来,对于每一个 ai 做一遍下面的过程:
求出 n0 = ai^m,如果此时 n0 = 1 或 p - 1 就视为检测成功。
依次求出 n1 = 2*n0,n2 = 2*n1 = 2^2*n0,...,nk = 2^k*n0。
依次检验 n1~nk。对于 ni,如果 ni = p - 1 视为检测成功;否则如果 ni = 1 视为检测失败;如果最终 nk ≠ 1 则视为检测失败。
可以发现上面的检测既要求满足二次探测,又要求满足费马小定理才会检测成功。
质数 a1, a2, ..., ak 视情况而定。
如果 x <= 10^12,a 取 {2, 13, 23, 1662803} 即可。
如果 x <= 10^18,a 取 {2, 3, 5, 7, 11, 13, 17, 19, 23} 即可。
取得太多虽然可以进一步保证正确性,但太慢。
这个算法,检测失败一定失败,检测成功不一定成功。这就是它的随机性所在。
但在算法竞赛的数据范围中是可以保证正确性的。
@1.3 - 算法实现@
算法实现还是比较巧妙的。
typedef long long ll;
const int prm[] = {2, 3, 5, 7, 11, 13, 17, 19, 23, 29};
ll mul_mod(ll a, ll b, ll mod) {
ll ret = 0;
while( b ) {
if( b & 1 ) ret = (ret + a)%mod;
a = (a + a)%mod;
b >>= 1;
}
return ret;
}
ll pow_mod(ll b, ll p, ll mod) {
ll ret = 1;
while( p ) {
if( p & 1 ) ret = mul_mod(ret, b, mod);
b = mul_mod(b, b, mod);
p >>= 1;
}
return ret;
}
bool Miller_Rabin(ll x, ll y, ll z) {
ll pw = pow_mod(y, z, x);
if( pw == 1 || pw == x-1 ) return true;
for(;z!=x-1;z<<=1) {
pw = mul_mod(pw, pw, x);
if( pw == x-1 ) return true;
else if( pw == 1 ) return false;
}
return false;
}
bool Prime_Test(ll x) {
for(int i=0;i<10;i++)
if( x == prm[i] ) return true;
else if( x % prm[i] == 0 ) return false;
ll k = x-1;
while( k % 2 == 0 ) k /= 2;
for(int i=0;i<10;i++)
if( !Miller_Rabin(x, prm[i], k) ) return false;
return true;
}
@2 - 因数分解:Pollard-Rho算法@
@2.0 - 参考资料@
@2.1 - 算法来源@
同素性测试一样,pollard-rho 算法是结合代码简便+时间相对优秀为一体的算法,方便用于算法竞赛。
假如 N 为合数(此处需要先用素性测试测试 N 是否为合数)。
则存在 p <= q 且 p ≠ 1,满足 N = p*q。
我们生成一个随机数列 {xi},使得 x1 = rand(), xi = f(xi-1) mod N。
如果存在 i, j 满足 xi = xj mod p 且 xi ≠ xj mod N,则我们就算是找到了这个 p。
而取 d = gcd(xi - xj, N) 就一定可以找到 N 的一个非平凡因子 d。
注意按照我们定义出来的随机数列 {xi},它必然存在一个循环节。且根据生日悖论,该循环节 r1 的期望长度为 O(√N)。
令数列 {yi} 为 yi = xi mod p,则 {yi} 的循环节 r2 的期望长度为 O(√p),且是 r1 的一个因子。
当 r1 ≠ r2 时,我们就可以按照上面的方法找到非平凡因子 d。
@2.2 - 算法描述@
一般来说,我们可以取 f(x) = x^2 + a,其中 a 是一个随机参数。
我们取 x[i] 与 x[2*i](具体可以令 z[i] = x[2*i],则 z[i+1] = f(f(z[i])))。
如果 gcd(x[i] - x[2*i], N) = 1,则 2*i - i = i 并不是个循环节,继续迭代。
如果 gcd(x[i] - x[2*i], N) = N,则 x[i] = x[2*i],2*i - i = i 是 {xi} 的循环节;此时停止迭代,变更随机参数,再来一遍。
否则,gcd(x[i] - x[2*i], N) 就是我们要找的那个因子。
我们期望枚举 O(√p) 次以得到我们的答案,而 p = O(√N)。
所以最终算法时间复杂度期望为 \(O(N^{\frac{1}{4}}*\alpha(N))\),其中 \(\alpha(N)\) 为 gcd 的复杂度。
这个算法也是一个随机算法,不过和上面的不同,这个算法一定给出正确答案,但时间复杂度是期望的。
@2.3 - 算法实现@
下面的代码展示的是求一个数的最小因数的过程。
实现上依然有一些值得注意的小细节。
typedef long long ll;
const int INF = (1<<30);
const int prm[] = {2, 3, 5, 7, 11, 13, 17, 19, 23, 29};
ll mul_mod(ll a, ll b, ll mod) {
ll ret = 0;
while( b ) {
if( b & 1 ) ret = (ret + a)%mod;
a = (a + a)%mod;
b >>= 1;
}
return ret;
}
ll pow_mod(ll b, ll p, ll mod) {
ll ret = 1;
while( p ) {
if( p & 1 ) ret = mul_mod(ret, b, mod);
b = mul_mod(b, b, mod);
p >>= 1;
}
return ret;
}
bool Miller_Rabin(ll x, ll y, ll z) {
ll pw = pow_mod(y, z, x);
if( pw == 1 || pw == x-1 ) return true;
for(;z!=x-1;z<<=1) {
pw = mul_mod(pw, pw, x);
if( pw == x-1 ) return true;
else if( pw == 1 ) return false;
}
return false;
}
bool Prime_Test(ll x) {
for(int i=0;i<10;i++)
if( x == prm[i] ) return true;
else if( x % prm[i] == 0 ) return false;
ll k = x-1;
while( k % 2 == 0 ) k /= 2;
for(int i=0;i<10;i++)
if( !Miller_Rabin(x, prm[i], k) ) return false;
return true;
}
ll gcd(ll x, ll y) {
return y == 0 ? x : gcd(y, x%y);
}
ll abs(ll x) {
return x < 0 ? -x : x;
}
ll min(ll x, ll y) {
return x < y ? x : y;
}
ll Pollard_Rho(ll x) {
for(int i=0;i<10;i++)
if( x % prm[i] == 0 ) return prm[i];
ll a = rand() % (x-2) + 2, b = a;
ll c = rand() % (x-1) + 1, d = 1;
while( d == 1 ) {
a = (mul_mod(a, a, x) + c)%x;
b = (mul_mod(b, b, x) + c)%x;
b = (mul_mod(b, b, x) + c)%x;
d = gcd(abs(a-b), x);
}
return d;
}
ll Find_Min_Div(ll x) {
if( x == 1 ) return INF;
if( Prime_Test(x) ) return x;
ll y = Pollard_Rho(x);
return min(Find_Min_Div(y), Find_Min_Div(x/y));
}
@总结 - 10@ Miller-Rabin素性测试与Pollard-Rho因数分解的更多相关文章
- Miller-Rabin 素性测试 与 Pollard Rho 大整数分解
\(\\\) Miller-Rabin 素性测试 考虑如何检验一个数字是否为素数. 经典的试除法复杂度 \(O(\sqrt N)\) 适用于询问 \(N\le 10^{16}\) 的时候. 如果我们要 ...
- Miller Rabin素数检测与Pollard Rho算法
一些前置知识可以看一下我的联赛前数学知识 如何判断一个数是否为质数 方法一:试除法 扫描\(2\sim \sqrt{n}\)之间的所有整数,依次检查它们能否整除\(n\),若都不能整除,则\(n\)是 ...
- POJ 2429 GCD & LCM Inverse(Miller-Rabbin素性测试,Pollard rho质因子分解)
x = lcm/gcd,假设答案为a,b,那么a*b = x且gcd(a,b) = 1,因为均值不等式所以当a越接近sqrt(x),a+b越小. x的范围是int64的,所以要用Pollard_rho ...
- POJ1811_Prime Test【Miller Rabin素数测试】【Pollar Rho整数分解】
Prime Test Time Limit: 6000MS Memory Limit: 65536K Total Submissions: 29193 Accepted: 7392 Case Time ...
- HDU1164_Eddy's research I【Miller Rabin素数测试】【Pollar Rho整数分解】
Eddy's research I Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Others ...
- Pollard Rho算法浅谈
Pollard Rho介绍 Pollard Rho算法是Pollard[1]在1975年[2]发明的一种将大整数因数分解的算法 其中Pollard来源于发明者Pollard的姓,Rho则来自内部伪随机 ...
- 关于素数:求不超过n的素数,素数的判定(Miller Rabin 测试)
关于素数的基本介绍请参考百度百科here和维基百科here的介绍 首先介绍几条关于素数的基本定理: 定理1:如果n不是素数,则n至少有一个( 1, sqrt(n) ]范围内的的因子 定理2:如果n不是 ...
- POJ 1811 Prime Test 素性测试 分解素因子
题意: 给你一个数n(n <= 2^54),判断n是不是素数,如果是输出Prime,否则输出n最小的素因子 解题思路: 自然数素性测试可以看看Matrix67的 素数与素性测试 素因子分解利用 ...
- 米勒罗宾素性测试(Miller–Rabin primality test)
如何判断一个素是素数 效率很高的筛法 打个表 (素数的倍数一定是合数) 就可以解决问题. 筛选法的效率很高,但是遇到大素数就无能为力了. 米勒罗宾素性测试是一个相当著名的判断是否是素数的算法 核心为费 ...
- POJ1811- Prime Test(Miller–Rabin+Pollard's rho)
题目大意 给你一个非常大的整数,判断它是不是素数,如果不是则输出它的最小的因子 题解 看了一整天<初等数论及其应用>相关部分,终于把Miller–Rabin和Pollard's rho这两 ...
随机推荐
- 轮播图js版&jQ版
JS版轮播图 html部分和css部分自己任意定 主要构成: 1,一个固定的框 超出框的部分隐藏 2,几张图片float:left 3,下部下原点,点击切换,切换到不同的张都有红色显示 4,左右两个大 ...
- POJ2182Lost Cows
Lost Cows Time Limit: 1000MS Memory Limit: 65536K Total Submissions: 11946 Accepted: 7690 Descri ...
- echarts 重新渲染(重新绘制,重新加载数据)等
- Ubuntu的网络共享
实际场景 公司项目中遇到一个场景:Ubuntu的主机上装了个4G卡(USB模式),需要将这个4G网共享给一个AP,使得所有连接AP的移动设备都可以通过4G上外网 方法很简单: 1. 将4G网口之外的另 ...
- HBuilderX生成本地打包App资源
http://ask.dcloud.net.cn/question/60254 概要 在HBuilderX中开发的应用可以提交到云端打包生成apk(Android平台)和ipa(iOS平台).如果本地 ...
- 学习笔记(3)---安装SVM问题及解决方法
1. LibSVM下载,安装 下载地址:http://www.csie.ntu.edu.tw/~cjlin/libsvm/,最新的版本是3.17 2. 入门 [heart_scale_label,he ...
- SpringBoot 使用 @Value 从 YAML文件读取属性
在 YAML中有如下配置 paypal: mode:live 在类中,通过 @Value属性读取 @Value("${paypal.mode}") private String m ...
- JQuery-- 链式编程、静态函数,自己制作jQuery插件
一.链式编程 为什么jQuery运行链式编程 ,让我们的代码(方法)连续不间断书写(连续调用)其实主要还是jQuery很多的函数执行完毕之后,都会返回一个jQuery对象 因为获取操作的时候,会返回获 ...
- 【JZOJ4899】【NOIP2016提高A组集训第17场11.16】雪之国度
题目描述 雪之国度有N座城市,依次编号为1到N,又有M条道路连接了其中的城市,每一条道路都连接了不同的2个城市,任何两座不同的城市之间可能不止一条道路.雪之女王赋予了每一座城市不同的能量,其中第i座城 ...
- awss3
import java.awt.image.BufferedImage; import java.io.File; import java.io.IOException; import java.io ...