Miller-Rabin质数测试 & Pollard-Rho质因数分解

考试遇见卡质因数分解的题了...活久见...毒瘤lun

于是就学了一发qaq

Pollard-Rho分解质因数的话需要依赖另一个算法.

Miller-Rabin质数测试

一个多项式时间的基于随机的质数测试算法.

玄学.

一些依赖的定理

费马小定理

若 \(p \in \mathbb P\) 则 \(\forall a\in \mathbb N^+, a^{p-1} \equiv 1\pmod p\)

它的逆命题基本上是真的, 于是我们可以把这个定理的逆命题作为判断质数的依据.

为啥说"基本上"是真的呢? 因为有些鬼畜的合数 \(n\) 满足 \(\forall a\in \mathbb N^+,a^{n-1}\equiv 1 \pmod n\). 这种鬼畜的合数被称为 Carmichael 数. 前三个 Carmichael 数是 \(561\), \(1105\) 和 \(1729\). OEIS数列编号 A002997 注意到它们可以挺小的...所以只用这个定理的逆命题测试有很大的问题.

二次探测引理

这个名字好像不是很通用...但是我们在这里先这么叫好了...

若 $p\in \mathbb P $ , 则 \(1\bmod p\) 不存在非平凡二次剩余.

我们尝试用它来作为判质数的条件, 那么就可以:

若 \(\forall a^2 \equiv 1 \pmod p, a\equiv \pm1 \pmod p\), 则 \(p \in \mathbb P\)

这个命题也基本上是真的...

但是也有些强伪质数满足上面这个条件...

不过没有合数能同时通过上面的两个测试.

具体用法是先令 \(n-1=2^tu\), 其中 \(u\) 是奇数. 然后随机一个值 \(a\), 把 \(a^u\) 平方 \(t\) 次. 如果某次平方出 \(1\) 了, 那么判断一下平方前的值是否是 \(\pm 1\). 如果是那么就算通过了二次探测. 通过二次探测后刚好求出了 \(a^{2^tu}\) 也就是 \(a^{n-1}\), 判一下是不是 \(1\) 就可以验证费马测试. 如果都是, 那么就称 \(n\) 通过了 \(a\) 的测试, 或者说 "\(a\) 不是 \(n\) 是合数的证据".

实现以及正确率

具体过程大概是

  1. 把乱七八糟的trival情况判掉, 比如 \(\le 2\) 啊, 偶数啊啥的.
  2. 进行 \(s\) 轮测试, 每次随机一个底数 \(a\) 进行上文所述的合并测试.

正确率大概要靠下面这个结论:

若 \(n\) 是一个奇合数, 那么 \(n\) 为合数的证据数量至少为 \(\frac{n-1}2\).

具体证明不详细展开了...算法导论上证了...

那么也就是说, 对于一个合数我们随机选择一个 \(a\) 然后刚好命中不是证据的 \(a\) 的概率小于 \(\frac 1 2\). 进行 \(s\) 轮测试后均命中非证据的概率小于 $2^{-s} $.

实际上不是证据的数的数量远远达不到 \(\frac {n-1} 2\). 能证明的最好结论是非证据的 \(a\) 最多有 \(\frac {n-1} 4\) 个. 而且这个界是能达到的.

所以实际正确率大概是 \(1-4^{-s}\). 一般取 \(s=10\) 效果就已经很好了.

一般用的时候要快速乘. 龟速乘(倍增加法)多半会GG.

代码实现

下面是板子题 LOJ #143. 质数判定 的AC代码

#include <bits/stdc++.h>

typedef long long intEx;

bool MillerRabin(intEx);
inline intEx RandInt(intEx,intEx);
inline intEx Mul(intEx,intEx,intEx);
inline intEx Pow(intEx,intEx,intEx); int main(){
for(intEx x;scanf("%lld",&x)!=EOF;){
if(MillerRabin(x))
puts("Y");
else
puts("N");
}
return 0;
} bool MillerRabin(intEx n){
static const int ROUND=10; if(n<2)
return false;
if(n==2)
return true;
if(!(n&1))
return false; int p=0;
intEx m=n-1;
while(!(m&1))
++p,m>>=1;
for(int k=0;k<ROUND;k++){
intEx pw=Pow(RandInt(1,n-1),m,n);
intEx last=pw;
for(int i=0;i<p;i++){
pw=Mul(pw,pw,n);
if(pw==1&&last!=1&&last!=n-1)
return false;
last=pw;
}
if(pw!=1)
return false;
}
return true;
} inline intEx Mul(intEx a,intEx b,intEx p){
intEx t=a*b-(intEx)((long double)a*b/p+0.5)*p;
return t<0?t+p:t;
} inline intEx RandInt(intEx l,intEx r){
static std::mt19937_64 mt(int(new int));
return std::uniform_int_distribution<intEx>(l,r)(mt);
} inline intEx Pow(intEx a,intEx n,intEx p){
intEx ans=1;
while(n>0){
if(n&1)
ans=Mul(ans,a,p);
a=Mul(a,a,p);
n>>=1;
}
return ans;
}

