素性测试poj1811

题意:给定一个64位整数,问是否为质数,如果不是,则输出其最小因子。

分析:

经典题!!

数学题

miller_rabbin素数判定。若不是,则pollard_rho分解质因子,找到最小即可。

Miller-rabin

Miller-rabin算法是一个用来快速判断一个正整数是否为素数的算法。它利用了费马小定理,即:如果p是质数,且a,p互质,那么a^(p-1) mod p恒等于1。也就是对于所有小于p的正整数a来说都应该复合a^(p-1) mod p恒等于1。那么根据逆否命题,对于一个p,我们只要举出一个a(a<p)不符合这个恒等式,则可判定p不是素数。Miller-rabin算法就是多次用不同的a来尝试p是否为素数。

但是每次尝试过程中还做了一个优化操作,以提高用少量的a检测出p不是素数的概率。这个优化叫做二次探测。它是根据一个定理:如果p是一个素数,那么对于x(0<x<p),若x^2 mod p 等于1,则x=1或p-1。逆否命题:如果对于x(0<x<p),若x^2 mod p 不等于1,则p不是素数。根据这个定理,我们要计算a^(p-1) mod p是否等于1时,可以这样计算,设p-1=(2^t) * k。我们从a^k开始,不断将其平方直到得到a^(p-1),一旦发现某次平方后mod p等于1了,那么说明符合了二次探测定理的逆否命题使用条件,立即检查x是否等于1或p-1,如果不是则可直接判定p为合数。

 

pollard-rho

这是一个用来快速对整数进行质因数分解的算法,需要与Miller-rabin共同使用。求n的质因子的基本过程是,先判断n是否为素数,如果不是则按照一个伪随机数生成过程来生成随机数序列,对于每个生成的随机数判断与n是否互质,如果互质则尝试下一个随机数。如果不互质则将其公因子记作p,递归求解p和n/p的因子。如果n是素数则直接返回n为其素因子。

至于这个随机数序列是如何生成的暂时还不能理解,而且也是有多种不同的方式。这个序列生成过程中会产生循环,遇到循环则立即退出。

/*
    题目形式:素性测试
    
    题意:给定一个64位整数,问是否为质数,
    如果不是,则输出其最小因子。
    时间复杂度:难以估计
*/
typedef long long LL;
const int S = 20;   //进行素性测试的循环次数
const int MAXN = 10000;

LL factor[MAXN];
int tot;

LL muti_mod(LL a,LL b,LL c){ //返回(a*b) mod c [a,b,c < 2^63]
    a %= c;
    b %= c;
    LL ret = 0;
    while(b > 0){
        if(b & 1){
            ret += a;
            if(ret >= c) ret -=c;
        }
        a <<= 1;
        if(a >= c) a -= c;
        b >>= 1;
    }
    return ret;
}

LL pow_mod(LL x,LL n,LL mod){ //返回x^n mod c
   LL ret = 1;
   while(n > 0){
      if(n&1) ret = muti_mod(ret,x,mod);
      x = muti_mod(x,x,mod);
      n >>= 1;
   }
   return ret;
}










//以a为基,n-1 = x*2^t,检验n是不是合数

bool check(LL a,LL n,LL x,LL t){   
   LL ret = pow_mod(a,x,n),last = ret;
   for(int i = 1;i <= t;++i){
       ret = muti_mod(ret,ret,n);       
if(ret == 1 && last != 1 && last != n -1)
          return true;
       last = ret;
   }
   if(ret != 1) return true;
   return false;
}

bool Miller_Rabin(LL n){
    LL x = n - 1,t = 0;
    while((x&1) == 0){x >>= 1; ++t;}
    bool flag = true;
    if(t >= 1 && (x&1) == 1){
        for(int k = 0;k < S;++k){
            LL a = rand() % (n - 1) + 1;
         if(check(a,n,x,t)){flag = true;break;}
            flag = false;
        }
    }
    if(!flag || n == 2) return false;
    return true;
}

LL gcd(LL a,LL b){
//a,b其一为0的时候要特判
 //a,b为0最大公约数没有意义
   if(a == 0) return b;
   if(a < 0) return gcd(-a,b);
   while(b > 0){
      LL t = a % b;
      a = b;
      b = t;
   }
   return a;
}














LL Pollard_rho(LL x,LL c){
   LL i = 1,x0 = rand() % x,y = x0,k = 2;
   while(1){
       i++;
       x0 = (muti_mod(x0,x0,x) + c) % x;
       LL d = gcd(y - x0,x);
       if(d != 1&& d != x){
           return d;
       }
       if(y == x0) return x;
       if(i == k){
          y = x0;
          k += k;
       }
   }
}

void findfac(LL n){ //递归进行质因子数分解N
     if(!Miller_Rabin(n)){
        factor[tot++] = n;
        return;
     }
     LL p = n;
while(p >= n) p = Pollard_rho(p,
                 rand() % (n - 1) + 1);
     findfac(p);
     findfac(n / p);
}




int main()
{
   int T;
   scanf("%d",&T);
   while(T--){
       LL n;
       scanf("%I64d",&n);
       if(!Miller_Rabin(n)){
           puts("Prime");
       } else {
           tot = 0;
           findfac(n);
           LL ans = n;
           for(int i = 0;i < tot;++i)
               ans = min(ans,factor[i]);
           printf("%I64d\n",ans);
       }
   }
   return 0;
}


 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值