@总结 - 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这两 ...
随机推荐
- python类相关总结(持续更新)
__init__() 构造函数 __new__ () 在构造函数之前,用来创建对象的,返回值是一个对象,__init__指的是将__new__返回的对象作为self来传入函数中,后续参数两者都可以一样 ...
- win7使用经验-调整cmd窗口大小
分享一个调整cmd窗口的方法: 1.右击标题栏空白处,选择属性 2.选择布局栏 3.修改屏幕缓冲区大小的宽度和高度(自定义) 4.确定 注意:这里的缓冲区大小是指用户可拖动缩放的范围,并不是cmd窗口 ...
- redis为什么快
今天面试的时候被问到的一个问题,大致说了几点.回去又研究了一下. 大致分为几点: 1:Redis是纯内存数据库,一般都是简单的存取操作,线程占用的时间很多,时间的花费主要集中在IO上,所以读取速度快. ...
- 2018-2-13-wpf-如何使用-Magick.NET-播放-gif-图片
title author date CreateTime categories wpf 如何使用 Magick.NET 播放 gif 图片 lindexi 2018-2-13 17:23:3 +080 ...
- 洛谷P1470 最长前缀
P1470 最长前缀 Longest Prefix 题目描述 在生物学中,一些生物的结构是用包含其要素的大写字母序列来表示的.生物学家对于把长的序列分解成较短的序列(即元素)很感兴趣. 如果一个集合 ...
- Laravel 5.2 使用 JWT 完成多用户认证 | Laravel China 社区 - 高品质的 Laravel 开发者社区 - Powered by PHPHub
Json Web Token# JWT代表Json Web Token.JWT能有效地进行身份验证并连接前后端. 降地耦合性,取代session,进一步实现前后端分离 减少服务器的压力 可以很简单的实 ...
- TypeScript类型检查机制
类型推断 指不需要指定变量的类型,TS编译器可以根据某些规则自动推断出类型. 什么时候会有类型推断? 声明变量时没有指定类型 函数默认参数 函数返回值 ...... let a; // 这时自动推断为 ...
- 各种高度的区别及height、clientHeight、scrollHeight、offsetHeight的区分
1.height.clientHeight.scrollHeight.offsetHeight 我们来实现test中的onclick事件 function justAtest() { ...
- SPSS统计基础-均值功能的使用
SPSS统计基础-均值功能的使用 均值过程计算一个或多个自变量类别中因变量的子组均值和相关的单变量统计.您也可以获得单因素方差分析.eta 和线性相关检验. 统计量.合计.个案数.均值.中位数.组内中 ...
- 【JZOJ3885】【长郡NOIP2014模拟10.22】搞笑的代码
ok 在OI界存在着一位传奇选手--QQ,他总是以风格迥异的搞笑代码受世人围观 某次某道题目的输入是一个排列,他使用了以下伪代码来生成数据 while 序列长度<n do { 随机生成一个整数属 ...