Link
https://www.luogu.org/problemnew/show/P4718
Miller-Rabbin
用於快速檢測一個大數 是否爲素數。
大家都知道費馬小定理 當 是素數
就有人猜想,只要存在 那麼 就是素數……當然這是錯誤的
那有人猜想,只要任意 那麼 就是素數……但是這也是錯誤的
但是起碼反例變少了?
二次探測: ( 是素數, ) 的解有且只有
利用二次探測定理,如果存在 且 ≠ 那麼 不是素數
然後每次隨機一個 ,先費馬
然後再根據 和 二次探測。
比較常見的寫法是選取前 ⑨ 個素數(2 ~ 23)作爲 來探測。(還可以(比較)快速判掉小的情況
具體步驟:隨機 ,逐個除去 的 ,判斷相鄰兩個是否滿足二次探測
Pollard-ρ
分解大合數 。
生日悖論的思想核心在於組合
選取 ;如果 就找到了一個 的因數 。
令 完全由 決定那麼顯然一定會循環。根據生日悖論循環節期望
我們取 ,枚舉 ,假設現在 沒被篩出來的最小質因數 。
(之所以考慮最小的那個,是因爲……模它循環節最小啊,先搞出它的概率大)
並且 循環得一定(大概?)比 早,於是就有 。
這個時候求 就可以得到 了。
因爲 循環節期望 並且 期望大約 所以期望是 的
在 Pollard-ρ 裏面,我們選取 ,然後 ,這樣的話咱沒法判斷出 所以得特判
(沒法判斷出 的證明:注意到模 意義下只會產生 或者 )
一個可以優化的點在於注意到我們繼續枚舉 當且僅當
那可以批量 gcd 來玄學加速。效率貌似不錯?一般似乎是取 作爲一次批量求 gcd 的步長
(雖然看上去貌似“只省了”求 gcd 而且直觀感覺上好像還多算了;不過搞 其實挺慢的(大鈣)
具體步驟:代碼清楚可愛
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<ctime>
#include<cctype>
using namespace std;
#define getchar() (frS==frT&&(frT=(frS=frBB)+fread(frBB,1,1<<12,stdin),frS==frT)?EOF:*frS++)
char frBB[1<<12], *frS=frBB, *frT=frBB;
template<typename T>
inline void read(T& x)
{
x=0;char ch=getchar();bool w=0;
while(!isdigit(ch))w|=(ch=='-'),ch=getchar();
while(isdigit(ch))x=x*10+(ch^48),ch=getchar();
w?(x=-x):0;
}
inline long long gcd(long long a, long long b)
{
return !b?a:gcd(b,a%b);
}
inline void exgcd(long long a, long long b, long long& d, long long& x, long long& y)
{
if (!b) { d = a; x = 1; y = 0; return;}
exgcd(b, a%b, d, y, x); y -= x * (a / b);
}
inline long long ReMul(long long a, const long long b, const long long& p)
{
static long long t;
t = (long double) a * b / p;
return ((a * b - t * p) % p + p) % p;
}
inline long long qpowm(long long a, long long b, const long long& p)
{
if (b <= 0) return 1;
long long ret = 1;
while (b)
{
if (b & 1) ret = ReMul(ret, a, p);
a = ReMul(a, a, p);
b >>= 1;
}
return ret;
}
long long Prime[9] = {2, 3, 5, 7, 11, 13, 17, 19, 23};
long long PowList[80];
inline bool Miller(const long long& x)
{
long long tem;
for (register int i = 0; i < 9; ++i)
{
if (Prime[i] == x) return true;
if (Prime[i] > x) return false;
tem = x - 1;
PowList[0] = 0;
while (tem % 2 == 0) ++PowList[0], tem /= 2;
PowList[++PowList[0]] = qpowm(Prime[i], tem, x);
for (register int j = PowList[0] - 1; j >= 1; --j)
{
PowList[j] = ReMul(PowList[j+1], PowList[j+1], x);
}
for (register int j = 1; j <= PowList[0]; ++j)
{
if (PowList[j] == x - 1) break;
if (PowList[j] != 1) return false;
}
}
return true;
}
#define SeMul(a, b) a = ReMul(a, a, b)
inline long long IdAbs(const long long& x)
{
return (x<0)?(-x):x;
}
inline long long Pollard_Find(const long long& N, const int& Step, const int& Alpha)
{
if (!(N&1)) return 2;
register long long LastX, LastY, x = 2, y = 2, Prod;
while (true)
{
LastX = x, LastY = y, Prod = 1;
for (register int i = 1; i <= Step; ++i)
{
SeMul(x, N), x += Alpha; (x>=N)?(x-=N):0;
SeMul(y, N), y += Alpha; (y>=N)?(y-=N):0;
SeMul(y, N), y += Alpha; (y>=N)?(y-=N):0;
Prod = ReMul(Prod, IdAbs(x - y), N);
}
Prod = gcd(N, Prod);
if (Prod == 1) continue;
if (Prod != N) return Prod; // Prod(Before GCD) == 0
x = LastX, y = LastY;
for (register int i = 1; i <= Step; ++i)
{
SeMul(x, N), x += Alpha; (x>=N)?(x-=N):0;
SeMul(y, N), y += Alpha; (y>=N)?(y-=N):0;
SeMul(y, N), y += Alpha; (y>=N)?(y-=N):0;
Prod = gcd(N, IdAbs(x - y));
if (Prod == 1) continue;
if (Prod == N) return 0;
return Prod;
}
//WHY
exit(19260817);
}
}
inline long long Pollard_Init(const long long& N)
{
if (Miller(N)) return N;
int Step = pow(N, 0.1), Alpha = 1; long long P = 0;
while (!P) P = Pollard_Find(N, Step, Alpha), ++Alpha;
return max(Pollard_Init(P), Pollard_Init(N/P));
}
int main()
{
int T;
long long n, p;
read(T);
while(T--)
{
read(n);
p = Pollard_Init(n);
if (p == n) puts("Prime");
else printf("%lld\n", p);
}
return 0;
}