2017-07-19 08:54 Amphetamine:能发一下代码吗?
  应我那位谜一样好友的邀约,我打算好好看一看Miller-Rabin和Pollard-Rho算法。很奇怪,各种地方有很多代码描述详细过程,但我仍旧很懵。也许是我太弱了,不能从那些“鱼龙混杂”的代码中找出本质上的共性。那么,我们现在来讨论一下吧。
  首先,大整数分解现在仍然是个世界级的难题,在“公共密钥”的研究上有着重要的作用。
!!先判断质数!!
试除法:原始的根号算法
  额。不想说了。正经一点。
Miller-Rabin:判断一个“p”是否是质数
  首先,快速幂是极为必要的。
  YYR说,这种东西是要靠RP的。但是,99.999%应该还是足以解决一些问题(或是大多数)了。
a^(p-1)≡1(mod p),gcd(a,p)=1
  费马小定理是极强的质数性质。只要找到一个a,与那个“p”不满足上述条件,就可以跳合数了?答案是肯定的。
  但是,如果找不到呢?就是质数?呵呵,很可惜啊。因为存在Carmichael数:无论取多少个a,有一个不满足,算我输。
  比如:561 = 11*51就是一个Carmichael数。
  为了99.999%,YYR给了一种相对靠谱的方法。
【PART 1:必要的第一次粗筛】
  先把2,3,5,7,11,13拿来筛一遍。
  这样理论上可以筛掉:1-(1-1/2)*(1-1/3)*(1-1/5)*(1-1/7)*(1-1/11)*(1-1/13)=1-19.18%=80.82%的数。
  当然,如果再加上17,19,23,29,理论上可以只会留下15.79%的数,也就是筛去了84.21%的数。
  肯定,我们会发现,这个筛率会趋于收敛。
  但是,让我们专门卡素数,怎么可能只用这种方法!!!
【PART 2:费马小定理与“二次探测定理”】
  在进入这一步之前,我们还不能确定这个“p”的身份。
  我们此时会先把p-1搞一搞,不停地除以2。直到发现p-1=2^k*t,而t是奇数。
  这样有何用意?且让我们分析。有一个定理:
 若a^2≡1(mod p),则a≡±1(mod p)
  YYR说到这里时,我说这很显然,他瞪大了眼睛。因为若p|(a^2-1),就有p|(a+1)(a-1)。这是经典的欧几里得引理,伟大的欧几里得就是以此证明了唯一分解定理。我们有(a+1)%p==0或(a-1)%p==0。即a≡±1(mod p)。
  然后,我们可以去随机地选取一些a。首先用快速幂算出a^t%p,然后把这个数自乘。得到了a^2t%p,然后再自乘。得到了a^4t%p,然后再自乘……我们可以发现,a^t%p自乘k次以后,就可以得到a^(2^k*t)%p,最后就是a^(p-1)%p了。
  在这样的一条路上,我们可以充分验证上述“定理”。前面k-1个数中只要出现了1,而它的“父亲”(它的“父亲”自乘得到了它)不是±1,那么就可以跳合数了。最后,第k个数即a^(p-1)%p若不为1,也可以跳合数。
  为什么这样效率很高?事实证明,若“p”通过一次测试,不是素数的概率为25%。若“p”通过x次测试,则不是素数的概率为1/(4^x)。当x=5时,素数误判的概率为1/(4^5)=1/1024=0.10%,已经直逼99.90%了。
  而假如选取了4个a为2,3,5,7,则在2.5*10^13以内唯一一个失误的数为3215031751。
  所以说,选几个a比较好?实践证明,这种东西可以悠着来。
【小结】
  1.读入一个"p"。
  2.用2,3,5,7,11,13粗筛,发现整除直接跳合数。
  3.把p-1搞一搞,不停地除以2。得到奇数t,和一个数k。p-1=2^k*t。
  4.随机地找一个数a,算出x=a^t。
  5.last=x,x=x*x%p,若x==1且last!=1且last!=p-1,则跳合数。该步骤重复k次。
  6.现在x=p-1,若x!=1,则跳合数。
  7.按心情回到第4步,99.99%与99.9999%在神犇眼里有着本质区别。
!!然后再!!
FERMAT:平方差
  如果是偶数,就把它釜底抽薪,变成奇数。
  对于一个奇数N,若不为质数,则设N=c*d,明显c、d均为奇数。
  不妨设c>d,令a=(c+d)/2,b=(c-d)/2,很明显,N=a*a-b*b。这就是了。
  我们枚举一个a,有a*a-N=b*b,a>=sqrt(N),若a*a-N是一个完全平方数,就可以递归了。
  但是,很明显。这样也是枚举,效率很低,得不偿失。
