分解因数算法

文章目录

  • 分解因数算法
    • 朴素算法
      • 唯一分解定理优化
    • Pollard Rho算法
      • 0.生日悖论
      • 1.如何产生随机数
      • 2.如何检查已经陷入了循环?
      • 3.最大公因数优化
    • 完整代码
    • BUUCTF-Alice和Bob

给定一个正整数N,求其所有质因数

朴素算法

最贴近人的想法,就是从2开始一直到N\sqrt{N}N​,用N取除以这些数,只要是能够整除就是因数,然后判定该因数是否是素因数

for(int i=2;i<=sqrt(N);i++){if(N%i==0){if(isPrime(i)){//i是N的素因数}}
}

唯一分解定理优化

任何正整数N都可以分解为若干个素数的乘积
N=p1k1p2k2...pnknN=p_1^{k_1}p_2^{k_2}...p_n^{k_n} N=p1k1​​p2k2​​...pnkn​​
比如48=24×3148=2^4\times 3^148=24×31

对于刚才纯朴素的做法,我们在找到222是48的素因数之后,如果不加处理后来又会发现4是48的因数但不是素因数,

可以将2k2^k2k从N的所有因数中去掉

int temp=N;
for(int i=2;i<=sqrt(temp);++i){if(temp%i==0){//i是temp的素因数,之所以没有判断素性就直接说是素因数,是因为有下面这个while保证while(temp%i==0){//实际上相当于筛子,筛去了i^k因子temp/=i;//由于temp%i=0因此i整除temp,此举将temp中所有i因数即其幂去掉}}
}
if(temp!=1){//最后剩下的temp也是N的素因数
}

只判断temptemp%i==0temp貌似只能说明iii是temptemptemp的因数,为什么还能将得到更严格结论"iii是temptemptemp的素因数"呢?

假设iii是temptemptemp的合因数

根据唯一分解定理,i=p1p2...pn,pi为素数i=p_1p_2...p_n,p_i为素数i=p1​p2​...pn​,pi​为素数

显然对于一个合数,i至少有两个素因数,不妨设为1<p1,p2<i1<p_1,p_2<i1<p1​,p2​<i

则p1∣i,i∣temp⇒p1∣tempp_1|i,i|temp\Rightarrow p_1|tempp1​∣i,i∣temp⇒p1​∣temp

那么p1p_1p1​是temptemptemp的比i小的素因数

现在将时光回溯到i=p1i=p_1i=p1​时,我们的while循环做了一件事:

     while(temp%p1==0){temp/=p1;}

此举保证了出while循环的时候,temp不再含有p1p1p1因子,也就是p1∤tempp1 \nmid tempp1∤temp

现在时光前进到i=tempi=tempi=temp时,我们刚刚得到结论p1∣tempp_1| tempp1​∣temp

于是产生了矛盾

因此假设不成立,即iii是temptemptemp的素因数

Pollard Rho算法

ρ\rho ρ

算法思想

朴素算法是从[2,N][2,\sqrt{N}][2,N​]里面遍历所有数找N的因数

而现在我们不轮着找,而是挑着找,就类似于抽样检测.

用随便挑的两个数的差去找,啥意思呢?

∀x1,x2∈[2,N]\forall x_1,x_2\in [2,\sqrt{N}]∀x1​,x2​∈[2,N​],我们求∣x2−x1∣|x_2-x_1|∣x2​−x1​∣,然后用这个数去试是否是NNN的因数

为啥不直接选一个∀x∈[2,N]\forall x\in[2,\sqrt{N}]∀x∈[2,N​],然后用这个x去比划N,而是选两个x的差去比划呢?

0.生日悖论

这个只能用概率解释了,生日悖论问题

1.考虑从[1,1000][1,1000][1,1000]上选k个数,求任意两个数的差不等于x的概率

2.考虑一伙子k个人,任意两个人生日不同的概率(生日悖论)

上面那个我不会求,下面这个我会

假设一年有n天,求k个人生日都不相同的概率

第一个人的生日可以在n天中的任意一天,有n种情况

第二个人的生日不能和第一个人相同,有n-1种情况

第三个人的生日不能和前两个人相同,有n-2种情况

第k个人的生日不能和前k-1个人相同,有n-(k-1)种情况

因此k个人生日都不相同的概率为
P=n(n−1)(n−2)...(n−(k−1))nk=nn×n−1n×n−2n×...×n−k+1n=1×(1−1n)×(1−2n)×...×(1−k−1n)P=\frac{n(n-1)(n-2)...(n-(k-1))}{n^k}\\ =\frac{n}{n}\times \frac{n-1}{n}\times \frac{n-2}{n}\times ...\times \frac{n-k+1}{n}\\ =1\times (1-\frac{1}{n})\times (1-\frac{2}{n})\times ...\times (1-\frac{k-1}{n}) P=nkn(n−1)(n−2)...(n−(k−1))​=nn​×nn−1​×nn−2​×...×nn−k+1​=1×(1−n1​)×(1−n2​)×...×(1−nk−1​)
这个数可不好求,但是可以放缩,用到的是高中就见过的老伙计了ex≥x+1e^x\ge x+1ex≥x+1
1−in≤e−in1-\frac{i}{n}\le e^{-\frac{i}{n}} 1−ni​≤e−ni​
因此
P=1×(1−1n)×(1−2n)×...×(1−k−1n)≤e−1n×e−2n×...×e−k−1n=e−1n∑i=1k−1i=e−k(k−1)2nP=1\times (1-\frac{1}{n})\times (1-\frac{2}{n})\times ...\times (1-\frac{k-1}{n})\\ \le e^{-\frac{1}{n}}\times e^{-\frac{2}{n}}\times ...\times e^{-\frac{k-1}{n}}\\ =e^{-\frac{1}{n}\sum_{i=1}^{k-1} i}\\ =e^{-\frac{k(k-1)}{2n}} P=1×(1−n1​)×(1−n2​)×...×(1−nk−1​)≤e−n1​×e−n2​×...×e−nk−1​=e−n1​∑i=1k−1​i=e−2nk(k−1)​
即k个人生日都不同的概率小于等于e−k(k−1)2ne^{-\frac{k(k-1)}{2n}}e−2nk(k−1)​

那么存在两个人生日相同的概率为1−e−k(k−1)2n1-e^{-\frac{k(k-1)}{2n}}1−e−2nk(k−1)​

忙活半天求这个概率的缩放值有啥目的呢?

为了证明当人稍微多一点,存在两个人生日同一天的概率就会变大,并且是超出直觉的大

比如n=365,求存在两个人生日相同的概率达到50%,至少需要几个人
1−e−k(k−1)730≥121-e^{-\frac{k(k-1)}{730}}\ge \frac{1}{2}\\ 1−e−730k(k−1)​≥21​

k(k−1)≥730ln⁡2=505.99744...≈506k(k-1)\ge 730\ln 2=505.99744...\approx 506 k(k−1)≥730ln2=505.99744...≈506

而23∗22=50623*22=50623∗22=506

因此k≥23k\ge 23k≥23

即至少23个人就可以

想想这个事情,随便路上找一个人比较生日,相同的概率只有1/365

但是随便找22个人都问一遍,就会有一半的概率,其中至少有一个人和我同生日

放在这里,求因数,怎么操作呢?

我们要产生一系列[2,N][2,\sqrt{N}][2,N​]上的随机数,然后每次取一对做差检验这个差是不是N的因数

1.如何产生随机数

构造随机数函数f(x)=(x2+c)modnf(x)=(x^2+c)\mod nf(x)=(x2+c)modn,作用是生成随机数序列,其中c是随便指定的一个数,然后随便选取一个正整数x0x_0x0​作为起点
x1=f(x0)=(x02+c)modnx2=f(x1)=(x12+c)modn...xm=f(xm−1)=(xm−12+c)modnx_1=f(x_0)=(x_0^2+c)\mod n\\ x_2=f(x_1)=(x_1^2+c)\mod n\\ ...\\ x_m=f(x_{m-1})=(x_{m-1}^2+c)\mod n x1​=f(x0​)=(x02​+c)modnx2​=f(x1​)=(x12​+c)modn...xm​=f(xm−1​)=(xm−12​+c)modn
计算若干次后一定会陷入循环

为什么一定会陷入循环?

假设不会陷入循环,

则每一次计算都会得到一个与前面所有xix_ixi​都不同的x值,但是∀xi∈[0,n−1]\forall x_i\in[0,n-1]∀xi​∈[0,n−1],即xix_ixi​顶多有n种取值,那么至多计算n次之后,由抽屉原理,一定会又两个xxx值相同.

假设是xj=f(f(f(f(...f(xi)))))x_j=f(f(f(f(...f(x_i)))))xj​=f(f(f(f(...f(xi​)))))即xix_ixi​经过若干次迭代计算之后得到xjx_jxj​,并且xi=xjx_i=x_jxi​=xj​

那么再计算xj+1=f(xj)=f(xi)=xi+1x_{j+1}=f(x_j)=f(x_i)=x_{i+1}xj+1​=f(xj​)=f(xi​)=xi+1​

以此类推可以得到xj+k=xi+kx_{j+k}=x_{i+k}xj+k​=xi+k​

显然陷入了循环

2.如何检查已经陷入了循环?

利用链表上快慢指针检查是否有环的思想

一个400米的圆形跑道,

两个傻子不知道跑道是⭕形状的,只知道沿着跑到一直跑

两个傻子同时同地起跑,快傻子每秒一米,慢傻子每秒半米,

相对速度差为每秒半米,那么经过800秒,相对路程差为800×0.5=400m800\times 0.5=400m800×0.5=400m,

快傻子正好比慢傻子快一圈,两人再次相遇表明"地球是圆的"即跑道是套圈的

在这里令xi=f(f(xi−1)),xj=f(xj−1)x_i=f(f(x_{i-1})),x_j=f(x_{j-1})xi​=f(f(xi−1​)),xj​=f(xj−1​),xix_ixi​就是那个快傻子,xjx_jxj​就是那个慢傻子,他俩就是那个海尔兄弟,舒克贝塔

并且两个傻子从并排起跑到再次相遇,他们的相对路程经理了一个从0到400的过程,遍历了[0,400][0,400][0,400]间的所有值

那么在这里两个快慢指针的差也就遍历了f(x)f(x)f(x)函数能够产生的,在圈上的,所有随机数的差

为啥说"在圈上的"

这里陷入循环不一定是从x0x_0x0​就在循环中,

可能是中间某个状态才进入循环

就类似于一个ρ\rhoρ,一开始的xxx值是在ρ\rhoρ的"腿"上的,后来的xxx才进入ρ\rhoρ的圈上

这个方法叫做"Floyd判环"

当两个快慢指针再次相遇的时候,说明所有f(x)f(x)f(x)能够产生的圈上的随机数的差都不是NNN的因数

那么只能说明选的随机数函数真的太逊了

然后对f(x)=x2+cf(x)=x^2+cf(x)=x2+c进行修改比如修改一个初始值x0′x_0'x0′​或者修改ccc或者直接修改函数表达式f(x)=x2+x+cf(x)=x^2+x+cf(x)=x2+x+c等等

3.最大公因数优化

刚才我们只是检查∣xi−xj∣|x_i-x_j|∣xi​−xj​∣能不能整除NNN,即检查∣xi−xj∣|x_i-x_j|∣xi​−xj​∣是不是NNN的因数

但是由于gcd(∣xi−xj∣,N)∣Nandgcd(∣xi−xj∣,N)∣∣xi−xj∣gcd(|x_i-x_j|,N)|N\ and\ gcd(|x_i-x_j|,N)||x_i-x_j|gcd(∣xi​−xj​∣,N)∣N and gcd(∣xi​−xj​∣,N)∣∣xi​−xj​∣,那么只要∣xi−xj∣|x_i-x_j|∣xi​−xj​∣是N的因数,那么gcd(∣xi−xj∣,N)gcd(|x_i-x_j|,N)gcd(∣xi​−xj​∣,N)也是N的因数

并且用最大公因数去和N比划的机会只会比只用∣xi−xj∣|x_i-x_j|∣xi​−xj​∣去直接比划N更多

对于∣xi−xj∣|x_i-x_j|∣xi​−xj​∣可以要求放松一点,只要是∣xi−xj∣|x_i-x_j|∣xi​−xj​∣的任何非1真因数能够整除NNN就算是给N找到了一个因数

完整代码

#include <iostream>
#include <algorithm>
using namespace std;long long gcd(const long long &a, const long long &b) {if (b == 0)return a;return gcd(b, a % b);
}long long f(const long long &x0, const long long &c, const long long &mod) {return (x0 * x0 % mod + c) % mod;
}long long Pollard_Rho(const long long &N) {long long c = rand() % (N - 1) + 1; //c∈[1,N-1]long long slow = f(0, c, N);long long fast = f(f(0, c, N), c, N);while (slow != fast) {long long d = gcd(abs(slow - fast), N);if (d > 1)return d;//找到N的真因数d,返回slow = f(slow, c, N);fast = f(f(fast, c, N), c, N);}return N;//失败
}
long long N;
int time = 0;int main() {while (cin >> N) {cout << ++time << ":";cout << Pollard_Rho(N) << endl;}
}

随便给几个数,这个程序的运行结果是怎样的呢?

8
1:8
8
2:4
8
3:8
8
4:4
8
5:8
8
6:8
9
7:9
9
8:9
9
9:9
9
10:3

可以发现,当N=8的时候,第一次失败了,第二次成功找到4,第三次又失败了,第四次又找到4,第五六次都失败了

当N=9,第九次失败,第十次找到三

如果真的失败了,多选上几个c去世或者改一下f函数去世.

如果试了很多次都失败了,就可以从概率上极大似然地认为N没有真因子了

BUUCTF-Alice和Bob

分解质因数985547997679855479976798554799767

还是运行刚才的程序

98554799767
1:98554799767
98554799767
2:101999

第二次就找到了真因子101999,我们只能确定他是985547997679855479976798554799767的因子,但是101999101999101999是不是素数还没有检查

但是吧101999101999101999用2,3,5,13,17这一些除一下都除不开,长成这样很像一个素数,98554799767/101999=96623398554799767/101999=96623398554799767/101999=966233,直接提交101999和966233就通过了

Pollard Rho算法分解因数相关推荐

  1. 大整数分解——Pollard Rho算法

    延续上一篇,这次来讲一讲大整数分解算法的应用. 要解决的问题很简单,对一个整数进行分解质因数. 首先还是效率非常低的暴力算法,相信大家都会,不多提. 和上次一样,当数达到非常大的时候,分解将变得非常困 ...

  2. c语言用rho函数求复数模长,Pollard Rho 算法简介

    $\text{update 2019.8.18}$ 由于本人将大部分精力花在了cnblogs上,而不是洛谷博客,评论区提出的一些问题直到今天才解决. 下面给出的Pollard Rho函数已给出散点图. ...

  3. BZOJ 5330 Luogu P4607 [SDOI2018]反回文串 (莫比乌斯反演、Pollard Rho算法)

    题目链接 (BZOJ) https://www.lydsy.com/JudgeOnline/problem.php?id=5330 (Luogu) https://www.luogu.org/prob ...

  4. 素数判定质因数分解(数论)(Miller Rabin)(Pollard Rho)

    太玄学了! 我真的被概率的魅力折服了.此前我认为1便是1,0.9999999999-便是0.9999999999-. 但实际上它们有着千丝万缕的关系. 试想,如果一件事发生的概率是0.99999999 ...

  5. 因数分解 Pollard rho

    因数分解 Pollard rho 算法思路 随机生成两个数a,ba,ba,b,然后求gcd⁡(n,a−b)\gcd\pod{n,a-b}gcd(n,a−b),如果其值不为111,则这个数就是nnn的一 ...

  6. 【快速因数分解】Pollard's Rho 算法

    算法目的 给一个数n,快速提取n的一个因数. 算法根据:生日悖论 讲生日悖论之前,先看一个东西. 给出[1-1000]的数,从中任意选出一个数为k的概率是110001\over 100010001​. ...

  7. 2.2 基本算法之递归和自调用函数 1751 分解因数 python

    http://noi.openjudge.cn/ch0202/1751/ """ 2.2 基本算法之递归和自调用函数 1751 分解因数 http://noi.openj ...

  8. 64位以内Rabin-Miller 强伪素数测试和Pollard rho 因数分解解析

    在求解POJ1811题Prime Test中应用到的两个重要算法是Rabin-Miller强伪素数测试和Pollard r因数分解算法.前者可以在的时间内以很高的成功概率判断一个整数是否是素数.后者可 ...

  9. 整数的素因子分解:Pollard rho method

    参考: 1.CLRS<算法导论> 2.http://www.csh.rit.edu/~pat/math/quickies/rho/#algorithm Pollard rho方法是随机算法 ...

  10. 简述大数分解算法Pollard Rho和Pollard p-1

    大数分解问题其实至今都是一个世界级难题,最常见的分解法是从2一直找到sqr(N),作为一个密码学专业的学生,每次看到别人这么做来进行因子分解,自己都控制不住想要制止他,因为这个算法的效率简直太太太太太 ...

最新文章

  1. Java项目:学生学科竞赛管理管理系统设计和实现(java+springboot+ssm+maven)
  2. 浅谈WebService的调用转
  3. 你需要知道的12个Git高级命令
  4. C++ string::size_type
  5. A Simple Note on P4FPGA: A Rapid Prototyping Framework for P4
  6. php 实现柱状图,PHP动态柱状图实现方法_PHP
  7. PySpark(一)
  8. LIS最长上升子序列详解+模板(dp和二分做法)
  9. 基于html + css + js完成淘宝网首页效果
  10. DOCTYPE声明——标准模式与怪异模式的区别
  11. Android手机减少微信步数,iPhone微信步数竟然比安卓少很多,原因已明确!
  12. 电脑硬件升级——笔记本更换更大容量的固态硬盘,并进行系统迁移
  13. 求任意两个整数的最大公因数
  14. springcloud springboot nacos版本对应
  15. 雨中的尾巴(线段树合并+树上差分)
  16. 亚马逊选品是单一产品好还是诸多产品好呢?
  17. 2009雷人语录最全
  18. Windows10常用快捷键汇总
  19. WebSSH安装和开机自启设置
  20. Revit二次开发入门关键

热门文章

  1. 如何在华为交换机上查询光模块DDM信息?
  2. 软件随想录(local.joelonsoftware.com/wiki)-2002年12月11日 程序设计领域的帕麦尔斯顿勋爵 - Lord Palmerston on Programming
  3. 如何查看自己的支付宝花呗是否已经接入央行征信? #花呗部分用户接入央行征信#
  4. 问题 E: LZY去年买了个表
  5. 使用健康档案数据计算OLT的用户光衰不合格率
  6. python实现图像格式转换(bmp、jpg、png)
  7. HDU 6232 2017 哈尔滨 Confliction
  8. 解决django admin表的外键关联数据过多响应时间过长问题
  9. E: The repository ‘http://ppa.launchpad.net/george-edison55/cmake-3.x/ubuntu bionic Release‘ does no
  10. python elif invalid syntax_Python错误集锦:if和elif语句提示:SyntaxError: invalid syntax