先看Eratosthenes筛:
思路是遍历2~n,每次筛去当前质数的倍数,时间复杂度O(n log log n)
bool label[n]; // 合数为 true,数的范围为2~n
void Eratosthenes(int n)
{
for (int i = 2; i * i <= n; i++)
if (!label[i])//未被标记(为质数)
for (int j = i; j <= n / i; j++)
//j为倍数,若倍数小于i则已经被前面的数标记过,所以j从i开始
label[i * j] = true;//将其倍数标记为合数
}
这种方法有个缺点,有些数会被多次筛掉
例如,遍历到2会筛去6,遍历到3也会筛去6
改进后的方法就是线性筛:
每个合数都仅被其最小的质因数筛掉,时间复杂度O(n)
为了做到这一点需要改变循环顺序、增加一个记录已获得质数的数组
为了保证是最小质因数,从小到大遍历当前质数数组,将i的质数倍标记为合数。
重点是要在i % prime[j] == 0时break,以跳出遍历质数数组的循环
原因:存在 i % prime[j] == 0 说明prime[j]是i的最小质因数,因此后续循环的i*prime[j]会在外层i循环的后续循环中被当前循环的prime[j]筛掉
vector<int> prime;//质数数组
bool label[n];
void linear(int n)
{
for (int i = 2; i <= n; i++)
{
if (!label[i])
for (int j = 0; j < prime.size() && prime[j] <= n / i; j++)
//为了保证是最小质因数,从小到大遍历当前质数数组
{
label[i * prime[j]] = true;//i的质数倍标记为合数
if (i % prime[j] == 0) break;
//存在 i % prime[j] == 0 说明prime[j]是i的最小质因数
//因此后续循环的i*prime[j]会在外层i循环的后续循环中被当前循环的prime[j]筛掉
}
}
}