/*
translation:
题意很简单,给定一个数,判断其是否为素数。如果不是素数,还需要给出最小的素因子。
solution:
这道题不能用常规的素数算法来做,因为肯定超时。所以需要用到一种随机性算法。
称之为Miller_robin算法。该算法可以在一定的错误概率内判断一个数是否为素数。求最小的
素因子也需要用到另外一种随机性算法pollard-rho算法。该算法可以随机性地将一个数分解因子。
但注意并非按照从小到大的顺序分解,而是随机性地分解。
note:
# 注意这道题中用到的gcd算法中的参数可能小于0,所以计算的时候注意分别讨论。
# long long的数相乘可能溢出,所以需要专门用multi_mod方法来处理。
# 关于这道题有一点需要优化,那就是multi_mod中的res -= n语句,相当与res %= n。但是后者运行时间为
1024ms,改成前者后只有436ms,因为取模运算慢。
* 关于miller_robin算法:
& 原理:
该算法是基于费马定理,即“对于a^(n-1) == 1(mod n)”如果成立,则有一定概率表明n是素数,如果不满足
则100%确定是合数。根据该原理即可测试出一个数是否为素数。(a是随机数)
& 漏洞:
该原理如果直接应用的话势必WA,原因很简单,如果测试结果是合数的话,一定正确。但是如果是素数的话,则只是
在一定几率下是正确的(虽然几率不大)。例如在a=2情况下,数字314就是一个“漏网之鱼”。
& 改进:
miller_robin算法就是对其进行了两点改进,克服了存在的问题
1--选取了多个随机值a。
2--每当计算每个模取幂时,在最后一组平方里,寻找一个以n为模的1的非平凡平方根。如果找到,说明是合数。
& 实现细节:见算导p567
date:
2016.10.25
*/
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <ctime>
using namespace std;
const int s = 20; //随机算法的运算次数
typedef long long ll;
ll n, ans;
ll multi_mod(ll a, ll b, ll n)
{
a %= n;
b %= n;
ll res = 0;
while(b)
{
if(b & 1)
{
res += a;
if(res >= n) res -= n;
}
a <<= 1;
if(a >= n) a -= n;
b >>= 1;
}
return res;
}
ll pow_mod(ll x, ll n, ll mod)
{
if(n == 1) return x % mod;
x %= mod;
ll tmp = x;
ll res = 1;
while(n > 0)
{
if(n & 1) res = multi_mod(res, tmp, mod);
tmp = multi_mod(tmp, tmp, mod);
n >>= 1;
}
return res;
}
bool witness(ll a, ll n, ll u, ll t)
{
ll res = pow_mod(a, u, n);
ll last = res;
for(int i = 0; i < t; i++)
{
res = multi_mod(res, res, n);
if(res == 1 && last != 1 && last != n-1) return true;
last = res;
}
if(res != 1) return true;
else return false;
}
bool robin_miller(ll n) //判断是否素数
{
if(n < 2) return false;
if(n == 2) return true;
if(!(n & 1)) return false;
ll u = n - 1, t = 0;
while(!(u & 1)) u >>= 1, t++;
if(t >= 1 && (u & 1) == 1)
{
for(int i = 0; i < s; i++)
{
ll a = rand() % (n-1) + 1;
if(witness(a, n, u, t)) return false; //不是素数
}
}
return true; //是素数
}
ll gcd(ll a, ll b)
{
if(a == 0) return 1;
if(a < 0) return gcd(-a, b);
while(b)
{
ll t = a % b;
a = b;
b = t;
}
return a;
}
ll pollard_rho(ll x, ll c)
{
ll i = 1, x0 = rand() % x;
ll y = x0;
ll k = 2;
for(;;)
{
i++;
x0 = (multi_mod(x0, x0, x) + c) % x;
ll d = gcd(y - x0, x); //这里传给gcd的参数可能负数,注意分别讨论
if(d != 1 && d != x) return d; //这里表明找到了一个因子
if(y == x0) return x;
if(i == k)
{
y = x0;
k += k;
}
}
}
void find_fac(ll n)
{
//if(n == 1) return;
if(robin_miller(n))
{
ans = min(ans, n);
return;
}
ll p = n;
while(p >= n) p = pollard_rho(p, rand() % (n-1) + 1);
find_fac(p);
find_fac(n / p);
}
int main()
{
srand(time(NULL));
int T;
scanf("%d", &T);
while(T--)
{
scanf("%lld", &n);
if(robin_miller(n)) printf("Prime\n");
else
{
ans = n;
find_fac(n);
printf("%lld\n", ans);
}
}
return 0;
}
poj1811(miller_robin和pollard分解因数的随机性算法)
最新推荐文章于 2022-12-26 16:51:12 发布