质数
1只包含 1 1 1和本身两个约数
1. 判断质数
试除法,只需枚举 2 ∼ n 2 \sim \sqrt{n} 2∼n即可
- 代码:
bool is_prime(int n)
{
if (n < 2) return false;
for (int i = 2; i <= n / i; i ++ )
if (x % i == 0)
return false;
return true;
}
- 细节
- 不推荐写 n \sqrt{n} n,因为要调用函数,太慢。
- 不推荐写 i × i < = n i \times i <= n i×i<=n,当 n n n太大时, i × i i \times i i×i有可能溢出。
2. 分解质因数
n n n最多只含一个 > n >\sqrt{n} >n的质因子,仍使用试除法,遇到约数就约掉。如果约掉了所有 < n <\sqrt{n} <n的质因子之后,剩余的数仍大于 1 1 1,则剩下的数必是质因子,单独处理即可。
代码:
void divide(int n)
{
for (int i = 2; i <= n / i; i ++ )
if (n % i == 0)
{
// 约掉所有因子,易证从小到大约掉的因子必是质因子
int s = 0;
while (x % i == 0) x /= i, s ++ ;
cout << i << ' ' << s << endl; // 输出质因子及其个数
}
if (x > 1) cout << x << ' ' << 1 << endl; // 单独处理最后一个因子
cout << endl;
}
3. 筛质数
给定一个范围 1 ∼ n 1 \sim n 1∼n,筛掉范围内所有的质数。
(1) 朴素筛法 → O ( n × log n ) \to O(n \times \log n) →O(n×logn)
-
思路:从 2 ∼ n 2 \sim n 2∼n枚举,依次筛掉 2 2 2的倍数、 3 3 3的倍数、 4 4 4的倍数,以此类推。
(2) 埃氏筛法 → O ( n × log ( log n ) ) \to O(n \times \log(\log n)) →O(n×log(logn))
- 优化:
只筛掉质数的倍数,例如在(1)中枚举到 4 4 4,由于 4 4 4已经在 2 2 2的时候就被筛掉了,就跳过 4 4 4不再筛 4 4 4的倍数了。显然,如果枚举到某个数 i i i,且 i i i之前没有被筛掉,那么 i i i必是质数。
- 缺点:仍旧还是有些数被重复筛了,例如 6 6 6会被 2 2 2和 3 3 3同时筛掉,效率仍待提高。
(3) 线性筛法 → O ( n ) \to O(n) →O(n)
-
优化:每个数只会被自己最小的质因子筛掉,例如上面 6 6 6只会被 2 2 2筛掉。
-
思路:
- i m o d p j = 0 → p j i \bmod p_j = 0 \to p_j imodpj=0→pj一定是 i i i的最小质因子, p j p_j pj也一定是 p j × i p_j \times i pj×i的最小质因子。
- i m o d p j ≠ 0 → p j i \bmod p_j \neq 0 \to p_j imodpj=0→pj一定小于 i i i的所有质因子, p j p_j pj也一定是 p j × i p_j \times i pj×i的最小质因子。
- 按照此逻辑来决定一个数一个数地筛。
- 代码:
int primes[N], cnt; // primes[]存储所有素数
bool st[N]; // st[x]存储x是否被筛掉
void get_primes(int n)
{
for (int i = 2; i <= n; i ++ )
{
if (!st[i]) primes[cnt ++ ] = i;
for (int j = 0; primes[j] <= n / i; j ++ )
{
st[primes[j] * i] = true;
if (i % primes[j] == 0) break;
}
}
}