Pollard-Rho质因数分解

这个算法也是个概率算法. 不过它运行时间是期望的...

首先它也有个依赖的结论

生日悖论与生日攻击

首先是生日悖论. 一个 \(23\) 人的团体存在两人生日相同的概率要大于 \(50\%\). 一个推论是在 \(n\) 个值中随机选取若干个值, \(O(\sqrt n)\) 次后就会有很大概率产生某个值与之前选的值重复的情况.

大概证明可以长这样:

我们补集转化一下, 转而求选出的 \(k\) 个值都不同的概率. 显然它应该长这样:

\[P_n(k)=\prod_{i=0}^{k-1} \left(1-\frac i n\right)
\]

我们只要让这个值小于 \(\frac 1 2\) 就好了. 而由泰勒展开可得:

\[\exp(x)=1+x+\frac{x^2}{2!}+\frac{x^3}{3!}+\cdots
\]

那么对于 \(x> 0\) 有:

\[1+x < \exp(x)
\]

于是就有:

\[P_n(k)=\prod_{i=0}^{k-1}\left(1-\frac i n\right) < \prod_{i=0}^{k-1}\exp\left(-\frac i n\right) = \exp\left(-\sum_{i=0}^{k-1} \frac i n\right) =\exp\left(\frac{-k(k-1)}{2n}\right)
\]

那么我们只要让不等式右边小于 \(\frac 1 2\) 就好了. 那么我们有:

\[\exp\left(\frac{-k(k-1)}{2n}\right) < \frac 1 2
\]

两边取对数解一下就有:

\[k^2-k>2n\ln2
\]

又因为 \(\ln 2\) 是个常数, 于是 \(k=O(\sqrt n)\).

生日攻击是一种现代密码学攻击方法, 就是利用上面的生日悖论来制造Hash碰撞. 主要攻击对象为数字签名. 如果值域为 \(U\) 的话期望生成 \(\sqrt U\) 种不同信息即可发生碰撞. 如果产生Hash碰撞则可以进行伪造数字签名等等一系列攻击行为. Cookie也需要防范这种情况.

主要思想

设我们要对 \(n\) 进行因数分解, \(n\) 存在一个非平凡因子 \(p\) 且 \(p\le n/p\).

我们取一个次数至少为 \(2\) 且包含常数项的多项式函数 \(f(x)\) (比如 \(f(x)=x^2+c\), \(c\) 随机取), 设 \(g(x)=f(x)\bmod n\), 用 \(g\) 来生成伪随机数. 方法是随机选取一个初始值 \(r\) 不断将新的 \(x\) 代入 \(g(x)\) , 也就是 \(x_1=g(r), x_2=g(g(r)),x_3=g(g(g(r)))\) . 那么我们可以认为数列 \(\langle x_k\rangle\) 看上去是随机的. 而且 \(x_k=g(x_{k-1})\).

也就是说这个伪随机数列只和它的上一个输出有关. 从一个相同的初值可以得到相同的序列.

由于 \(p\mid n\), 所以数列 \(\langle x_k \bmod p\rangle\) 也满足这个性质, 即 \(x_k\equiv g(x_{k-1}) \pmod p\). 因为这个伪随机数生成器的状态只和上一个输出有关, 那么只要生成的新值命中了之前已经生成的值, 那么这个随机数列就会出现环.

由于 \(p\) 比较小, \(\langle x_k \bmod p \rangle\) 有很大概率比 \(\langle x_k \rangle\) 更早出现环 (只要新生成的 \(x\) 对 \(p\) 取模的值命中以前出现的值就会出现环, 而 \(p\) 不会超过 \(\sqrt n\)). 根据生日悖论, 在大约 \(\sqrt p\) 次随机后就会有很大概率出现. 如果出现这种情况, 那么我们就获得了两个值 \(x_a \equiv x_b \pmod p\). 于是它们之间的差是可以被 \(p\) 整除的. 我们取 \(p=\gcd (\left|x_a-x_b\right|,n)\) 即可.

