欧拉筛(线性筛
老办法,我们先来看看埃式筛
一.埃式筛
埃式筛运用的是一个很简单的算法,“一个素数的整数倍一定是一个合数”,运用这个办法,我们来编写一下埃式筛的程序
const int n = 1000000;
int prime[1000010];//用来存放素数
void aishishai()
{
int i, j;
for (i=0; i<n; i++)//方便理解,是素数的初值附为1
prime[i] = 1;
prime[0] = prime[1] = 0;//再次方便理解,因为0 1不是素数,所以改成0
for (i=2; i<n; i++)//开始筛
{
if(prime[i]==0)//如果已经知道他是合数了,就没必要再乘了,直接continue开始下一次循环
continue;
for (j=i*2; j<n; j=j+i)//这里有大佬会做优化,比如j=i*i开始那种。
//但是我们这里理解的是欧拉筛,就不给宁乱脑子了
prime[ j ] = 0;
}
}
我们可以看到,假如我们要把30这个合数筛去,我们有好几种办法筛,例如310,即素数为3的时候筛掉,又可以在215,即素数为2的时候筛掉。这样很明显会浪费时间,所以埃式筛的时间复杂度是o(nloglogn)(埃式筛时间复杂度的证明过程以及埃式筛的改进有(xue)机(hui)会(le)会再发几篇。
二.欧拉筛在埃式筛上的进化
我们要改进埃式筛,就是要阻止这些筛多次的事情发生。所以我们延伸了线性筛。
首先我们要明白一个定理叫做"算数基本定理",也被叫做“整数唯一分解定理”
为了让整篇博客通俗易懂而且又不贫瘠和粗略,这里我们只给出结论,证明以及推广可
@百度百科@其他大佬的博客
算数基本定理
任何一个大于1的自然数 N,如果N不为素数,那么N可以唯一分解成有限个质数的乘积
也就是说,我们要找到一个合数的最小质因数,然后用这个最小质因数来把他筛掉,就可了。
最小质因数
一个合数分解出的有限个素数乘积中,最小的那个素数
三.欧拉筛
接下来我们正式进入欧拉筛的代码实现
1.代码思路
1.我们要找到素数
2.确保这个素数是我们要筛的合数的最小质因数
3.利用埃式筛的最基本思想筛掉一些合数
2.代码实现
#include<stdio.h>
int prime[1010];//存放素数
bool vis[1000000];//检验素数,vis[i]=0代表i是素数, vis[i]=1代表i不是素数
int main()
{
int t=0;//1是为了让p[1010]这个数组连续存放素数,自己定义的下标(不用i;
//2是为了记录素数个数,为后来的循环做条件。
for(int i=2;i<=1000;i++)//这里定义的i,既是素数的来源(第13行),也是后面(第20行)用来筛合数的乘数
{
//printf("##i=%d\n",i);
if(vis[i]==0)//如果是i是素数
{
prime[t]=i;//保存在p[]里
t++;
}
//printf("t=%d ",t-1);
for(int j=0;j<t;j++)//开始筛
{
//printf("##i=%d prime[j]=%d i*prime[j]=%d\n",i,prime[j],i*prime[j]);
vis[prime[j]*i]=true;//把合数筛掉
if(i%prime[j]==0)//这里就是埃氏筛和欧拉筛不同之处,优化大多就靠这一条语句(后有证明
break;
}
//printf("\n------------\n");
}
for(int i=0;prime[i]!=0;i++)//输出素数
printf("%d ",prime[i]);
}
其中的一些检验语句陪我埋了,要是看不明白可解开//封印,一个一个值的对看。
3.if(i%p[j]==0) break;
我们这一条语句就保证了最小质因数的条件,也就是说,如果i是prime[j]这个素数的整数倍,那么意味着prime[j+1]不是prime[j+1]*i的最小质因数( 这句话比较难理解,确保你看懂了,在看下面的证明
4.证明----如果i是prime[j]这个素数的整数倍,那么意味着prime[j+1]不是prime[j+1]*i的最小质因数
很容易可知,prime[j]<prime[j+1],同时我们还不知道k等于几,所以可证明