单纯的POLLARD RHO:生日悖论和判圈
  精彩现在继续。有一篇文章很好:点击这里
  单纯的Pollard Rho算法非常有趣,容易理解。虽然不是目前最快的算法,但它要比试除法快上多个量级。实现它的思想同样可以用于其他地方。
  该算法最早于1975年由John M. Pollard提出,而Richard Brent于1980年提出了改进版本。Wikipedia上说得特别清楚。
  上一个FERMAT算法中,我们试图分解N=c*d。那么,现在我们会继续这一思路。假设N只有c和d两个互异质因子,然后在[2,N]中随机出一个数,概率是2/(N-1),很小。
  但是,随机选取23个人,他们中存在相同生日的概率大于50%。
  这就是生日悖论。
  但为什么是对的?如果是对的,然后呢?
【生日悖论的正确性】
  我们设现在随机选取n个人(n<=365),希望能够算出“存在相同生日”的概率。请注意,这个事件是指,我们能够从中找出至少两个生日一样的人。
  设该概率为P,很显然它与“不存在相同生日”的概率Q之和为1。而对于Q,我们可以发现,如果人一个一个加入。对于第2个人,要让他与第1个人不同,概率为1-1/365;对于第3个人,要让他与第1个人和第2个人不同,概率为1-2/365;对于第4个人,要让他与第1个人、第2个人和第3个人不同,概率为1-3/365;对于第5个人,要让他与第1个到第4个人不同,概率为1-4/365;……;最后,对于第n个人,要让他与第1个到第n-1个人不同,概率为1-(n-1)/365。最后,即可得Q=(1-1/365)*(1-2/365)*(1-3/365)……(1-(n-1)/365)。
  中间有一个不等式:1+x<=ex(x≠0),我不想证了。
  然后,就有Q=e(-1/365)*e(-2/365)*……*e(-(n-1)/365)=e(-n(n-1)/(2*365))
  于是我们就可以算出,当n>=23时,他们中存在相同生日的概率大于50%。
  其实,如果说一年有N天,那么只要n>=sqrt(N),他们中存在相同生日的概率就会大于50%。(详见《算导》黑书)
【然后呢?】
  现在,我们随机从[2,N]中选出n个数,当好刷出c或d的概率很小,但是如果它们两两作差呢?
  使用类似的思路,我们可以猜(当然也可以用完全相同的证法),知道n~sqrt(N)时,概率约为50%。其实仔细想想也很有道理,因为sqrt(N)个数任意组队,有N/2种方案。
  虽然很有道理,但是不可能直接两两作差,因为概率还是很低。但是,你可能已经想到了。
  如果是gcd(|a-b|,N)呢?于是,事情变得很好看了。
  因为N=c*d,c和d都很稀有,但是c和d的倍数便是一波大军。

所以,一个简单的策略如下:

  • 在区间[2,N-1]中随即选取n个数,x1,x2, … … , xn
  • 判断是否存在gcd(|xi-xj| ,N) >1, 若存在,gcd(|xi-xj| ,N) 是N的一个因子 (c 或 d)
  这是很可靠的。如果N有多个因子,也能用类似于FERMAT的方法递归解决。
  但是我们算一算。但是很早就出现了一个问题,我们需要选取大约N1/4个数,这个数量太大了,以至于我们并不能将其存放在内存中。
  然后,怎么去造这n个数呢?
  PollardRho说,很简单。
【流水线】
  我们发现,n个数如果先造出来,再去判断实在太傻。于是PollardRho告诉我们,2个数就够了。
  我们可以用不断地生成伪随机数。然后像流水线一样判断。
  有一个随机函数特别棒,在此举例:f(x)=(x^2+a)%N
  这个a已经被你给定了,可以为1当然也能是rand(),只是中途一般不作变化。
  代码如下:
 int find_factorplus(int N) {
a = ;
for( int i= ; i <= ; i++ ) {
b = f(a);
p = GCD( abs( b - a ) , N);
if( p > ) return p;//Found factor: p
a = b;
}
return ;//Failed. :-(
}

  似乎很玄学,但是实际效果确实很棒。但不好的是,伪随机数有着玄学般的循环节。

