0
点赞
收藏
分享

微信扫一扫

Pollard Rho算法分解因数

小典典Rikako 2022-04-14 阅读 51

分解因数算法

文章目录


给定一个正整数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 = p 1 k 1 p 2 k 2 . . . p n k n N=p_1^{k_1}p_2^{k_2}...p_n^{k_n} N=p1k1p2k2...pnkn
比如 48 = 2 4 × 3 1 48=2^4\times 3^1 48=24×31

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

可以将 2 k 2^k 2k从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的素因数
}

Pollard Rho算法

ρ \rho ρ

算法思想

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

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

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

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

为啥不直接选一个 ∀ 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个人,任意两个人生日不同的概率(生日悖论)

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

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

1.如何产生随机数

构造随机数函数 f ( x ) = ( x 2 + c ) m o d    n f(x)=(x^2+c)\mod n f(x)=(x2+c)modn,作用是生成随机数序列,其中c是随便指定的一个数,然后随便选取一个正整数 x 0 x_0 x0作为起点
x 1 = f ( x 0 ) = ( x 0 2 + c ) m o d    n x 2 = f ( x 1 ) = ( x 1 2 + c ) m o d    n . . . x m = f ( x m − 1 ) = ( x m − 1 2 + c ) m o d    n x_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(xm1)=(xm12+c)modn
计算若干次后一定会陷入循环

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

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

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

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

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

这个方法叫做"Floyd判环"

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

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

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

3.最大公因数优化

刚才我们只是检查 ∣ x i − x j ∣ |x_i-x_j| xixj能不能整除 N N N,即检查 ∣ x i − x j ∣ |x_i-x_j| xixj是不是 N N N的因数

但是由于 g c d ( ∣ x i − x j ∣ , N ) ∣ N   a n d   g c d ( ∣ x i − x j ∣ , N ) ∣ ∣ x i − x j ∣ gcd(|x_i-x_j|,N)|N\ and\ gcd(|x_i-x_j|,N)||x_i-x_j| gcd(xixj,N)N and gcd(xixj,N)xixj,那么只要 ∣ x i − x j ∣ |x_i-x_j| xixj是N的因数,那么 g c d ( ∣ x i − x j ∣ , N ) gcd(|x_i-x_j|,N) gcd(xixj,N)也是N的因数

并且用最大公因数去和N比划的机会只会比只用 ∣ x i − x j ∣ |x_i-x_j| xixj去直接比划N更多

对于 ∣ x i − x j ∣ |x_i-x_j| xixj可以要求放松一点,只要是 ∣ x i − x j ∣ |x_i-x_j| xixj的任何非1真因数能够整除 N N N就算是给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

分解质因数 98554799767 98554799767 98554799767

还是运行刚才的程序

98554799767
1:98554799767
98554799767
2:101999

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

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

举报

相关推荐

0 条评论