【学习笔记】Pollard's rho算法

【算法简介】

  • PollarsRho P o l l a r ′ s R h o 算法是一种用于分解质因数的算法。
  • 对于一个被分解的数 N N ,假设 N 的最小的质因数为 p(pN) p ( p ≠ N ) ,那么 PollarsRho P o l l a r ′ s R h o 算法能够在 O(pα(N)) O ( p ∗ α ( N ) ) 的期望时间复杂度内将 N N 分解为两个不是 1 的数的乘积,其中 α(N) α ( N ) 是求解两个数的 gcd g c d 的时间复杂度,并且 PollarsRho P o l l a r ′ s R h o 算法几乎不需要额外的空间。
  • 由于我们假定了 N N 不是质数,有 pN ,因此 PollarsRho P o l l a r ′ s R h o 算法产生一次有效的分解的期望时间复杂度为 O(N14α(N)) O ( N 1 4 ∗ α ( N ) )
  • 同时, PollarsRho P o l l a r ′ s R h o 算法还有一个不常用的优化,拟定一个参数 β β PollarsRho P o l l a r ′ s R h o 算法产生一次有效的分解的期望时间复杂度可以被优化至 O(N14+N14α(N)β+βα(N)) O ( N 1 4 + N 1 4 ∗ α ( N ) β + β ∗ α ( N ) )

【算法流程】

  • 假设我们需要分解 N=pq N = p ∗ q ,其中 p p N 的一个非平凡因子。

  • 我们在模意义下生成一个近似随机的数列 {xn} { x n } x1=2,xi=f(xi1) mod  N (i2) x 1 = 2 , x i = f ( x i − 1 )   m o d     N   ( i ≥ 2 ) ,其中 f(x) f ( x ) 为一个伪随机函数,可取 f(x)=x2+1 f ( x ) = x 2 + 1 。此时,另一个数列 {xn mod  p} { x n   m o d     p } 同样客观存在着。由于这两个数列中的每一个数都由前一个数唯一确定,所以这两个数列一定都存在某个时刻,数列会进入一个循环,由 生日悖论 {xn} { x n } 的循环节的出现位置期望为 O(N) O ( N ) {xn mod  p} { x n   m o d     p } 的循环节的出现位置期望为 O(p) O ( p )

  • 假设存在两个位置 i,j i , j 使得 xi mod  p=xj mod  p x i   m o d     p = x j   m o d     p xixj x i ≠ x j ,那么我们取 gcd(xixj,N) g c d ( x i − x j , N ) ,就可以找到 N N 的一个非平凡因子。

  • 那么我们不妨取 i,2i 两个位置,计算 gcd(xix2i,N) g c d ( x i − x 2 i , N )

    gcd(xix2i,N)=1 g c d ( x i − x 2 i , N ) = 1 ,那么说明 i,2i i , 2 i 不在任何数列的循环节的对应位置,继续枚举下一个 i i

    gcd(xix2i,N)=N ,那么说明 i,2i i , 2 i 同时在 {xn} { x n } {xn mod  p} { x n   m o d     p } 的循环节的对应位置,这表明我们无法通过枚举更大的 i i 来找到使得 xi mod  p=x2i mod  p xix2i x i ≠ x 2 i i i ,因此我们只能更换随机函数 f(x) ,重新进行算法。

    否则,我们就找到了 N N 的一个非平凡因子 gcd(xix2i,N)

  • 由于形如 f(x)=x2+a f ( x ) = x 2 + a 的随机函数只会生成在模 4 4 意义下为 a a+1 a + 1 的数,因此当取 f(x)=x2+a f ( x ) = x 2 + a 的随机函数时,算法无法分解出 2 2 这个因子,需要特判。

  • 我们期望需要枚举 O(p) i i 来分解出 N 的一个非平凡因子 gcd(xix2i,N) g c d ( x i − x 2 i , N ) ,因此。 PollarsRho P o l l a r ′ s R h o 算法能够在 O(pα(N)) O ( p ∗ α ( N ) ) 的期望时间复杂度内分解出 N N 的一个非平凡因子,其中 α(N) 是求解两个数的 gcd g c d 的时间复杂度,即分解出 N N 的一个非平凡因子的期望时间复杂度为 O(N14α(N))

  • 注意到我们只有在 gcd(xix2i,N)=1 g c d ( x i − x 2 i , N ) = 1 的情况下才会继续运行算法,我们拟定一个参数 β β ,计算每 β β xix2i x i − x 2 i 的乘积对 N N 取模的结果 prod

    gcd(prod,N)=1 g c d ( p r o d , N ) = 1 ,则说明这 β β xix2i x i − x 2 i N N gcd 均为 1 1

    gcd(prod,N)N ,那么我们就找到了 N N 的一个非平凡因子 gcd(prod,N)

    否则,即 gcd(prod,N)=N g c d ( p r o d , N ) = N ,我们不知道此时我们是否需要重新进行算法,需要回到 β β 次操作之前,进行原本的 PollarsRho P o l l a r ′ s R h o 算法。

  • 此时, PollarsRho P o l l a r ′ s R h o 算法产生一次有效的分解的期望时间复杂度被优化至了 O(N14+N14α(N)β+βα(N)) O ( N 1 4 + N 1 4 ∗ α ( N ) β + β ∗ α ( N ) )

