对于整数的分解,是指将整数拆成质数乘积的形式。
Birthday Trick :
我们从 [1, 10000] 中随机取出一个数,取到想要的那个数的概率为
110000
。
对这个问题进行修改,我们从[1, 10000] 中随机取出两个数,取到这两个数的差为我们要的那个数的概率近似为
15000
。
从选一个数到选两个数得到我们要的数的概率就提高了。
那么我们还可以选择更多的数,来使得我们的到我们要的数的概率大大提高!
引用:
生日悖论
生日悖论,指如果一个房间里有23个或23个以上的人,那么至少有两个人的生日相同的概率要大于50%。这就意味着在一个典型的标准小学班级(30人)中,存在两人生日相同的可能性更高。对于60或者更多的人,这种概率要大于99%。从引起逻辑矛盾的角度来说生日悖论并不是一种悖论,从这个数学事实与一般直觉相抵触的意义上,它才称得上是一个悖论。大多数人会认为,23人中有2人生日相同的概率应该远远小于50%。计算与此相关的概率被称为生日问题,在这个问题之后的数学理论已被用于设计著名的密码攻击方法:生日攻击。
由生日悖论转到对整数分解上。
我们选出 x1, x2, x3, x4, x5 …… xn 个数,其中两个的差是 n 的因数或者与 n 的最大公因数大于1。
对于第一种,我们要考虑的是固定的 p 和 q 整除 N,只有两个。
对于第二种,我们要考虑的是组成的数和 N 的最大公约数是否大于 1。
显然第二种情况更能找出因数。
因此我们的方法是在 [2, n-1] 中选取 k 个数,如果 gcd(xi - xj, N) > 1 ,那么我们就找到了相应的因数。
在这个方法中,需要给出一个神奇的函数
f(x)=(x2+a)modN
.
这个函数可以找出伪随机数,但是也会出现循环圈。如果出现了循环圈,我们更改一下初始点就好了。
代码:
#include <cstdio>
#include <iostream>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <ctime>
#define ll long long
using namespace std;
const int S = 8;//随机算法判定次数,一般8~10次就够了
//计算ret = (a*b)%c a, b, c < 2^63
ll mult_mod(ll a, ll b, ll c) {
a %= c;
b %= c;
ll ret = 0;
ll tem = a;
while (b) {
if (b & 1) {
ret += tem;
if (ret > c) {
ret -= c;
}
}
tem <<= 1;
if (tem > c) {
tem -= c;
}
b >>= 1;
}
return ret;
}
//计算 ret = (a^n) % mod
ll pow_mod(ll a, ll n, ll mod) {
ll ret = 1;
ll tem = a % mod;
while (n) {
if (n & 1) {
ret = mult_mod(ret, tem, mod);
}
tem = mult_mod(tem, tem, mod);
n >>= 1;
}
return ret;
}
// 通过 a^(n-1)=1(mod n)来判断n是不是素数
// n-1 = x * 2^t 中间使用二次判断
// 是合数返回true,不一定是合数返回false
bool check(ll a, ll n, ll x, ll t) {
ll ret = pow_mod(a, x, n);//a^x % n
ll last = ret;
for (int i = 1; i <= t; ++i)//进行t次(a^x % n)^2 % n
{
ret = mult_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) {
if (n < 2) {
return false;
}
if (n == 2) {
return true;
}
if ((n & 1) == 0) {
return false;
}
ll x = n - 1;
ll t = 0;
while ((x & 1) == 0) {
x >>= 1;
t++;
}
srand(time(NULL));
for (int i = 0; i < S; ++i) {
ll a = rand() % (n - 1) + 1;
if (check(a, n, x, t)) {
return false;
}
}
return true;
}
int tol;//质因数的个数,编号为0~tol-1
ll factor[100];//质因素分解结果(刚返回时是无序的)
ll gcd(ll a, ll b) {
ll t;
while (b) {
t = a;
a = b;
b = t % b;
}
if (a >= 0) {
return a;
}
return -a;
}
//找出一个因子
ll pollard_rho(ll x, ll c) {
ll i = 1, k = 2;
srand(time(NULL));
ll x0 = rand() % (x - 1) + 1;//产生随机数x0(并控制其范围在1 ~ x-1之间)
ll y = x0;
while (1) {
i++;
x0 = (mult_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;
}
}
}
//对n进行素因子分解,存入factor。 k设置为107左右即可
void findfac(ll n, int k) {
if (n == 1) {
return;
}
if (Miller_Rabin(n))//是素数就把这个素因子存起来
{
factor[tol++] = n;
return;
}
int c = k;
ll p = n;
while (p >= n) {
p = pollard_rho(p, c--);
}
findfac(p, k);
findfac(n / p, k);
}
int main() {
int T;
ll n;
scanf("%d", &T);
while (T--) {
scanf("%lld", &n);
if (Miller_Rabin(n)) cout << "Prime" << endl;
else {
tol = 0;
findfac(n, 107);
ll ans = factor[0];
for (int i = 1; i < tol; ++i)
ans = min(ans, factor[i]);
cout << ans << endl;
}
}
return 0;
}