P58 3-5 厄拉多塞筛
百度:
埃拉托色尼选筛法(the Sieve of Eratosthenes)简称埃氏筛法,是古希腊数学家埃拉托色尼提出的一种筛选法。 是针对自然数列中的自然数而实施的,用于求一定范围内的质数。步骤如下:
(1)先把1删除(现今数学界1既不是质数也不是合数)
(2)读取队列中当前最小的数2,然后把2的倍数删去
(3)读取队列中当前最小的数3,然后把3的倍数删去
(4)读取队列中当前最小的数5,然后把5的倍数删去
(5)如上所述直到需求的范围内所有的数均删除或读取
原程序在处理边界上有误,做了一定修改:
void EratosthenesPrime()
{
init();
for(int i = 2; i < N; ++ i)
{
if(b[i])
{
prime[prime_len ++] = i;
for(int j = i; j < (N + i - 1) / i; ++ j)
b[i * j] = 0, ++ step;
}
}
}
该算法的复杂度是
F(n)=n/2+n/3+n/5+...
<
H(n)
<
nln(n)
为了检验算法复杂度,引入了count计数值,并且与线性筛法做了对比
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <algorithm>
using namespace std;
#define N 100
#define CL(p, val) memset(p, val, sizeof(p))
int n, m;
bool b[N];
int prime[100000];
int prime_len;
int step;
void init()
{
for(int i = 2; i < N; ++ i)
{
b[i] = 1;
}
prime_len = 0;
step = 0;
}
void EratosthenesPrime()
{
init();
for(int i = 2; i < N; ++ i)
{
if(b[i])
{
prime[prime_len ++] = i;
for(int j = i; j < (N + i - 1) / i; ++ j)
b[i * j] = 0, ++ step;
}
}
printf("%d %d\n", prime_len, step);
}
void cd_prime() // 线性算法
{
init();
for(int i = 2; i < N; ++ i)
{
if(b[i])
prime[prime_len ++] = i;
for(int j = 0; j < prime_len; ++ j)
{
int k = prime[j] * i;
++ step;
if(k >= N)
break;
b[k] = 0;
if(i % prime[j] == 0)
break;
}
}
printf("%d %d\n", prime_len, step);
}
int main()
{
freopen("in.txt", "r", stdin);
freopen("out.txt", "w", stdout);
EratosthenesPrime();
cd_prime();
return 0;
}
线性筛法的优点是,每个待处理的自然数只被扫描到一次,几乎可以收敛在
O(n)
上。考虑到乘法所占指令时间较长,在测试时将count值加在prime[j]*i之后。
以下是N=100,1000,10000,100000,1000000时的结果
N | E筛法 | 线性筛法 |
---|---|---|
100 | 102 | 139 |
1000 | 1409 | 1497 |
10000 | 16979 | 15466 |
100000 | 193076 | 157396 |
1000000 | 2122046 | 1591421 |
可以看到,随着N规模的扩大,线性筛法逐渐体现出复杂度上的优势;但原筛法 F(n) 相当接近线性筛法的复杂度,也不失为一种良好的算法。