【代码】


#include<bits/stdc++.h>

using namespace std;
const int MAXN = 100005;
const int p[9] = {2, 3, 5, 7, 11, 13, 17, 19, 23};
typedef long long ll;
typedef long double ld;
template <typename T> void chkmax(T &x, T y) {x = max(x, y); }
template <typename T> void chkmin(T &x, T y) {x = min(x, y); } 
template <typename T> void read(T &x) {
  x = 0; int f = 1;
  char c = getchar();
  for (; !isdigit(c); c = getchar()) if (c == '-') f = -f;
  for (; isdigit(c); c = getchar()) x = x * 10 + c - '0';
  x *= f;
}
template <typename T> void write(T x) {
  if (x < 0) x = -x, putchar('-');
  if (x > 9) write(x / 10);
  putchar(x % 10 + '0');
}
template <typename T> void writeln(T x) {
  write(x);
  puts("");
}
ll times(ll a, ll b, ll P) {
  ll tmp = (ld) a * b / P;
  return ((a * b - tmp * P) % P + P) % P;
}
ll mns(ll a, ll b) {
  if (a >= b) return a - b;
  else return b - a;
}
ll gcd(ll a, ll b) {
  if (b == 0) return a;
  else return gcd(b, a % b);
}
ll power(ll a, ll b, ll P) {
  if (b == 0) return 1;
  ll tmp = power(a, b / 2, P);
  if (b % 2 == 0) return times(tmp, tmp, P);
  else return times(a, times(tmp, tmp, P), P);
}
bool prime(ll n) {
  for (int i = 0; i <= 8; i++) {
      if (p[i] == n) return true;
      else if (p[i] > n) return false;
      ll tmp = power(p[i], n - 1, n), tnp = n - 1;
      if (tmp != 1) return false;
      while (tmp == 1 && tnp % 2 == 0) {
          tnp /= 2;
          tmp = power(p[i], tnp, n);
          if (tmp == n - 1) break;
          if (tmp != 1) return false;
      }
  }
  return true;
}
ll brent(ll n, int steps, int add) {
  if (n % 2 == 0) return 2;
  ll x = 2, y = 2, d = 1;
  while (true) {
      ll tmpx = x, tmpy = y;
      for (int i = 1; i <= steps; i++) {
          x = times(x, x, n) + add;
          if (x >= n) x -= n;
          y = times(y, y, n) + add;
          if (y >= n) y -= n;
          y = times(y, y, n) + add;
          if (y >= n) y -= n;
          d = times(d, mns(x, y), n);
      }
      d = gcd(n, d);
      if (d == 1) continue;
      if (d != n) return d;
      x = tmpx, y = tmpy;
      for (int i = 1; i <= steps; i++) {
          x = times(x, x, n) + add;
          if (x >= n) x -= n;
          y = times(y, y, n) + add;
          if (y >= n) y -= n;
          y = times(y, y, n) + add;
          if (y >= n) y -= n;
          d = gcd(n, mns(x, y));
          if (d != 1) return d % n;
      }
      return 0;
  }
}
ll work(ll n) {
  if (prime(n)) return n; ll tmp = 0;
  int steps = pow(n, 0.1), add = 1;
  while (tmp == 0) tmp = brent(n, steps, add++);
  return max(work(tmp), work(n / tmp));
}
int main() {
  int T; read(T);
  while (T--) {
      ll n; read(n);
      ll tmp = work(n);
      if (tmp == n) printf("Prime\n");
      else printf("%lld\n", tmp);
  }
  return 0;
}
  • 12
    点赞
  • 37
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值