【floyd来判圈】
  对于循环节。你一定会觉得,用个vis数组就好了。似乎很可以,只要你存得下,不揪心就好。
  刘汝佳先生说:想象一下,假设有两个小孩子在一个“可以无限向前跑”的跑道上赛跑,同时出发。但其中一个小孩的速度是另一个的两倍。如果跑道是直的,跑得快的小孩永远在前面;但如果跑道有环,则跑得快的小孩将“追上”跑得慢的小孩。
  于是就这样了。这叫做“floyd判圈”算法。
 int find_factorplus(int N) {
a = ;
b = a;
do {
a = f(a);//a runs once
b = f(f(b));//b runs twice as fast
p = GCD( abs( b - a ) , N);
if( p > ) return p;//Found factor: p
} while( b != a );
return ;//Failed. :-(
}

  这样,我们就可以把退出条件温和化,只要发现有环,那就只有退出了。而不是暴力地把i从1 for 到 1,000,000。

  如果算法失败了,我们只需要找到一个新的伪随机函数f(x)或是一个新的a就好了。不过请放心,大多数时候你并不会失败。

  最后说一下,代码中a的初值是2,在实际生活中,你并不需要那么讲究,rand()一个也是不错的选择。

“最后”的POLLARD RHO:当与Miller-Rabin发生反应

  我们可以发现pollard rho直到现在都还没有与Miller-Rabin有任何联系,但马上就不是了。

  对于pollard rho,它可以在Θ(sqrt(p))的时间复杂度内找到N的一个小因子p,这一点我们曾论证过。可见,如果N的因子很多、因子值很小的整数N来说,效率是很优异的。

  但是,如果反过来呢?如果说N是大整数,恰好因子很少、因子值很大?

  例如,N=2*p,p为质数。你立马发现,N有一个因子2,然后你试图去解决p。然后,这个很优秀的算法成了根号算法。而且直到最后,你都很难判断这个p是否真的不可约。

  但是,一旦拥有Miller-Rabin,一切便都已解决。

  我们现在可以分析一下复杂度。N的质因子中,超过sqrt(N)的有且仅有一个。这样,即使运气极差,也能有相当的保障。

!!最后总结一下!!

  斯堪福说,总结是好习惯。

  对于Miller Rabin,我们需要一个快速幂,一个快速乘。先用2,3,5,7,11,13粗筛一遍,再将p的2抽尽,然后随机地选取一些数进行二次探测与费马小定理检验。

  对于Pollard Rho,我们需要一个伪随机函数f,一个常数a,一个gcd,一个abs。使用floyd判圈算法。找到一个因子后递归解决,中间判断是否是质数。如果是,做记录。

  当我们在做大数质因子分解时,质因子记录完毕后,我们常常会发现这是无序的。这就需要进行一下排序,然后离散化处理出每个质因子出现的次数。这样就解决了。就真的解决了。

致谢
  yy儒学长(NOI AG THU)
  Amphetamine
  IDY002
  BIGBALLON
参考
  刘汝佳《算法竞赛入门经典》《算法竞赛入门经典训练指南》
  林厚从、王宏《信息学奥赛值数学一本通》
  丁尧尧idy002《信息学竞赛中的数学知识小结》
  BIGBALLON《A Quick Tutorial on Pollard's Rho Algorithm》

