素数是一个经常的涉及到得内容,所以有必要整理出有关解决素数相关问题的算法
学习资料:Eratosthenes筛法和欧拉筛法对比 一般筛法求素数+快速线性筛法求素数 数学技巧之素数筛选
素数与素性测试 〖数学算法〗素性测试 请看Miller-Rabin算法! Miller-Rabin素数测试 Pollard-rho大整数分解
1. 素数筛法
代码1(埃氏筛法):
/* 埃氏筛法:返回n以内素数的个数, 复杂度O (nloglogn) */ int seive(int n) { //Eratosthenes (埃氏筛法) int p = 0; memset (is_prime, true, sizeof (is_prime)); is_prime[0] = is_prime[1] = false; for (int i=2; i<=n; ++i) { if (is_prime[i]) { prime[++p] = i; for (int j=2*i; j<=n; j+=i) is_prime[j] = false; } } return p; }
代码2(欧拉筛法):
/* 欧拉筛法:返回n以内素数的个数, 复杂度O (n) */ int seive(int n) { //Euler (欧拉筛法) int p = 0; memset (is_prime, true, sizeof (is_prime)); is_prime[0] = is_prime[1] = false; for (int i=2; i<=n; ++i) { if (is_prime[i]) prime[++p] = i; for (int j=1; j<=p && i*prime[j]<=n; ++j) { is_prime[i*prime[j]] = false; if (i % prime[j] == 0) break; } } return p; }
附上比较两种筛法的测试结果(新标签查看原图)
2. 素性测试
代码1:
/* 素性测试,输入正数,复杂度O (sqrt(n)) */ bool is_prime(int n) { for (int i=2; i*i<=n; ++i) { if (n % i == 0) return false; } return n != 1; //1例外 }
代码2:
/* 素性测试,在小范围(1e5)内判素数个数以及单个数判素数有奇效,不适用于大范围判素数 */ bool is_prime(int x) { if (x == 2 || x == 3) return true; if (x % 6 != 1 && x % 6 != 5) return false; for (int i=5; i*i<=x; i+=6) { if (x % i == 0 || x % (i + 2) == 0) return false; } return true; }
代码3(Miller_Rabin 随机算法):
/* 素性测试,Miller_Rabin 随机算法 可以判断< 2^63的数 素数:true,合数:false */ const int S = 20; //随机算法判定次数,S越大,判错概率越小 //非递归写法,递归写法可能会爆栈 ll GCD(ll a, ll b) { if (a == 0) return 1; if (a < 0) a = -a; while (b) { ll c = a % b; a = b; b = c; } return a; } //计算 (a * b) % p,a,b是long long数,直接相乘可能会溢出 ll multi_mod(ll a, ll b, ll p) { ll ret = 0; a %= p; b %= p; while (b) { if (b & 1) { ret += a; if (ret >= p) ret -= p; } a <<= 1; if (a >= p) a -= p; b >>= 1; } return ret; } //计算 a ^ x % p ll pow_mod(ll a, ll x, ll p) { ll ret = 1; a %= p; while (x) { if (x & 1) ret = multi_mod (ret, a, p); a = multi_mod (a, a, p); x >>= 1; } return ret; } /* 以a为基,n-1=x*2^t,a^(n-1) = 1(mod n) 验证n是不是合数 一定是合数返回true, 不一定返回false */ bool check(ll a, ll n, ll x, int t) { ll ret = pow_mod (a, x, n); ll last = ret; for (int i=1; i<=t; ++i) { ret = multi_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 true; if (n < 2 || ! (n & 1)) return false; //偶数或1 ll x = n - 1; int t = 0; while (! (x & 1)) { x >>= 1; t++; } for (int i=1; i<=S; ++i) { ll a = rand () % (n - 1) + 1; //需要cstdlib头文件 if (check (a, n, x, t)) return false; //合数 } return true; }
3. 整数分解
代码1:
/* 整数分解,试除法进行质因子分解,复杂度O(sqrt(n)) */ map<int, int> factorize(int n) { map<int, int> res; for (int i=2; i*i<=n; ++i) { while (n % i == 0) { ++res[i]; n /= i; } } if (n != 1) res[n] = 1; return res; }
代码2:
/* 整数分解,先打个素数表优化试除法 */ vector<int> factorize(int n) { int p = seive (100000); vector<int> ret; for (int i=1; i<=p && prime[i]<=n/prime[i]; ++i) { while (n % prime[i] == 0) { n /= prime[i]; ret.push_back (prime[i]); } } if (n != 1) ret.push_back (n); return ret; }
代码3(Pollard_rho 随机算法):
/* 大整数分解,Pollard_rho 随机算法 factorize ()保存质因数在vector */ ll Pollard_rho(ll x, ll c) { ll i = 1, k = 2; ll a = rand () % x; ll b = a; while (1) { i++; a = (multi_mod (a, a, x) + c) % x; ll d = GCD (b - a, x); if (d != 1 && d != x) return d; if (b == a) return x; if (i == k) b = a, k += k; } } void factorize(ll n, vector<ll> &ret) { //ret保存质因数,无序 if (Miller_Rabin (n)) { //素数 ret.push_back (n); return ; } ll p = n; while (p >= n) p = Pollard_rho (p, rand () % (n - 1) + 1); factorize (p, ret); factorize (n / p, ret); } int main(void) { srand (time (NULL)); //需添加ctime头文件 int T; scanf ("%d", &T); while (T--) { ll n; scanf ("%I64d", &n); if (Miller_Rabin (n)) puts ("Prime"); else { if (n <= 1) { //1的情况特判,否则RE puts ("-1"); continue; } vector<ll> ans; factorize (n, ans); sort (ans.begin (), ans.end ()); for (int i=0; i<ans.size (); ++i) { printf ("%I64d%c", ans[i], (i == ans.size ()-1) ? ' ' : ' '); } } } return 0; }
其他:
代码:
/* 约数枚举,复杂度O (sqrt(n)) */ vector<int> divisor(int n) { vector<int> ret; for (int i=1; i*i<=n; ++i) { if (n % i == 0) { ret.push_back (i); if (n / i != i) ret.push_back (n / i); } } return res; }