埃氏筛
判断素数可通过试除小于\(\sqrt n\)的素数来实现,那么将其反过来,只要将\(<= \sqrt n\)的素数的倍数都删掉,那么就能得到一张\(<=n\)的素数表,\(O(n\lg \lg n)\)
// 需划掉合数,所以最初假设均为素数,即数组初始化为0
bool composite[maxn];
void generate(int n)
{
for(int i = 2, lim = sqrt(n); i <= lim; ++i) {
if(composite[i]) continue;
for(int j = i*i; j <= n; j += i)
composite[j] = 1;
}
for(int i = 2; i < n; ++i)
if(!composite[i]) printf("%d ", i);
putchar(‘\n‘);
}
线性筛
埃氏筛的问题在于一个合数可能会被多个素数删除,造成不必要操作。为解决该问题,注意到每个合数均可写成其最小素因数p乘C,即\(n=pC\),其中必有\(C \ge p\),因为若非如此,当C为素数时,不满足假设“p为最小素因数”;当C为合数时,其必能分解除小于C的素因数,同样不满足假设“p为最小素因数”。线性筛就是通过从小到大地遍历C来保证每个合数均被,其仅被删除一次。
// composite同样初始化为0;np是素数个数;prime保存找到的素数
bool composite[maxn];
int np, prime[maxn];
void generate2(int n)
{
// 这里的i就是上述讨论中的C
for(int i = 2; i <= n; ++i) {
if(!composite[i]) prime[np++] = i;
for(int j = 0; j<np && i*prime[j]<=n; ++j) {
composite[i*prime[j]] = 1;
// 为什么在这里break,类比完全背包思考一下 (^_^)
if(i % prime[j] == 0) break;
}
}
for(int i = 0; i < np; ++i)
printf("%d ", prime[i]);
putchar(‘\n‘);
}