3. 有关素数的算法
1. 素性判定:
质数又称素数。一个大于1的自然数,除了1和它自身外,不能被其他自然数整除的数叫做质数;否则称为合数。
如果 d 是 n 的约数, 那么 n / d 也是 n 的约数,可知 min (d, n / d) ≤ √n , 那么只要检查 2 ~ √n 的整数即可。同理可知整数分解和约数枚举都可以在 0(√n) 的时间复杂度下完成。
//素性测试 bool isPrime (int n) { for (int i = 2; i * i <= n; i++) if (n % i == 0) return false; return n != 1; } //约数枚举 vector<int> divisor (int n) { vector<int> res; for (int i = 1; i * i <= n; i++) { if (n % i == 0){ res.push_back(i); if (i != n / i) res.push_back(n / i); } } return res; } //整数分解 map<int, int> primefactor (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; }
整数分解(integer factorization)又称素因数分解(prime factorization),是将一个正整数写成几个约数的乘积
整数分解 map里存放的是 n 的素约数。
2. 埃氏筛法 0(n log log n)
用来解决 n 以内有多少素数。
先将 2 到 n 范围内的所有整数打表, 其中最小的 2 是素数。 将表中所有 2 的倍数都划去。表中剩余最小的 3 是素数,再把表中所有 3 的倍数都划去。以此类推, 如果表中剩余的最小数字是 m 时, m 就是素数。 像这样反复操作,就能依次枚举 n 以内的素数。
int prime[MAX_N]; //第 i 个素数 bool isPrime[MAX_N + 1]; // true 表示 i 是素数 // 返回 n 以内素数的个数 int sieve (int n) { int p = 0; fill (isprime, isprime + n+1, true); isPrime[0] = isPrime[1] = false; for (int i = 2; i <= n; i++) { if (isPrime[i]){ prime[p++] = i; for (int j = 2 * i; j <= n; j += i) isPrime[j] = false; } } return p; }
3.区间筛法
区间 [a, b) , b 以内的合数的最小质因数一定不超过 √b 。 也就是说 如果有 √b 以内的素数表, 就可以吧埃氏筛法用在 [ a, b ) 上,也就是说, 先分别做好 [ 2, √b ) 和 [ a, b) 的表, 然后从 [ 2, √b ) 的表中筛得的素数的同时,也将其倍数从 [a, b) 的表中划去, 最后剩下的就是 [a, b) 内的素数。
typedef long long ll; //区间范围[a, b) bool is_prime[MAX_L]; bool is_prime_small[MAX_SQRT_B]; void segment_sieve(ll a, ll b) { for (int i = 0; (ll) i * i < b; i++) is_prime_small[i] = true; for (int i = 0; i < b - a; i++) is_prime[i] = true; for (int i = 2; (ll)i * i < b; i++) { if (is_prime_small[i]) { //筛(2, sqrt(b) ) for (int j = 2 * i; (ll)j * j < b; j += i) is_prime_small[j] = false; //筛 (a, b) for(ll j = max(2LL, (a + i - 1) / i) * i; j < b; j += i) is_prime[j - a] = false; } } }
人生不如意的时候,是上帝给的长假,这个时候应该好好享受假期。
突然有一天假期结束,时来运转,人生才是真正开始了。
突然有一天假期结束,时来运转,人生才是真正开始了。