素数筛法
素数定理:\(\pi(x) = \frac{x}{\ln(x)}\),即不超过 \(x\) 的素数的个数接近于 \(\frac{x}{\ln(x)}\)。
朴素筛
void isPrime(int n)
{
for (int i = 2; i <= n; ++ i)
{
k = sqrt(i); flag = false;
for (int j = 2; j <= k; ++ j)
{
if (i%j == 0)
{
flag = false;
break;
}
}
if (flag) isPrime[++ PrimeSize] = i;
}
}
埃拉托斯特尼筛法(埃氏筛)
算法过程:
以最小的素数 \(2\) 为起始,作为一个合数的 较小 的因数,筛掉 \(2^2-\dots\) 的合数,然后判断下一个没有被筛掉的数(即为素数),重复进行即可。
注意: 从一个质数的 平方 开始筛。
时间复杂度为 \(O(n\log(\log n))\) (推导过程)。
优化:
(1)当用 \(\sqrt n\) 筛的时候,已经是可以筛到 \(n\) 这个数字了。另外,在 \(\sqrt n\) 到 \(n\) 的数中,它的最小因数一定是小于等于 \(n\) 的,所以枚举时,只需枚举到 \(\sqrt n\) 即可。
当然 还有许多优化:(2)只筛奇数、(3)分块筛选 \(\dots\dots\)
实现
void AISHI(int n)
{
m = sqrt(n);
memset(Prime,true,sizeof Prime);
for (int i = 2; i <= m; ++ i)
{
if (Prime[i]) isPrime[++ isPrimeSize] = i;
for (int j = i*i; j <= n; ++ j)
{
Prime[j] = false;
}
}
}
线性筛(欧拉筛法)
前提:
在埃氏筛中,一个合数可能会被它的多个质数因数 筛掉多次,例如:\(12\) 会被 \(2\) 和 \(3\) 筛掉,相当于其他合数,被筛掉多次的次数大大增加。
为了减少这种损失,最好是用 最小 的因数 去筛掉这个数。
线性筛也用于 欧拉函数 的求解过程中,也称作 欧拉筛法。
实现过程:
设 \(i\) 作为某一个合数的 最大 因数,枚举比 \(i\) 小的所有质数 \(k_j\),作为最小因数,将 \(i\times k_j\) 筛掉。
又因为 \(i\) 可能为一个合数,所以当出现 \(i\bmod k_j = 0\) 的时候,\(i\) 中存在一个与 \(k_j\) 相等的因数。那么在枚举比 \(k_j\) 大的质数 \(k_l\) 时,\(k_l\) 就不是 \(i\times k_l\) 的最小因数了,存在一个数 \(y=\frac{i}{k_j}\) 使得 \(i*k_l = (y*k_j)\times k_l\),由此就可以将 \(i\) 的循环结束掉。
那么 \(k_l\times i\) 这个合数,一定会在 \(k_j\times (\frac{i}{k_j}\times k_l)\) 的时候被筛掉,此时 \(k_j\) 将会作为最小因子存在,而 \(\frac{i}{k_j}\times k_l\) 一定是一个比 \(i\) 大的数,所以不必担心漏掉。
void OULA(int n)
{
memset(Prime,true,sizeof Prime);
for (int i = 2; i <= n; ++ i)
{
if (Prime[i]) isPrime[++ isPrimeSize] = i;
for (int j = 1; j <= isPrimeSize && i*isPrime[j] <= n; ++ j)
{
Prime[i*isPrime[j]] = false;
if (!(i%isPrime[j])) break;
}
}
}