埃拉托色尼筛法及线性筛法

时间:2022-08-15 00:36:38

1.埃拉托色尼筛法

时间复杂度 O(nlogn)


希腊学者埃拉托色尼(公元前275-公元前193,第一个算出地球周长的人)提出的质数筛选法充分利用了质数的特性,非常好理解。


基本原理:任意整数 x 的倍数 2x,3x,... 等都不是质数 。


例:找出1-50内所有的质数


第一步:2为质数,标记4,6,8,10……50为合数


第二步:3为质数,标记6,9,12,15……48为合数


第三步:4已被标记为合数,标记8,12,16……48为合数


第四步:5为质数,标记10,15,20……50为合数


(从小到大执行到某个数时,若它未被标记,它即为质数)


……


不断进行下去,我们就可以标记出1-50所有的质数和合数。十分简单,所以我不提供代码实现。



2.线性筛法

时间复杂度 O(logn)


在上面的方法我们发现,有很多重复标记的现象。例如12既会被2又会被3标记,根本原因是没有找到唯一产生12的方式。


基本原理:每个合数必为它的最小质因子和一个最大因数(除它本身)的乘积。(质数的最小质因子为它本身,最大因数(除它本身)为1)


我们用这个最大因数(除它本身)把合数筛掉,避免重复。


也是从2开始筛选,每一步,将当前数与已经找出来的质数中小于等于它的最小质因子的相乘,把乘积作为合数筛掉。


例:找出1-50内所有的质数


第一步:2为质数,标记4为合数


第二步:3为质数,标记6为合数


第三步:4为合数,标记8为合数


第四步:5为质数,标记10,15为合数


(从小到大执行到某个数时,若它未被标记,它即为质数)


……


以下是线性筛法找质数的函数代码实现:

int prime[MAXN],ct=0; //存储找到的质数 
int v[MAXN]; //记录0~MAXN是否为质数

void fprime(int b) //线性筛法找质数
{
for(int i=2;i<=b;i++) //找到2到b内的所有质数
{
if(v[i]==0) //v[i]没被筛过,v[i]为质数
prime[++ct]=i; //储存第cnt个质数
for(int j=1;j<=ct;j++) //遍历小于i的所有质数
{
if(i*prime[j]>b) break; //无需判断比b大的数是质数还是偶数
v[i*prime[j]] = 1; //标记不为质数的数
if(i%prime[j]==0) break; //当遇到最小的质数是i的因数时,break
}
}
}