算法 | 埃氏筛、欧拉筛
检定素数的算法:埃氏筛 (埃拉托斯特尼筛法, Sieve of Eratosthenes)、欧拉筛 (线性筛, Euler's sieve)
埃氏筛
复杂度
- 时间复杂度:
- 空间复杂度:
代码实现
结果为一个范围从
const int MAXN = 1e5 + 10;
bool is_prime[MAXN]; // is_prime储存该数是否为素数
void init_prime()
{
memset(is_prime, true, sizeof(is_prime));
is_prime[0] = is_prime[1] = false; // 特判0、1不是素数
for (int i = 2; i < MAXN; i++) // 使用埃氏筛处理>1的情况
if (is_prime[i])
for (int j = 2; j * i < MAXN; j++)
is_prime[i * j] = false;
}
算法分析
原理
从
这个思路非常简单易懂,因此埃氏筛比下面要讲的欧拉筛好写。
举例
欧拉筛
时间复杂度
- 时间复杂度:
- 空间复杂度:
代码实现
结果为一个下标
为什么这里使用 not_prime 而不是 is_prime,其实只是为了省去全部 memset 为 true 的时间,加快效率。不过实际也不会有很大的效率区别,如果自己不习惯的话可以也改成 is_prime 版本。
// prime储存素数序列,primesize即素数数量,not_prime储存该数是否不是素数
const int MAXN = 1e5 + 10;
int prime[MAXN], primesize = 0;
bool not_prime[MAXN];
void init_prime()
{
not_prime[0] = not_prime[1] = true; // 特判0、1不是素数
for (int i = 2; i < MAXN; i++) // 使用欧拉筛处理>1的情况
{
if (!not_prime[i])
prime[primesize++] = i;
for (int j = 0; j < primesize && i * prime[j] < MAXN; j++)
{
not_prime[i * prime[j]] = true;
if (i % prime[j] == 0)
break;
}
}
}
算法分析
埃氏筛会将一个数多次筛除,如
欧拉筛只会用该数的最小质因子筛除该数,例如
该算法的灵魂就是每一步停止筛除的判断,对应到代码即 if (i % prime[j] == 0) break;
这一条。
原理
简单证明如下:
因为
接下去的素数同理,所以不用筛下去了。
因此,在满足
举例
模拟该算法的操作:从
为素数,将其加入 prime[ ],当前 prime[1] = {2}- 筛除
,停止筛除
- 筛除
为素数,将其加入 prime[ ],当前 prime[2] = {2, 3}- 筛除
- 筛除
,停止筛除
- 筛除
为合数- 筛除
,停止筛除
- 筛除
为素数,将其加入 prime[ ],当前 prime[3] = {2, 3, 5}- 筛除
- 筛除
- 到达 primesize,停止筛除
- 筛除
为合数- 筛除
,停止筛除
- 筛除
就拿在
时间复杂度对比
一个为
但在较小数据范围时,优势较小。而且线性筛内部包含有取模运算,在特定数据范围内可能速度不如埃氏筛。
本文采用 CC BY-SA 4.0 许可,本文 Markdown 源码:Haotian-BiJi
欧拉筛算法分析那里的举例,下面部分:
"""
5为素数,将其加入 prime[ ],当前 prime[3] = {2, 3, 5}
筛除 5*2
筛除 5*3
筛除 5*4
筛除 5*5
5%5==0,停止筛除
"""
实际筛除的应该没有5*4,我认为,20这个数应该会在i遍历到10的时候被筛除。
(博主的文章很棒,感谢分享~)
感谢指正,已修正。确实这种情况是到达了 primesize,循环结束就停止筛除了。