至于这个判环的过程, 我们可以使用神奇的Floyd判环法. 建立两个哨兵 \(x_a\) 和 \(x_b\), 每次令 \(x_a\) 前进一步, 令 \(x_b\) 前进两步, 如果 \(x_a=x_b\), 则说明走到了环上.

但是我们并不知道 \(p\) 是多少, 所以并不能直接判断 \(x_a = x_b\) 来判断 \(\langle x_k \bmod p\rangle\) 是否进入环. 但是根据上文所述, 如果出现了 \(x_a\equiv x_b \pmod p\), 那么就一定出环了, 这个时候 \(p\mid \gcd (\left|x_a-x_b\right|,n)\), 我们便成功获得了一个 \(n\) 的非平凡因子.

不过有时候我们可能会手气不佳, 抽到的 \(c\) 和 \(r\) 生成的 \(\langle x_k \rangle\) 和 \(\langle x_k \bmod p\rangle\) 可能会同时进入环(显然前者不可能比后者进环更快, 最多同时). 不难发现这个环长也是 \(\sqrt p\) 级别的. 但是由于这时候找到的 \(x_a\) 和 \(x_b\) 是相等的, 于是并不能带来什么信息. 在这个时候有两种可能, 第一是 \(n\in \mathbb P\), 第二是脸黑.

普通的Pollard-Rho会到此为止而不提供任何信息.

期望的时间复杂度是 \(O(n^{1/4}\log n)\) 的, 那个 \(\log\) 是 \(\gcd\) 的时候来的.

具体实现

首先上文所述的过程只能用来找非平凡因子而非质因子.

我们把它和 Miller-Rabin 质数测试结合起来就可以进行质因数分解了. 大致流程如下:

  1. 用 Miller-Rabin 判断当前 \(n\) 是否是质数. 如果是, 丢进答案集合, 跑路.
  2. 随机选择一对 \(c\) 和 \(r\), 进行上文所述的测试过程.
  3. 如果找到了一个非平凡因子 \(p\), 递归求解 \(p\) 和 \(n/p\), 然后跑路.
  4. 否则, 承认自己脸黑, 返回第 2 步再来一次.

代码片段

void PollardRho(intEx n,std::vector<intEx>& factor){
if(MillerRabin(n))
factor.push_back(n);
else while(true){
intEx a,b,c=RandInt(0,n-1);
a=b=RandInt(0,n-1);
auto f=[=](intEx x){
return (Mul(x,x,n)+c)%n;
};
b=f(b);
while(a!=b){
intEx delta=std::abs(a-b);
delta=std::__gcd(delta,n);
if(delta!=1){
PollardRho(delta,factor);
PollardRho(n/delta,factor);
return;
}
a=f(a);
b=f(f(b));
}
}
}

洛谷板子过于SXBK地卡常于是没过TwT...我活该大常数...

LOJ板子更加SXBK地把数据出到了 \(1\times 10^{30}\)...跑路了QAQ...

一般应用没啥问题(吧)...

参考资料