大数质因解:浅谈Miller-Rabin和Pollard-Rho算法的更多相关文章

  1. Miller Rabin素数检测与Pollard Rho算法

    一些前置知识可以看一下我的联赛前数学知识 如何判断一个数是否为质数 方法一:试除法 扫描\(2\sim \sqrt{n}\)之间的所有整数,依次检查它们能否整除\(n\),若都不能整除,则\(n\)是 ...

  2. Pollard rho算法+Miller Rabin算法 BZOJ 3668 Rabin-Miller算法

    BZOJ 3667: Rabin-Miller算法 Time Limit: 60 Sec  Memory Limit: 512 MBSubmit: 1044  Solved: 322[Submit][ ...

  3. Pollard Rho算法浅谈

    Pollard Rho介绍 Pollard Rho算法是Pollard[1]在1975年[2]发明的一种将大整数因数分解的算法 其中Pollard来源于发明者Pollard的姓,Rho则来自内部伪随机 ...

  4. 浅谈DFS,BFS,IDFS,A*等算法

    搜索是编程的基础,是必须掌握的技能.--王主任 搜索分为盲目搜索和启发搜索 下面列举OI常用的盲目搜索: 1.dijkstra 2.SPFA 3.bfs 4.dfs 5.双向bfs 6.迭代加深搜索( ...

  5. 计蒜客 18487.Divisions-大数的所有因子个数-Miller_Rabin+Pollard_rho-超快的(大数质因解+因子个数求解公式) (German Collegiate Programming Contest 2015 ACM-ICPC Asia Training League 暑假第一阶段第三场 F)

    这一场两个和大数有关的题目,都用到了米勒拉宾算法,有点东西,备忘一下. 题目传送门 F. Divisions 传送门 这个题是求一个数的所有因子个数,但是数据比较大,1e18,所以是大数的题目,正常的 ...

  6. MMORPG战斗系统随笔(二)、浅谈场寻路Flow Field PathFinding算法

    转载请标明出处http://www.cnblogs.com/zblade/ 今天给大家带来一篇游戏中寻路算法的博客.去年,我加入一款RTS的游戏项目,负责开发其中的战斗系统,战斗系统的相关知识,属于游 ...

  7. 浅谈双流水线调度问题以及Jhonson算法

    引入:何为流水线问题 有\(n\)个任务,对于每个任务有\(m\)道工序,每个任务的\(m\)道工序必须在不同的m台机器上依次完成才算把这个任务完成,在前\(i-1\)道工序完成后才能去完成第\(i\ ...

  8. 【转】浅谈对主成分分析(PCA)算法的理解

    以前对PCA算法有过一段时间的研究,但没整理成文章,最近项目又打算用到PCA算法,故趁热打铁整理下PCA算法的知识.本文观点旨在抛砖引玉,不是权威,更不能尽信,只是本人的一点体会. 主成分分析(PCA ...

  9. 关于素数:求不超过n的素数,素数的判定(Miller Rabin 测试)

    关于素数的基本介绍请参考百度百科here和维基百科here的介绍 首先介绍几条关于素数的基本定理: 定理1:如果n不是素数,则n至少有一个( 1, sqrt(n) ]范围内的的因子 定理2:如果n不是 ...

随机推荐

  1. hadoop1.0 和 Hadoop 2.0 的区别

    1.Hadoop概述 在Google三篇大数据论文发表之后,Cloudera公司在这几篇论文的基础上,开发出了现在的Hadoop.但Hadoop开发出来也并非一帆风顺的,Hadoop1.0版本有诸多局 ...

  2. Python装饰器、内置函数之金兰契友

    装饰器:装饰器的实质就是一个闭包,而闭包又是嵌套函数的一种.所以也可以理解装饰器是一种特殊的函数.因为程序一般都遵守开放封闭原则,软件在设计初期不可能把所有情况都想到,所以一般软件都支持功能上的扩展, ...

  3. 图解slub

    1.前言 在Linux中,伙伴系统(buddy system)是以页为单位管理和分配内存.但是现实的需求却以字节为单位,假如我们需要申请20Bytes,总不能分配一页吧!那岂不是严重浪费内存.那么该如 ...

  4. Docker 教程(一)

    Docker 使用客户端-服务器 (C/S) 架构模式,使用远程API来管理和创建Docker容器. Docker 容器通过 Docker 镜像来创建. 容器与镜像的关系类似于面向对象编程中的对象与类 ...

  5. c/c++ 网络编程 bind函数

    网络编程 bind函数 bind的作用是确定端口号. 正常处理都是先bind,然后listen 如果不bind,直接listen,会是什么结果? 内核会自动随机分配一个端口号 例子: #include ...

  6. Swift JSON字符串和字典以及数组的互转

    1.JSONString转换为字典 // JSONString转换为字典 func getDictionaryFromJSONString(jsonString:String) ->NSDict ...

  7. Dijango学习_02_极简本地博客创建

    二. Python 自带SQLite3数据库,Django默认使用SQLite3数据库,如果使用其它数据库可以在settings.py文件中设置. DATABASES = { 'default': { ...

  8. 雨后清风教你如何在Windows 7中对硬盘进行分区

    磁盘分区是将硬盘驱动器分成多个逻辑单元.人们通常不会选择对硬盘进行分区,但它有很多好处.主要是,通过对磁盘进行分区,您可以将操作系统与数据分开,从而减少数据损坏的可能性. 磁盘分区方法 打开“计算机管 ...

  9. Python(五)模块

    本章内容: 模块介绍 time & datetime random os sys json & picle hashlib XML requests ConfigParser logg ...

  10. linux环境快速编译安装python3.6

    一.下载python3源码包 cd /tmp/wget https://www.python.org/ftp/python/3.6.2/Python-3.6.2.tgz 二.下载python3编译的依 ...