众所周知,求素数的方法有很多,我们最开始学习求素数的方法自然是根据素数的定义来依次遍历一般,就像下面的代码一样:
int prime(int n)
{
for(int i=2;i<n;i++)
{
if(n % i == 0) //按照素数的定义
return 0;
}
return 1;
}
你觉得这样的算法过于复杂,然后你选择改进一下
int prime(int n)
{
for(int i=2;i<=sqrt(n);i++)
{
if(n % i == 0)
return 0;
}
return 1;
}
但是,其实我们会发现,如果当n很大的时候,你对于去求某个区间段内的素数,必然用一个循环,然后你每次调用这个prime函数的时候,你会发现,这个函数里竟然还有一个循环(哈哈哈哈哈),这个时候你的程序的时间复杂度就会到达
,这样显然不是最优的算法,那我们可不可以这样想,给定一个
的范围,我把从2到
这个范围内的所有的素数都求出来,并且把他们都存进一个数组里,这样我们查找的时候,数组的下标就是我们所要查找的第几个元素了。
虽然说我们决定了通过一个数组来存放从2到
的所有素数,但是怎么存放却是一个问题,我们可以这样想,把每一个是素数的,把他们的自己的倍数的数也都删了,比如3,那么6,9,12,这些关于3的倍数的,我们可以把它们全部从筛子里面剃掉。换句话说,就是将所有最小质因数的倍数给筛掉。
下面我们直接上代码来看看:
const int maxn = 1e5 + 5; //首先我们定义一个maxn,即所求的最大范围
bool u[maxn];//定义一个bool类型的数组,用来判定true为素数,false则不是
int s[maxn]; //我们再定义一个int类型的数组,用来存放素数
void prepare() //构建一个函数,用来产生素数
{
int num = 1;
int i, j;
memset(u, true, sizeof(u)); //将u全部变为true
for(i=2;i<=maxn;i++)
{
if(u[i]) s[num++] = i;//如果u[i]为true的话 ,也就是i是个素数的话那我们就将i放进数组里面
for(j=1;j<num;j++) //从1开始,枚举所有的质数
{
if(i * s[j] > maxn) //筛完结束
break;
u[i * s[j]] = false; //将所有最小质因子的倍数筛掉
if(i % s[j] == 0) //避免重复筛选,降低时间复杂度
break;
}
}
}
可以说,这算是一个比较好的欧拉筛模板,看懂了的朋友们,以后直接用就