Loading docs/math/pollar-rho.md +141 −120 Original line number Diff line number Diff line Loading @@ -17,15 +17,16 @@ Pollar-Rho 算法是一种用于快速分解质因数的算法。 设 k 个人生日互不相同为事件$A$, 则事件$A$的概率为 $$ P(A)=\frac{n}{n} * \frac{n-1}{n} * \dots *\frac{n-k+1}{n} P(A)=\frac{n}{n} \times \frac{n-1}{n} \times \dots \times \frac{n-k+1}{n} $$ 至少有两个人生日相同的概率为 $P(\overline A)=1-P(A)$ 。根据题意可知 $P(\overline A)\ge\frac{1}{2}$ , 那么就有 $1 * \frac{n-1}{n} * \dots *\frac{n-k+1}{n} \le \frac{1}{2}$ 至少有两个人生日相同的概率为$P(\overline A)=1-P(A)$。根据题意可知$P(\overline A)\ge\frac{1}{2}$ ,那么就有$1 \times \frac{n-1}{n} \times \dots \times \frac{n-k+1}{n} \le \frac{1}{2}$ 由不等式$1+x\le e^x$可得 $$ P(A) \le e^{-\frac{1}{n}}* e^{-\frac{2}{n}}*\dots * e^{-\frac{k-1}{n}}=e^{-\frac{k(k-1)}{2n}}\le\frac{1}{2}\\ P(A) \le e^{-\frac{1}{n}}\times e^{-\frac{2}{n}}\times \dots \times e^{-\frac{k-1}{n}}=e^{-\frac{k(k-1)}{2n}}\le\frac{1}{2}\\ e^{-\frac{k(k-1)}{2n}}\le\frac{1}{2} $$ Loading @@ -33,17 +34,17 @@ $$ 然而我们可以得到一个不等式方程,$e^{-\frac{k(k-1)}{2n}}\le 1-p$,其中$p$是一个概率。那么当$k=57$,$n=365$时,可以求得$p\approx 0.99$。 考虑一个问题,设置一个数据 $n$ ,在 $[1,1000]$ 里随机选取 $i$ 个数( $i=1$ 时就是它自己),使它们之间有两个数的差值为 $k$ 。当 $i=1$ 时成功的概率是 $\frac{1}{1000}$ ,当 $i=2$ 时成功的概率是 $\frac{1}{500}$ (考虑绝对值, $k_2$ 可以取 $k_1-k$ 或 $k_1+k$ ),随着 $i$ 的增大,这个概率也会增大最后趋向于 1。 考虑一个问题,设置一个数据$n$,在$[1,1000]$里随机选取$i$个数($i=1$时就是它自己),使它们之间有两个数的差值为$k$。当$i=1$时成功的概率是$\frac{1}{1000}$,当$i=2$时成功的概率是$\frac{1}{500}$(考虑绝对值,$k_2$可以取$k_1-k$或$k_1+k$),随着$i$的增大,这个概率也会增大最后趋向于1。 ## 优化随机算法 最大公约数一定是某个数的约数,即 $\forall k \in\mathbb{N}_{+},gcd(k,n)|n$ ,只要选适当的 $k$ 使得 $1<\gcd(k,n)< n$ ,就可以求得一个约数 $\gcd(k,n)$ 。满足这样条件的 $k$ 不少, $k$ 有若干个质因子,每个质因子及其倍数都是可行的。 最大公约数一定是某个数的约数,即$\forall k \in\mathbb{N}_{+},\gcd(k,n)|n$,只要选适当的$k$使得$1<\gcd(k,n)< n$,就可以求得一个约数$\gcd(k,n)$。满足这样条件的$k$不少,$k$有若干个质因子,每个质因子及其倍数都是可行的。 根据生日悖论,取多组数据作差能够优化取数的精确性。那么不妨取一组数 $x_1,x_2,\dots,x_n$若有$1<\gcd(|x_i-x_j|,n) < n$,则称$gcd(|x_i-x_j|,N)$是$N$的一个因子(更严谨一些,应该是非平凡因子,即满足 $1< d < n,d|n$ 的那些数)。 ### 构造伪随机函数 构造一个伪随机数序列,取相邻的两项来求 $\gcd(|x_i-x_j|,N)$ 。通过 $f(x)=(x^2+c)\%n$ 来生成随机数,其中 $c=rand()$ ,是一个随机的常数。 构造一个伪随机数序列,取相邻的两项来求$\gcd(|x_i-x_j|,N)$。通过$f(x)=(x^2+c)\bmod n$来生成随机数,其中$c=rand()$,是一个随机的常数。 随机取一个$x_1$,令$x_2=f(x_1),x_3=f(x_2),\dots,x_i=f(x_{i-1})$,在一定范围内可以认为这个数列是“随机”的。 Loading @@ -61,15 +62,17 @@ $$ 设$a=f(1),b=f(f(1))$,每一次更新$a=f(a),b=f(f(b))$,只要检查在更新过程中a、b是否相等,如果相等了,那么就出现了环。 我们每次对 $d=gcd(|x_i-x_j|,n)$ ,判断 d 是否满足 $1< d< n$ ,若满足则可直接返回 $d$ 。由于 $x_i$ 是一个伪随机数列,必定会形成环,在形成环时就不能再继续操作了,直接返回 n 本身,并且在后续操作里调整随机常数 $c$ ,重新分解。 我们每次对$d=\gcd(|x_i-x_j|,n)$,判断d是否满足$1< d< n$,若满足则可直接返回$d$。由于$x_i$是一个伪随机数列,必定会形成环,在形成环时就不能再继续操作了,直接返回n本身,并且在后续操作里调整随机常数$c$,重新分解。 ??? note "基于Floyd判环的Pollar-Rho算法" ```c++ ll PR(ll N) { ll PR(ll N) { ll c = rand() % (N - 1) + 1; ll t = f(0, c, N); ll r = f(f(0, c, N), c, N); while (t != r) { while(t != r) { ll d = gcd(abs(t - r), N); if(d > 1) return d; t = f(t, c, N); Loading @@ -81,22 +84,26 @@ $$ ### 倍增优化 使用 $gcd$ 求解的时间复杂度为 $O(log N)$ ,频繁地调用会使算法运行地很慢,可以通过乘法累积来减少求 $gcd$ 的次数。如果 $1< gcd(a,b)$ ,则有 $1< gcd(ac,b)$ , $c\in \mathbb{N}_{+}$ ,并且有 $1< gcd(ac \%b,b)=gcd(a,b)$ 。 使用$\gcd$求解的时间复杂度为$O(\log N)$,频繁地调用会使算法运行地很慢,可以通过乘法累积来减少求$\gcd$的次数。如果$1< \gcd(a,b)$,则有$1< \gcd(ac,b)$,$c\in \mathbb{N}_{+}$,并且有$1< \gcd(ac \bmod b,b)=\gcd(a,b)$。 我们每过一段时间将这些差值进行 $gcd$ 运算,设 $s=\prod|x_0-x_j|\% n$ ,如果某一时刻得到 $s=0$ 那么表示分解失败,退出并返回 $n$ 本身。每隔 $2^k$ 个数,计算是否满足 $1< gcd(s, n) < n$ 。 我们每过一段时间将这些差值进行$\gcd$运算,设$s=\prod|x_0-x_j|\bmod n$,如果某一时刻得到$s=0$那么表示分解失败,退出并返回$n$本身。每隔$2^k$个数,计算是否满足$1< \gcd(s, n) < n$。 ??? note "参考实现" ```c++ ll PR(ll x) { ll PR(ll x) { ll s = 0,t = 0; ll c= rand() % (x - 1) + 1; int step = 0, goal = 1; ll val = 1; for (goal = 1;; goal <<= 1, s = t, val = 1) { for (step = 1; step <= goal; ++step) { 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; if ((step % 127) == 0) { if( (step % 127) == 0 ) { ll d = gcd(val,x); if (d > 1) return d; } Loading @@ -107,7 +114,8 @@ $$ } ``` 例题: [P4718【模板】Pollard-Rho 算法](https://www.luogu.com.cn/problem/P4718) 例题:[P4718 【模板】Pollard-Rho算法](https://www.luogu.com.cn/problem/P4718) 对于一个数 n ,用[Miller Rabin算法](/prime/#_4)判断是否为素数,如果是就可以直接返回了,否则用Pollard-Rho算法找一个因子 p ,将 n 除去因子 p 。再递归分解 n 和 p ,用Miller Rabin判断是否出现质因子,并用max_factor更新就可以求出最大质因子了。由于这个题目的数据过于庞大,用Floyd判环的方法是不够的,这里采用倍增优化的方法。 Loading @@ -123,15 +131,19 @@ $$ int t; ll max_factor, n; inline ll gcd(ll a, ll b) { inline ll gcd(ll a,ll b) { if(b == 0) return a; return gcd(b, a % b); } inline ll qp(ll x, ll p, ll mod) { inline ll qp(ll x,ll p,ll mod) { ll ans = 1; while (p) { if (p & 1) ans = (lll)ans * x % mod; while(p) { if(p & 1) ans = (lll)ans * x % mod; x = (lll) x * x % mod; p >>= 1; } Loading @@ -157,18 +169,25 @@ $$ return 1; } inline ll f(ll x, ll c, ll n) { return ((lll)x * x + c) % n; } inline ll f(ll x,ll c,ll n) { return ((lll)x * x + c) % n; } inline ll PR(ll x) { inline ll PR(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 <<= 1, s = t, val = 1) { for (step = 1; step <= goal; ++step) { for( goal=1; ;goal <<= 1, s = t, val = 1) { for(step = 1; step <= goal; ++step) { t = f(t, c, x); val= (lll)val * abs(t - s) % x; if ((step % 127) == 0) { if( (step % 127) == 0 ) { ll d = gcd(val,x); if (d > 1) return d; } Loading @@ -178,9 +197,11 @@ $$ } } inline void fac(ll x) { inline void fac(ll x) { if(x <= max_factor || x < 2) return; if (Miller_rabin(x)) { if(Miller_rabin(x)) { max_factor = max(max_factor, x); return; } Loading @@ -190,17 +211,17 @@ $$ fac(x),fac(p); } int main() { int main() { scanf("%d", &t); while (t--) { while(t --) { srand((unsigned)time(NULL)); max_factor=0; scanf("%lld", &n); fac(n); if (max_factor == n) printf("Prime\n"); else printf("%lld\n", max_factor); if(max_factor == n) printf("Prime\n"); else printf("%lld\n",max_factor); } return 0; } Loading Loading
docs/math/pollar-rho.md +141 −120 Original line number Diff line number Diff line Loading @@ -17,15 +17,16 @@ Pollar-Rho 算法是一种用于快速分解质因数的算法。 设 k 个人生日互不相同为事件$A$, 则事件$A$的概率为 $$ P(A)=\frac{n}{n} * \frac{n-1}{n} * \dots *\frac{n-k+1}{n} P(A)=\frac{n}{n} \times \frac{n-1}{n} \times \dots \times \frac{n-k+1}{n} $$ 至少有两个人生日相同的概率为 $P(\overline A)=1-P(A)$ 。根据题意可知 $P(\overline A)\ge\frac{1}{2}$ , 那么就有 $1 * \frac{n-1}{n} * \dots *\frac{n-k+1}{n} \le \frac{1}{2}$ 至少有两个人生日相同的概率为$P(\overline A)=1-P(A)$。根据题意可知$P(\overline A)\ge\frac{1}{2}$ ,那么就有$1 \times \frac{n-1}{n} \times \dots \times \frac{n-k+1}{n} \le \frac{1}{2}$ 由不等式$1+x\le e^x$可得 $$ P(A) \le e^{-\frac{1}{n}}* e^{-\frac{2}{n}}*\dots * e^{-\frac{k-1}{n}}=e^{-\frac{k(k-1)}{2n}}\le\frac{1}{2}\\ P(A) \le e^{-\frac{1}{n}}\times e^{-\frac{2}{n}}\times \dots \times e^{-\frac{k-1}{n}}=e^{-\frac{k(k-1)}{2n}}\le\frac{1}{2}\\ e^{-\frac{k(k-1)}{2n}}\le\frac{1}{2} $$ Loading @@ -33,17 +34,17 @@ $$ 然而我们可以得到一个不等式方程,$e^{-\frac{k(k-1)}{2n}}\le 1-p$,其中$p$是一个概率。那么当$k=57$,$n=365$时,可以求得$p\approx 0.99$。 考虑一个问题,设置一个数据 $n$ ,在 $[1,1000]$ 里随机选取 $i$ 个数( $i=1$ 时就是它自己),使它们之间有两个数的差值为 $k$ 。当 $i=1$ 时成功的概率是 $\frac{1}{1000}$ ,当 $i=2$ 时成功的概率是 $\frac{1}{500}$ (考虑绝对值, $k_2$ 可以取 $k_1-k$ 或 $k_1+k$ ),随着 $i$ 的增大,这个概率也会增大最后趋向于 1。 考虑一个问题,设置一个数据$n$,在$[1,1000]$里随机选取$i$个数($i=1$时就是它自己),使它们之间有两个数的差值为$k$。当$i=1$时成功的概率是$\frac{1}{1000}$,当$i=2$时成功的概率是$\frac{1}{500}$(考虑绝对值,$k_2$可以取$k_1-k$或$k_1+k$),随着$i$的增大,这个概率也会增大最后趋向于1。 ## 优化随机算法 最大公约数一定是某个数的约数,即 $\forall k \in\mathbb{N}_{+},gcd(k,n)|n$ ,只要选适当的 $k$ 使得 $1<\gcd(k,n)< n$ ,就可以求得一个约数 $\gcd(k,n)$ 。满足这样条件的 $k$ 不少, $k$ 有若干个质因子,每个质因子及其倍数都是可行的。 最大公约数一定是某个数的约数,即$\forall k \in\mathbb{N}_{+},\gcd(k,n)|n$,只要选适当的$k$使得$1<\gcd(k,n)< n$,就可以求得一个约数$\gcd(k,n)$。满足这样条件的$k$不少,$k$有若干个质因子,每个质因子及其倍数都是可行的。 根据生日悖论,取多组数据作差能够优化取数的精确性。那么不妨取一组数 $x_1,x_2,\dots,x_n$若有$1<\gcd(|x_i-x_j|,n) < n$,则称$gcd(|x_i-x_j|,N)$是$N$的一个因子(更严谨一些,应该是非平凡因子,即满足 $1< d < n,d|n$ 的那些数)。 ### 构造伪随机函数 构造一个伪随机数序列,取相邻的两项来求 $\gcd(|x_i-x_j|,N)$ 。通过 $f(x)=(x^2+c)\%n$ 来生成随机数,其中 $c=rand()$ ,是一个随机的常数。 构造一个伪随机数序列,取相邻的两项来求$\gcd(|x_i-x_j|,N)$。通过$f(x)=(x^2+c)\bmod n$来生成随机数,其中$c=rand()$,是一个随机的常数。 随机取一个$x_1$,令$x_2=f(x_1),x_3=f(x_2),\dots,x_i=f(x_{i-1})$,在一定范围内可以认为这个数列是“随机”的。 Loading @@ -61,15 +62,17 @@ $$ 设$a=f(1),b=f(f(1))$,每一次更新$a=f(a),b=f(f(b))$,只要检查在更新过程中a、b是否相等,如果相等了,那么就出现了环。 我们每次对 $d=gcd(|x_i-x_j|,n)$ ,判断 d 是否满足 $1< d< n$ ,若满足则可直接返回 $d$ 。由于 $x_i$ 是一个伪随机数列,必定会形成环,在形成环时就不能再继续操作了,直接返回 n 本身,并且在后续操作里调整随机常数 $c$ ,重新分解。 我们每次对$d=\gcd(|x_i-x_j|,n)$,判断d是否满足$1< d< n$,若满足则可直接返回$d$。由于$x_i$是一个伪随机数列,必定会形成环,在形成环时就不能再继续操作了,直接返回n本身,并且在后续操作里调整随机常数$c$,重新分解。 ??? note "基于Floyd判环的Pollar-Rho算法" ```c++ ll PR(ll N) { ll PR(ll N) { ll c = rand() % (N - 1) + 1; ll t = f(0, c, N); ll r = f(f(0, c, N), c, N); while (t != r) { while(t != r) { ll d = gcd(abs(t - r), N); if(d > 1) return d; t = f(t, c, N); Loading @@ -81,22 +84,26 @@ $$ ### 倍增优化 使用 $gcd$ 求解的时间复杂度为 $O(log N)$ ,频繁地调用会使算法运行地很慢,可以通过乘法累积来减少求 $gcd$ 的次数。如果 $1< gcd(a,b)$ ,则有 $1< gcd(ac,b)$ , $c\in \mathbb{N}_{+}$ ,并且有 $1< gcd(ac \%b,b)=gcd(a,b)$ 。 使用$\gcd$求解的时间复杂度为$O(\log N)$,频繁地调用会使算法运行地很慢,可以通过乘法累积来减少求$\gcd$的次数。如果$1< \gcd(a,b)$,则有$1< \gcd(ac,b)$,$c\in \mathbb{N}_{+}$,并且有$1< \gcd(ac \bmod b,b)=\gcd(a,b)$。 我们每过一段时间将这些差值进行 $gcd$ 运算,设 $s=\prod|x_0-x_j|\% n$ ,如果某一时刻得到 $s=0$ 那么表示分解失败,退出并返回 $n$ 本身。每隔 $2^k$ 个数,计算是否满足 $1< gcd(s, n) < n$ 。 我们每过一段时间将这些差值进行$\gcd$运算,设$s=\prod|x_0-x_j|\bmod n$,如果某一时刻得到$s=0$那么表示分解失败,退出并返回$n$本身。每隔$2^k$个数,计算是否满足$1< \gcd(s, n) < n$。 ??? note "参考实现" ```c++ ll PR(ll x) { ll PR(ll x) { ll s = 0,t = 0; ll c= rand() % (x - 1) + 1; int step = 0, goal = 1; ll val = 1; for (goal = 1;; goal <<= 1, s = t, val = 1) { for (step = 1; step <= goal; ++step) { 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; if ((step % 127) == 0) { if( (step % 127) == 0 ) { ll d = gcd(val,x); if (d > 1) return d; } Loading @@ -107,7 +114,8 @@ $$ } ``` 例题: [P4718【模板】Pollard-Rho 算法](https://www.luogu.com.cn/problem/P4718) 例题:[P4718 【模板】Pollard-Rho算法](https://www.luogu.com.cn/problem/P4718) 对于一个数 n ,用[Miller Rabin算法](/prime/#_4)判断是否为素数,如果是就可以直接返回了,否则用Pollard-Rho算法找一个因子 p ,将 n 除去因子 p 。再递归分解 n 和 p ,用Miller Rabin判断是否出现质因子,并用max_factor更新就可以求出最大质因子了。由于这个题目的数据过于庞大,用Floyd判环的方法是不够的,这里采用倍增优化的方法。 Loading @@ -123,15 +131,19 @@ $$ int t; ll max_factor, n; inline ll gcd(ll a, ll b) { inline ll gcd(ll a,ll b) { if(b == 0) return a; return gcd(b, a % b); } inline ll qp(ll x, ll p, ll mod) { inline ll qp(ll x,ll p,ll mod) { ll ans = 1; while (p) { if (p & 1) ans = (lll)ans * x % mod; while(p) { if(p & 1) ans = (lll)ans * x % mod; x = (lll) x * x % mod; p >>= 1; } Loading @@ -157,18 +169,25 @@ $$ return 1; } inline ll f(ll x, ll c, ll n) { return ((lll)x * x + c) % n; } inline ll f(ll x,ll c,ll n) { return ((lll)x * x + c) % n; } inline ll PR(ll x) { inline ll PR(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 <<= 1, s = t, val = 1) { for (step = 1; step <= goal; ++step) { for( goal=1; ;goal <<= 1, s = t, val = 1) { for(step = 1; step <= goal; ++step) { t = f(t, c, x); val= (lll)val * abs(t - s) % x; if ((step % 127) == 0) { if( (step % 127) == 0 ) { ll d = gcd(val,x); if (d > 1) return d; } Loading @@ -178,9 +197,11 @@ $$ } } inline void fac(ll x) { inline void fac(ll x) { if(x <= max_factor || x < 2) return; if (Miller_rabin(x)) { if(Miller_rabin(x)) { max_factor = max(max_factor, x); return; } Loading @@ -190,17 +211,17 @@ $$ fac(x),fac(p); } int main() { int main() { scanf("%d", &t); while (t--) { while(t --) { srand((unsigned)time(NULL)); max_factor=0; scanf("%lld", &n); fac(n); if (max_factor == n) printf("Prime\n"); else printf("%lld\n", max_factor); if(max_factor == n) printf("Prime\n"); else printf("%lld\n",max_factor); } return 0; } Loading