Miiler-Robin素数测试
目前已知分解质因数以及检测质数确定性方法就只能\(sqrt{n}\)试除
但是我们可以基于大量测试的随机算法而有大把握说明一个数是质数
Miler-Robin素数测试基于以下两个原理:
费马小定理
即我们耳熟能详的
对于质数\(p\)
\[a^{p - 1} \equiv 1 \pmod p\]
二次探测原理
对于质数\(p\),如果存在\(x\)满足
\[x^2 \equiv 1 \pmod p\]
那么\(x\)只能是\(1\)或者\(p - 1\)
由此我们便可以随机生成多个\(x\),逐一用以上两个原理检验即可
只要全都符合,我们就有大概\(1 - (\frac{1}{4})^{T}\)的把握说\(p\)是一个质数
具体操作时,令\(p = 2^{t}r + 1\),我们对于\(z = 2^{m}r\),其中\(m\)小于等于\(t\),都使用二次探测原理检验
最后再利用费马小定理检验
bool Miller_rabin(LL n){ if (n == 2 || n == 3 || n == 5 || n == 7 || n == 11 || n == 13) return true; if (n == 1 || n % 2 == 0 || n % 3 == 0 || n % 7 == 0 || n % 11 == 0 || n % 13 == 0) return false; int T = 50; LL t = n - 1,k = 0; while (!(t & 1)) t >>= 1,k++; while (T--){ LL x = qpow(random(n),t,n),y; REP(i,k){ y = x,x = mul(x,x,n); if (x == 1 && y != 1 && y != n - 1) return false; } if (x != 1) return false; } return true; }
Pollard-Rho大数分解法
我们利用式子\(x^2 + c\)伪随机生成两个数\(a\)和\(b\),判断\(d = (a - b,n)\)是否大于\(1\)小于\(n\),如果是,我们便找打了一个\(n\)的因子\(d\),递归处理\(\frac{n}{d}\)和\(d\)即可,当我们使用以上的素数判定判定出\(n\)是质数时,计入答案
当然我们伪随机生成的两个数可能成环而导致死循环,我们用\(Floyd\)的大步小步法判环即可
具体看代码
LL pr[maxn],pi; LL gcd(LL a,LL b){return b ? gcd(b,a % b) : a;} LL Pollard_Rho(LL n){ LL x = random(n),y = x,c = random(n),step = 1,t = 2; while (true){ step++; x = (mul(x,n) + c) % n; if (y == x) return 1; LL d = gcd((y - x + n) % n,n); if (d > 1) return d; if (step == t) y = x,t <<= 1; } } void Find(LL n){ if (n == 1) return; if (Miller_rabin(n)) {pr[++pi] = n; return;} LL p = n; while (p == n) p = Pollard_Rho(n); Find(n / p); Find(p); }
至此我们就可以写出POJ1811
#include<algorithm> #include<iostream> #include<cstring> #include<cstdlib> #include<cstdio> #include<cmath> #include<map> #define Redge(u) for (int k = h[u],to; k; k = ed[k].nxt) #define REP(i,n) for (int i = 1; i <= (n); i++) #define mp(a,b) make_pair<int,int>(a,b) #define cls(s) memset(s,sizeof(s)) #define cp pair<int,int> #define LL long long int using namespace std; const int maxn = 100005,maxm = 100005; const LL INF = 1000000000ll * 1000000000ll; inline LL read(){ LL out = 0,flag = 1; char c = getchar(); while (c < 48 || c > 57){if (c == ‘-‘) flag = -1; c = getchar();} while (c >= 48 && c <= 57){out = (out << 3) + (out << 1) + c - 48; c = getchar();} return out * flag; } inline LL random(LL x){ LL re = 0; REP(i,4) re = (re << 14) + rand(); return re % x; } inline LL mul(LL a,LL b,LL P){ LL re = 0; for (; b; b >>= 1,a = (a + a) % P) if (b & 1) re = (re + a) % P; return re; } inline LL qpow(LL a,LL P){ LL re = 1; for (; b; b >>= 1,a = mul(a,a,P)) if (b & 1) re = mul(re,P); return re; } bool Miller_rabin(LL n){ if (n == 2 || n == 3 || n == 5 || n == 7 || n == 11 || n == 13) return true; if (n == 1 || n % 2 == 0 || n % 3 == 0 || n % 7 == 0 || n % 11 == 0 || n % 13 == 0) return false; int T = 50; LL t = n - 1,n); if (x == 1 && y != 1 && y != n - 1) return false; } if (x != 1) return false; } return true; } LL pr[maxn],t <<= 1; } } void Find(LL n){ if (n == 1) return; if (Miller_rabin(n)) {pr[++pi] = n; return;} LL p = n; while (p == n) p = Pollard_Rho(n); Find(n / p); Find(p); } int main(){ //srand(998244353); int T = read(); LL n; while (T--){ if (Miller_rabin(n = read())) puts("Prime"); else { pi = 0; Find(n); //REP(i,pi) printf("%lld ",pr[i]); puts(""); if (pi == 1) {puts("Prime"); continue;} LL x = pr[1]; for (int i = 2; i <= pi; i++) x = min(x,pr[i]); printf("%lld\n",x); } } return 0; }