现在的位置: 首页 > 综合 > 正文

素数判定(Miller_Rabin) & 大数分解(Pollard_Pho)

2013年10月11日 ⁄ 综合 ⁄ 共 2677字 ⁄ 字号 评论关闭

    · 博主语文体育老师教的.

    · 本文年龄限定 16+

    · 吐槽上面 2条的都是⑨

    事情要从昨天考试说起.

    Pro 2 : 给定n, 求n最少能分成多少个完全平方数.

    那啥做法就不扯淡了, 各种特判. 

    核心算法的素数判定以及大数分解完全就被一大堆特判淹没了.

    推荐:

        1、《64位以内Rabin-Miller+强伪素数测试和Pollard+rho+因数分解算法的实现》 (里面的伪代码非常奇葩.... 传说中的PC语言 ?!)

        2、CSDN Fisher_jiang《Miller_Rabin素数测试》- http://blog.csdn.net/fisher_jiang/article/details/986654

        3、《算法导论》自己翻去.

    素数判定 (Miller_Rabin) 

    

    个人就不吐槽了.

    总之就是用费马小定理 a^(p - 1) ≡ 1 (mod p)  (其中 a, p 互质且 p 为质数)  确定 p 是否为质数的较高正确性.

    证明不管 (确切地说没有精力去看), 只剽算法=. =

    只要多选取几个 a , 就可以使得算法正确率非常高. 目前有以下二种方法:

        1、随机取.

        2、选取前 k 个质数.      

    第一种出错率 1 / 4^s ( s为随机选取 a 的个数),  第二种在 [1] 中有详细介绍, 取前 10 个质数就可以满足在int64范围内100%正解.

    那啥怎么求a^(p - 1) ?  快速幂...... ( 不可能有人都看到这种东西了还不会快速幂吧......)

    大数分解 (Pollard_Pho)

    极度吐槽. 让我悲剧了一下午的东西.

    总之就是随机选 2 个数a, b (b < a < n),  判断 gcd(a - b, n) 是否大于 1 就可以了.

    也许会说这种随机怎么可能那么快出解啊! 什么的.

    但是若构造一个循环节 (a1, a2, a3, a4, a5 ... ak) (在模n意义下循环),  并且使 b 也有一定规律的话.

    根据各种论文,  能在O(√p) 内找出 n 的一个因子 p (注意是因子, 不是质因子, 所以找出 p 后要递归处理)

    一般用 f(x) = x² + c 来构造循环节. 其余证明见 [1]  (个人认为应该是算导以外最好的了)

    

    以下是伪代码 :

    Function Pho(n)

        if N IS A PRIME  then                                 // n 是个质数就直接退出

          return error;

        x = y = x0;   c = random[1, n);                   //  初始化

        k = 0;   i = 1;   d = 1;

        while (true)   do

            ++k;                                                          

            x = f(x), d = gcd(x - y, n);                       //  构造 x

            if  d ∈ (1, n)  then  return d;                //   找到一个因子

            if  d = n  then c = random[1, n);          //   x - y = 0, 表示 c 这个常数因子有点萎... 换一个.  (悲剧一下午, Orz 屏屏哥)

            if  k = i  then y = x, i <<= 1;                  //   更新 y

    End Function

    以上是Pollard 的 Brent优化代码, 同样, 具体见 [1].

    下面是完全平方数分解的 Code ( 里面自带素数判定以及大数的质因数分解 )

#include <cmath>
#include <cstdio>
#include <cstdlib>
#include <iostream>
#include <algorithm>
#define error -1
#define ll long long
const int po[11] = {0, 2, 3, 5, 7, 11, 13, 17, 19, 23, 29};
using namespace std;

int test;
ll n, p[10010];

ll gcd(ll a, ll b)
{
    ll x;
    while (x = a, b) a = b, b = x % b;
    return a; 
}
ll mul(ll a, ll b, ll c)
{
    ll plas = 0;
    for (ll i = 1; i <= b; i <<= 1, a = (a + a) % c)
      if (b & i)  plas = (plas + a) % c;
    return plas;  
}
ll Rollard_Brent(ll n)
{
    ll x = 1, y = 1, d = 1;  y = x;
    int k = 0, i = 1, z = rand() + 1;
    while (true)
      {  ++k;
         x = (mul(x, x, n) + z) % n;
         d = gcd((x - y + n) % n, n);
         if (d > 1  &&  d < n)  return d;
         if (k == i)  y = x, i <<= 1;
         if (d == n) z = rand() + 1;
      }
}    
bool Miller_Rabin(ll n)
{
    ll a, w, sec;
    for (int i = 1; i <= 10; ++i)
      {   sec = 1;  if (n == po[i])  continue;
          for (a = po[i], w = 1; w < n; w <<= 1, a = a * a % n)
            if ((n - 1) & w)  sec = sec * a % n; 
          if (sec != 1)  return false;
      }    
    return true;
}
int tap(ll n)
{
    ll plas = n;
    int head = 1, tail = 1, top = 0;
    p[tail] = n; 
    while (head <= tail  &&  n != 1)
      if (Miller_Rabin(p[head]))  {  bool wis = 0; 
        while (n % p[head] == 0)  wis = !wis, n /= p[head];
        if (wis  &&  (p[head] & 3) == 3)  goto Compare;  ++head;
      }      
      else  {
        ll vec = Rollard_Brent(p[head]);
        p[++tail] = p[head] / vec;  p[++tail] = vec;  
        ++head;  }  
    return 2;
    Compare:
    while (!(plas & 3))  plas >>= 2;
    if (((plas - 7) & 7) == 0)  return 4;
    else  return 3;
}
int main()
{
    freopen("p2.in", "r", stdin);
    freopen("p2.out", "w", stdout);
    srand((unsigned)time(NULL));
    scanf("%d", &test);
    for (; test--; )
      {      
          scanf("%I64d", &n);  
          int w = (int) sqrt((double) n);  if ((ll) w * w == n)  {  printf("1\n");  continue;  }
          printf("%d\n", tap(n));
      }    
}

抱歉!评论已关闭.