分解质因数
引入
给定一个正整数
考虑朴素算法,因数是成对分布的,
当
朴素算法
最简单的算法即为从
我们能够证明 result
中的所有元素即为 N
的全体素因数。
证明 result
中即为 的全体素因数
首先考察 N
的变化。当循环进行到 i
结束时,由于刚执行结束 while(N % i == 0) N /= i
部分,i
不再整除 N
。而且,每次除去一个因子,都能够保证 N
仍整除 i
开始时,N
是 i
的整数整除。
其次证明 result
中的元素均为 i
时,能够在 result
中存入 i
的条件是 N % i == 0
,这说明 i
整除 N
,且已经说明 N
是 i
是 i
的循环结束时,若 N
不为一,也会存入 result
。此时它根据前文,也必然是
其次证明 result
中均为素数。我们假设存在一个在 result
中的合数 i
不超过 i
是 K
的一个因子。这样的 i
存入 result
,因为第一段已经说明,当循环到 N
不被任何小于 i
整除。这样的 i * i > N
,故而已经遍历完了所有不超过 i
,而且据上文所说, 这些 i
绝不能整除目前的 N
,亦即
最后证明,所有 result
中。不妨假设 result
中。根据上文的讨论,i
。设 i
是退出循环前最后的 i
,则 i
严格小于 N
不被之前的 i
整除,故而 N
。所以最后的 N
大于一,则根据前文所述,它必然是素数,则 N
就等于 result
,与假设矛盾。
值得指出的是,如果开始已经打了一个素数表的话,时间复杂度将从
例题:CF 1445C
Pollard Rho 算法
引入
利用暴力算法获得一个非平凡因子的复杂度为
它的核心想法是,对于一个随机自映射
要理解进入循环的期望时间为
生日悖论
不考虑出生年份(假设每年都是 365 天),问:一个房间中至少多少人,才能使其中两个人生日相同的概率达到
解:假设一年有
设
至少有两个人生日相同的概率为
由不等式
因此
将
当
类似地可以计算,随机均匀地选取一列生日,首次获得重复生日需要的人数的期望也是
这启发我们,如果可以随机选取一列数字,出现重复数字需要的抽样规模的期望也是
利用最大公约数求出一个约数
实际构建一列模
这里选取的函数容易计算,且往往可以生成相当随机的序列。但它并不是完全随机的。举个例子,设
可以发现数据在
更重要的是,这样的函数确实提供了
证明
若
作为
这一算法并不是总能成功的,因为
根据上文分析,理论上,任何满足
实现
我们需要实现的算法,能够在迭代过程中快速判断
Floyd 判环
假设两个人在赛跑,A 的速度快,B 的速度慢,经过一定时间后,A 一定会和 B 相遇,且相遇时 A 跑过的总距离减去 B 跑过的总距离一定是圈长的倍数。
设
我们每次令
基于 Floyd 判环的 Pollard-Rho 算法
Brent 判环
实际上,Floyd 判环算法可以有常数上的改进。Brent 判环从
可以证明3,这样得到环之前需要调用
倍增优化
无论是 Floyd 判环还是 Brent 判环,迭代次数都是
简单来说,如果
如果每
这里提供 Brent 判环且加上倍增优化的 Pollard-Rho 算法实现。
实现
ll Pollard_Rho(ll x) {
ll t = 0;
ll c = rand() % (x - 1) + 1;
ll s = t;
int step = 0, goal = 1;
ll val = 1;
for (goal = 1;; goal <<= 1, s = t, val = 1) {
for (step = 1; step <= goal; ++step) {
t = f(t, c, x);
val = val * abs(t - s) % x;
// 如果 val 为 0,退出重新分解
if (!val) return x;
if (step % 127 == 0) {
ll d = gcd(val, x);
if (d > 1) return d;
}
}
ll d = gcd(val, x);
if (d > 1) return d;
}
}
from random import randint
from math import gcd
def Pollard_Rho(x):
c = randint(1, x - 1)
s = t = f(0, c, x)
goal = val = 1
while True:
for step in range(1, goal + 1):
t = f(t, c, x)
val = val * abs(t - s) % x
if val == 0:
return x # 如果 val 为 0,退出重新分解
if step % 127 == 0:
d = gcd(val, x)
if d > 1:
return d
d = gcd(val, x)
if d > 1:
return d
s = t
goal <<= 1
val = 1
复杂度
Pollard-Rho 算法中的期望迭代次数为
值得一提的是,前文分析基于的是完全随机的自映射函数,但 Pollard-Rho 算法实际使用的是伪随机函数,所以该算法并没有严格的复杂度分析,实践中通常跑得较快。
例题:求一个数的最大素因子
对于一个数
实现
#include <algorithm>
#include <cstdlib>
#include <ctime>
#include <iostream>
using namespace std;
using ll = long long;
using ull = unsigned long long;
int t;
ll max_factor, n;
ll gcd(ll a, ll b) {
if (b == 0) return a;
return gcd(b, a % b);
}
ll bmul(ll a, ll b, ll m) { // 快速乘
ull c = (ull)a * (ull)b - (ull)((long double)a / m * b + 0.5L) * (ull)m;
if (c < (ull)m) return c;
return c + m;
}
ll qpow(ll x, ll p, ll mod) { // 快速幂
ll ans = 1;
while (p) {
if (p & 1) ans = bmul(ans, x, mod);
x = bmul(x, x, mod);
p >>= 1;
}
return ans;
}
bool Miller_Rabin(ll p) { // 判断素数
if (p < 2) return false;
if (p == 2) return true;
if (p == 3) return true;
ll d = p - 1, r = 0;
while (!(d & 1)) ++r, d >>= 1; // 将d处理为奇数
for (ll k = 0; k < 10; ++k) {
ll a = rand() % (p - 2) + 2;
ll x = qpow(a, d, p);
if (x == 1 || x == p - 1) continue;
for (int i = 0; i < r - 1; ++i) {
x = bmul(x, x, p);
if (x == p - 1) break;
}
if (x != p - 1) return false;
}
return true;
}
ll Pollard_Rho(ll x) {
ll s = 0, t = 0;
ll c = (ll)rand() % (x - 1) + 1;
int step = 0, goal = 1;
ll val = 1;
for (goal = 1;; goal *= 2, s = t, val = 1) { // 倍增优化
for (step = 1; step <= goal; ++step) {
t = (bmul(t, t, x) + c) % x;
val = bmul(val, abs(t - s), x);
if ((step % 127) == 0) {
ll d = gcd(val, x);
if (d > 1) return d;
}
}
ll d = gcd(val, x);
if (d > 1) return d;
}
}
void fac(ll x) {
if (x <= max_factor || x < 2) return;
if (Miller_Rabin(x)) { // 如果x为质数
max_factor = max(max_factor, x); // 更新答案
return;
}
ll p = x;
while (p >= x) p = Pollard_Rho(x); // 使用该算法
while ((x % p) == 0) x /= p;
fac(x), fac(p); // 继续向下分解x和p
}
int main() {
cin >> t;
while (t--) {
srand((unsigned)time(NULL));
max_factor = 0;
cin >> n;
fac(n);
if (max_factor == n) // 最大的质因数即自己
cout << "Prime\n";
else
cout << max_factor << '\n';
}
return 0;
}
参考资料与链接
-
https://en.wikipedia.org/wiki/Birthday_problem#Reverse_problem ↩
-
Menezes, Alfred J.; van Oorschot, Paul C.; Vanstone, Scott A. (2001). Handbook of Applied Cryptography. Section 3.11 and 3.12. ↩
-
Brent, R. P. (1980), An improved Monte Carlo factorization algorithm, BIT Numerical Mathematics, 20(2): 176–184, doi:10.1007/BF01933190 ↩
创建日期: 2020年8月20日