質因數分解的一些討論(Pollard-Rho算法)


一類問題:對於一個整數n,將n進行質因數分解

算法1:

根據定義直接枚舉,直接給出代碼:

void Decom(int n) {
int i;
vector<int> res;
for(i = 2; i <= n; i++) {
while(n%i == 0) {
res.push_back(i);
n /= i;
}
}
for(i = 0; i < res.size()-1; i++) printf("%d*", res[i]);
printf("%d\n", res.back());
}

算法2:

考慮到若有 i 滿足n % i == 0,則必有n % (n/i) == 0,所以可以僅枚舉 i 從2到 n ,代碼如下:

void Decom(int n) {
int i;
vector<int> res;
for(i = 2; i*i <= n; i++) {
while(n%i == 0) {
res.push_back(i);
n /= i;
}
}
for(i = 0; i < res.size()-1; i++) printf("%d*", res[i]);
printf("%d\n", res.back());
}

Pollard-Rho算法:

該算法需要使用的大素數判定的 Miller - Rabin 算法,之前已經討論過。
Miller-Rabin算法戳這里
對於一個大整數n,我們取任意一個數 x 使得 x n 的質因數的幾率很小,但如果取兩個數 x1 以及 x2 使得它們的差是n的因數的幾率就提高了,如果取 x1 以及 x2 使得 gcd(abs(x1x2),n)>1 的概率就更高了。這就是Pollard-Rho算法的主要思想。

對於滿足 gcd(abs(x1x2),n)>1 x1 x2 gcd(abs(x1x2),n) 就是n的一個因數,只需要判斷它是否為素數,若為素數,則是n的質因數,否則遞歸此過程。

其中判斷素數就使用 Miller - Rabin 算法。

那么我們怎樣不斷取得 x1 x2 呢?
x1 在區間 [1,n] 中隨機出來,而 x2 則由x[i]=(x[i-1]*x[i-1]%n+c)%n推算出來,其中 c 為任意給定值,事實證明,這樣就是比較優的。

代碼如下:

#include<cstdio>
#include<algorithm>
#include<vector>
using namespace std;

const int MAXN = 65;
long long x[MAXN];
vector<long long> f;

long long multi(long long a, long long b, long long p) {
long long ans = 0;
while(b) {
if(b&1LL) ans = (ans+a)%p;
a = (a+a)%p;
b >>= 1;
}
return ans;
}

long long qpow(long long a, long long b, long long p) {
long long ans = 1;
while(b) {
if(b&1LL) ans = multi(ans, a, p);
a = multi(a, a, p);
b >>= 1;
}
return ans;
}

bool Miller_Rabin(long long n) {
if(n == 2) return true;
int s = 20, i, t = 0;
long long u = n-1;
while(!(u & 1)) {
t++;
u >>= 1;
}
while(s--) {
long long a = rand()%(n-2)+2;
x[0] = qpow(a, u, n);
for(i = 1; i <= t; i++) {
x[i] = multi(x[i-1], x[i-1], n);
if(x[i] == 1 && x[i-1] != 1 && x[i-1] != n-1) return false;
}
if(x[t] != 1) return false;
}
return true;
}

long long gcd(long long a, long long b) {
return b ? gcd(b, a%b) : a;
}

long long Pollard_Rho(long long n, int c) {
long long i = 1, k = 2, x = rand()%(n-1)+1, y = x;
while(true) {
i++;
x = (multi(x, x, n) + c)%n;
long long p = gcd((y-x+n)%n, n);
if(p != 1 && p != n) return p;
if(y == x) return n;
if(i == k) {
y = x;
k <<= 1;
}
}
}

void find(long long n, int c) {
if(n == 1) return;
if(Miller_Rabin(n)) {
f.push_back(n);
return;
}
long long p = n, k = c;
while(p >= n) p = Pollard_Rho(p, c--);
find(p, k);
find(n/p, k);
}

若仍然不能理解,戳這里
以下可以略去:


關於此算法名稱的來歷:
由於該算法在推算 x[i] 時,最后必定會出現 x[i+j]=x[i] ,然后把 x[i] 按照一種神奇的方式寫下來,就會長得像希臘字母 ρ 。。。
如下圖:
這里寫圖片描述

然后發明者叫做 Pollard ,所以就叫做了這樣一個神奇的名字。。


注意!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系我们删除。



 
粤ICP备14056181号  © 2014-2020 ITdaan.com