[学习笔记] Miller-Rabin质数测试 & Pollard-Rho质因数分解的更多相关文章

  1. HDU 3864 D_num Miller Rabin 质数推断+Pollard Rho大整数分解

    链接:http://acm.hdu.edu.cn/showproblem.php? pid=3864 题意:给出一个数N(1<=N<10^18).假设N仅仅有四个约数.就输出除1外的三个约 ...

  2. POJ1811_Prime Test【Miller Rabin素数测试】【Pollar Rho整数分解】

    Prime Test Time Limit: 6000MS Memory Limit: 65536K Total Submissions: 29193 Accepted: 7392 Case Time ...

  3. HDU1164_Eddy&#39;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 ...

  4. STM32学习笔记(十) CAN通讯测试(环回模式)

    1.CAN通讯的理解 想学习CAN通讯,那么要对通讯协议有一定的认知.通讯协议是指通信双方对数据传送控制的一种约定.约定中包括对数据格式,同步方式,传输速度,传送步骤,检纠错方式以及控制字符定义等问题 ...

  5. AMQ学习笔记 - 17. 事务的测试

    概述 对事务机制进行测试. 测试实例 测试实例 结果预测 发送正常 3条消息入队 发送异常 0条消息入队 接收正常 3条消息出队 接收异常 0条消息出队 demo设计 设计图 测试分工 测试类 测试方 ...

  6. AMQ学习笔记 - 18. 持久化的测试

    概述 对持久化的有效性进行测试. 测试实例 测试实例 结果预测 持久化递送 重启ActiveMQ后,消息还在队列中 非持久化递送 重启ActiveMQ后,消息不在队列中 demo设计 jms-prod ...

  7. IGS_学习笔记08_IREP通过soapUI测试客户化Web Service调用(案例)

    20150819 Created By BaoXinjian

  8. IGS_学习笔记07_IREP通过页面测试客户化Web Service调用(案例)

    20150819 Created By BaoXinjian

  9. Spring学习笔记6——注解方式测试

    需要下载junit-4.12.jar和hamcrest-all-1.3.jar,将下载好的包导入到项目当中. 修改TestSpring, 并运行1. @RunWith(SpringJUnit4Clas ...

  10. spring框架学习笔记1:搭建测试

    Spring框架介绍: Spring框架涵盖了web.service.dao三层,本身是一个存放对象的容器 形象来说:Spring是项目中对象管家 Spring框架的两大核心思想:控制反转(IOC). ...

随机推荐

  1. Ubuntu/Debian下通过Apt-get简单安装Oracle JDK

    近几年本人对各种Arm小板,开发板不明原因中毒,基本以Linux系统为主,本篇文章以记录在32位Arm的Debian8上,通过Apt-get的简单命令安装Oracle JDK8并成功的记录. 1.首先 ...

  2. Python学习(一)——数据类型

    在大学学过一点python,只学了语法,关于实际应用却没怎么用过.现在用一些python的脚本来模拟webservices,挺好用的.这个语言,还是要好好学习学习了. 目前看着教材来的,这本教材,好像 ...

  3. TFS中设置任务中的“计划开始时间”为可编辑状态

    问题现象 如果使用TFS系统的默认模板CMMI新建团队项目,你会发现在网页浏览器中,任务工作项的"计划开始日期"和"计划结束日期"的类型是普通字符,并且不能修改 ...

  4. 【TFS 2017】使用浏览器上传文件(TFVC)或者编辑代码,错误提示TF14098,需要对文件有PendChange 权限

    从TFS 2015开始,微软在TFS系统中增加了一个非常吸引开发人员的功能,"快速代码编辑器" (Quick Code Editor).使用这个功能,你可以在任何安装了浏览器的设备 ...

  5. [ASP.NET]关于DOT NET的IIS配置LocalHost访问和127.0.0.1访问的区别

    项目上遇到一个问题跟大家分享下,配置的localhost地址本地无法访问接口,外网却可以访问,查其原因百度资料比较全面的解释 localhost与127.0.0.1的概念和工作原理之不同 要比较两个东 ...

  6. .Net Core2.0中使用ADO.NET

    学习了解.NET CORE有段时间,没有用其做项目的主要原因就是这么多年积累的类库兼容问题.今天就先解决SqlHelper的兼容性: 建立类库,目标框架选择.NET Core2.0,复制粘贴代码. 问 ...

  7. “借刀杀人”之CSRF拿下盗图狗后台

    最近我一个做贸易的朋友找到我,他发现自己拍摄的图片又被某个同行盗用了,而且是全站的图片基本都被盗用. 之前对方是引用他的图片链接,后面我给他做了防盗链解决了,现在对方是先下载图片,然后自己上传到服务器 ...

  8. linux安装git,linux安装jenkins

    首先是两个地址,分别是git的版本下载地址,jenkins的下载地址 https://mirrors.edge.kernel.org/pub/software/scm/git/ http://mirr ...

  9. SQLAlchemy 代码学习

    1.Dialect:英文含义为方言,这边只模块对不同的数据库的连接以及操作的实现. 2.engine:引擎,代表到数据库的一个连接,数据库自身有一个连接最大限制,不能超过这个限制.这里引擎可以连接多个 ...

  10. jQuery中的100个技巧(译)

    1.当document文档就绪时执行JavaScript代码. 我们为什么使用jQuery库呢?原因之一就在于我们可以使jQuery代码在各种不同的浏览器和存在bug的浏览器上完美运行. <sc ...