Eratosthenes筛法(Sieve of Eratosthenes)
由于思想非常简单,故只给出实现。
void eratosthenes_sieve(int n)
{
totPrimes = 0;
memset(flag, 0, sizeof(flag));
int sqrtn = sqrt(n + 0.5);
for (int i = 2; i <= sqrtn; i++) {
if (!flag[i]) {
primes[totPrimes++] = i;
for (int j = i * i; j <= n; j += i) {
flag[j] = true;
}
}
}
for (int i = sqrtn + 1; i <= n; i++) {
if (!flag[i])
primes[++totPrimes] = i;
}
}
时间复杂度 。(窝不会证明)
Euler筛法(Sieve
of Euler)
欧拉筛是一种线性算法,并且同时可以计算出每个数的 。
做法
回顾经典的Eratosthenes筛法,它可能对同一个质数筛去多次。那么如果用某种方法使得每个合数只被筛去一次就变成是线性的了。
不妨规定每个合数只用其最小的一个质因数去筛,这便是欧拉筛了。
不妨先看代码:
void euler_sieve(int n)
{
totPrimes = 0;
memset(flag, 0, sizeof(flag));
for (int i = 2; i <= n; i++) {
if (!flag[i])
primes[totPrimes++] = i;
for (int j = 0; i * primes[j] <= n; j++) {
flag[i*primes[j]] = true;
if (i % primes[j] == 0)
break;
}
}
}
请仔细体会i % primes[j] == 0的含义。
时间复杂度 。
简单证明
这个看似很简单,其实还是要注意一下细节的。搞清了证明其他的问题也就清楚了。
证明分两部分。首先证每个合数都会被筛到(正确性),其次证每个合数只会被筛到一次(复杂度)。
每个合数都会被筛到
设有一合数 (为质数)
则一定会在 时被筛去(此时 ),因为对于小于 的质数,一定不会被 整除
每个合数都只会被筛到一次
与上面一样,还是设有一合数 ( 为质数)
倘若存在一个质因子 也筛去了,那么此时 。
,此时在内层循环中已经早早地break掉了,因为 。
,此时还没加进质数表QwQ(顺便一提:这种情况只有可能在 时发生)
难以理解的地方
i
% primes[j] == 0为何不放在前面?
你可以去试试……实践出真知。
放前面的话,所有的“某个质因子的次数不为1”的合数便会被当成质数。至于为什么,请看证明。
j
< totPrimes为何不加?
实践才是检验真理的唯一标准。
当 为质数时,内层循环会在最后一个质数(也就是 自己)终止。当 为合数时,内层循环会在它的第一个质因数终止。
当然加了也没有问题。
顺便把 算出来?
其实这是极简单的。
主要基于以下事实:(很容易通过定义推出来,不妨自己试试)
1.n为质数时,phi(n) = n - 1
2.p为质数且p整除n时,phi(np) = p phi(n)
3.p为质数且p不整除n时,phi(n*p) =(p - 1) * phi(n)
有没有发现简直就是为Euler筛法量身定做的!
代码:
void euler_sieve_with_phi(int n)
{
totPrimes = 0;
phi[1] = 1;
memset(flag, 0, sizeof(flag));
for (int i = 2; i <= n; i++) {
if (!flag[i]) {
primes[totPrimes++] = i;
phi[i] = i - 1;
}
for (int j = 0; i * primes[j] <= n; j++) {
flag[i*primes[j]] = true;
if (i % primes[j])
phi[i*primes[j]] = phi[i] * (primes[j] - 1);
else {
phi[i*primes[j]] = phi[i] * primes[j];
break;
}
}
}
}
速度比较
你可能会觉得 微不足道,那么你错了。
实测结果:(精确到微秒,编译时不打开优化开关)
when n = 10000
eratosthenes_sieve(1229): 0(us)
euler_sieve(1229): 0(us)
when n = 100000
eratosthenes_sieve(9592): 999(us)
euler_sieve(9592): 0(us)
when n = 1000000
eratosthenes_sieve(78498): 13004(us)
euler_sieve(78498): 7004(us)
when n = 10000000
eratosthenes_sieve(664579): 185130(us)
euler_sieve(664579): 79067(us)
when n = 100000000
eratosthenes_sieve(5761455): 2363692(us)
euler_sieve(5761455): 842592(us)
when n = 1000000000
eratosthenes_sieve(50847534): 25535159(us)
euler_sieve(50847534): 8987385(us)
差距还是蛮大的呢。
转自该大佬的博客please click here