理论依据来源于费马小定理,思想在于不断选取 [2,n-1] 中的基 a 并检验每次是否有 a^(n-1)==1 (mod n) 。
1 2 3 4 5 6 7 8 9 10 11
// C++ Version boolmillerRabin(int n){ if (n < 3) return n == 2; // test_time 为测试次数,建议设为不小于 8 // 的整数以保证正确率,但也不宜过大,否则会影响效率 for (int i = 1; i <= test_time; ++i) { int a = rand() % (n - 2) + 2; if (quickPow(a, n - 1, n) != 1) return0; // 快速幂 } return1; }
然而其正确性并不能保证。
Miller-Rabin 素性测试
即上述说提到的。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
// C++ Version boolmillerRabin(int n){ if (n < 3 || n % 2 == 0) return n == 2; int a = n - 1, b = 0; while (a % 2 == 0) a /= 2, ++b; // test_time 为测试次数,建议设为不小于 8 // 的整数以保证正确率,但也不宜过大,否则会影响效率 for (int i = 1, j; i <= test_time; ++i) { int x = rand() % (n - 2) + 2, v = quickPow(x, a, n); // 快速幂 if (v == 1) continue; for (j = 0; j < b; ++j) { if (v == n - 1) break; v = (longlong)v * v % n; } if (j >= b) return0; } return1; }
typedeflonglong ll; constint N = 1e5 + 7; ll x, y, a[N]; ll max_factor; structBigIntegerFactor { conststaticint N = 1e6 + 7; ll prime[N], p[N], fac[N], sz, cnt; //多组输入注意初始化cnt = 0 inline ll mul(ll a, ll b, ll mod){ //WA了尝试改为__int128或慢速乘 if (mod <= 1000000000) return a * b % mod; return (a * b - (ll)((longdouble)a / mod * b + 1e-8) * mod + mod) % mod; } voidinit(int maxn){ int tot = 0; sz = maxn - 1; for (int i = 1; i <= sz; ++i) p[i] = i; for (int i = 2; i <= sz; ++i) { if (p[i] == i) prime[tot++] = i; for (int j = 0; j < tot && 1ll * i * prime[j] <= sz; ++j) { p[i * prime[j]] = prime[j]; if (i % prime[j] == 0) break; } } } ll qpow(ll a, ll x, ll mod){ ll res = 1ll; while (x) { if (x & 1) res = mul(res, a, mod); a = mul(a, a, mod); x >>= 1; } return res; } boolcheck(ll a, ll n){ //二次探测原理检验n ll t = 0, u = n - 1; while (!(u & 1)) t++, u >>= 1; ll x = qpow(a, u, n), xx = 0; while (t--) { xx = mul(x, x, n); if (xx == 1 && x != 1 && x != n - 1) returnfalse; x = xx; } return xx == 1; } boolmiller(ll n, int k){ if (n == 2) returntrue; if (n < 2 || !(n & 1)) returnfalse; if (n <= sz) return p[n] == n; for (int i = 0; i <= k; ++i) { //测试k次 if (!check(rand() % (n - 1) + 1, n)) returnfalse; } returntrue; } inline ll gcd(ll a, ll b){ return b == 0 ? a : gcd(b, a % b); } inline ll Abs(ll x){ return x < 0 ? -x : x; } ll Pollard_rho(ll n){ //基于路径倍增的Pollard_Rho算法 ll s = 0, t = 0, c = rand() % (n - 1) + 1, v = 1, ed = 1; while (1) { for (int i = 1; i <= ed; ++i) { t = (mul(t, t, n) + c) % n; v = mul(v, Abs(t - s), n); if (i % 127 == 0) { ll d = gcd(v, n); if (d > 1) return d; } } ll d = gcd(v, n);
if (d > 1) return d; s = t; v = 1; ed <<= 1; } } voidgetfactor(ll n){ //得到所有的质因子(可能有重复的) if (n <= sz) { while (n != 1) fac[cnt ++ ] = p[n], n /= p[n]; max_factor = max_factor > p[n] ? max_factor : p[n]; return; } if (miller(n, 6)) { fac[cnt ++ ] = n; max_factor = max_factor > n ? max_factor : n; } else { ll d = n; while (d >= n) d = Pollard_rho(n); getfactor(d); getfactor(n / d); } return ; } } Q; intmain(){ //Q.init(N - 1);//如果代码超时且仅需要分解大数的质因数可以用这句话,否则不要用 ll T, n; scanf("%lld", &T); while (T--) { max_factor = -1; scanf("%lld", &n); Q.getfactor(n); if(max_factor == n) puts("Prime"); elseprintf("%lld\n", max_factor); } return0; }