前言
对于1~n范围内素数的查找,我们常用的二重循环暴力算法的复杂度是O(n2),如果利用开根缩小范围的时间复杂度也无非是在O( ),而,这些算法对于n在105以内都是可以接受的,但是如果需要更大范围的素数表,这些算法将显得力不从心。下面将介绍更加高效的算法。
埃氏筛
埃氏筛也叫素数筛法,其关键在一个“筛”字。算法从小到大枚举所有数,对于每一个素数,筛去它的所有倍数,剩下的就都是素数了。埃氏筛的时间复杂度只有O( )。
下面是一个例子:求1~15中的所有素数。(高亮的是已经筛去的)
-
2是素数(唯一需要事先确定的),因此筛去所有2的倍数,即4、6、8、10、12、14。
1,2,3,4,5,6,7,8,9,10,11,12,13,14,15
-
3没有被前面的步骤筛去,因此3是素数,筛去所有3的倍数,即6,9,12,15
1,2,3,4,5,6,7,8,9,10,11,12,13,14,15
-
4已经被筛去,因此4不是素数。
-
5没有被前面的步骤筛去,因此5是素数,筛去所有5的倍数,即10,15
1,2,3,4,5,6,7,8,9,10,11,12,13,14,15
-
6已经被筛去,因此6不是素数。
-
7没有被前面的步骤筛去,因此7是素数,筛去所有7的倍数 ,即14
1,2,3,4,5,6,7,8,9,10,11,12,13,14,15
-
8已经被筛去,因此8不是素数。
-
9已经被筛去,因此9不是素数.
-
10已经被筛去,因此10不是素数.
-
11没有被前面的步骤筛去,因此11素数,但15以内没有11的倍数。
-
12已经被筛去,因此12不是素数.
-
13没有被前面的步骤筛去,因此13素数,但15以内没有13的倍数。
-
14已经被筛去,因此14不是素数.
-
15已经被筛去,因此15不是素数.
至此,1~15以内的所有素数已全部得到,即2、3、5、7、11、13。
由这个例子很形象的展示了埃氏筛的过程,那么接下来就直接上代码:
int prime[maxn],pNum = 0; //prime数组存放所有素数,pNum为素数的个数
bool p[maxn] = {0}; //如果i为素数,则p[i]为false;否则为true
void Find_Prime()
{
for(int i = 2;i<maxn;i++)
{
if(p[i] == false)
{
prime[pNum++] = i;
for(int j=i+1;j<maxn;j+=i) //筛去所有i的倍数
{
p[j] = true;
}
}
}
}
欧拉筛
而分析埃氏筛我们会发现,埃氏筛在执行过程中会有多个数被重复筛选过了,只要避免这些重复筛选,我们的算法将会再次提高效率,这就出现了欧拉筛。
欧拉筛法的基本思想 :==在埃氏筛法的基础上,让每个合数只被它的最小质因子筛选一次,以达到不重复的目的。==欧拉筛的时间复杂度仅为O(n)。
先上代码:
int prime[maxn],pNum = 0; //prime数组存放所有素数,pNum为素数的个数
bool p[maxn] = {0}; //如果i为素数,则p[i]为false;否则为true
void Find_Prime()
{
for(int i = 2;i<=maxn;i++)
{
if(p[i] == false)
prime[++pNum] = i;
for(int j=1;j<=pNum&&i*prime[j]<=maxn;j++) //筛去所有i的倍数
{
p[i*prime[j]] = true;
if(i%prime[j]==0)
break;
}
}
}
再以举前面那个例子:求1~15中的所有素数。(高亮的是已经筛去的)
-
2是素数(唯一需要事先确定的),此时pNum = 1,i = 2;
j = 1, i*prime[j] = 4
1,2,3,4,5,6,7,8,9,10,11,12,13,14,15
-
3没有被前面的步骤筛去,因此3是素数,此时pNum = 2,i = 3;
j = 1, i*prime[j] = 6
j = 2, i*prime[j] = 9
1,2,3,4,5,6,7,8,9,10,11,12,13,14,15
-
4已经被筛去,因此4不是素数,此时pNum = 2,i = 4;
j = 1, i*prime[j] = 8
1,2,3,4,5,6,7,8,9,10,11,12,13,14,15
-
5没有被前面的步骤筛去,因此5是素数,此时pNum = 3,i = 5;
j = 1, i*prime[j] = 10
j = 2, i*prime[j] = 15
j = 3, i*prime[j] = 25 (>15)
1,2,3,4,5,6,7,8,9,10,11,12,13,14,15
-
6已经被筛去,因此6不是素数,此时pNum = 3,i = 6。
j = 1, i*prime[j] = 12
j = 2, i*prime[j] = 18 (>15)
j = 3, i*prime[j] = 30 (>15)
1,2,3,4,5,6,7,8,9,10,11,12,13,14,15
-
7没有被前面的步骤筛去,因此7是素数,此时pNum = 3,i = 7。
j = 1, i*prime[j] = 14
j = 2, i*prime[j] = 21 (>15)
j = 3, i*prime[j] = 35 (>15)
j = 4, i*prime[j] = 49 (>15)
1,2,3,4,5,6,7,8,9,10,11,12,13,14,15
此后的i*prime[1]>15,因此不必继续判断了。至此,1~15以内的所有素数已全部得到,即2、3、5、7、11、13。
首先,欧拉筛的过程中有两个值得我们关注的点:
-
p[i*prime[j]] = true:这里不是用i的倍数来消去合数,而是把 prime里面纪录的素数,升序来当做要消去合数的最小素因子。
-
尤其注意的是
if(i%prime[j]==0) break;
这段代码:当i为prime[j]的倍数时要跳出循环,因为:当i = k * prime[j],如果继续j+1,则会有 i * prime[j+1] = prime[i] * k * prime[j+1],而当i循环到i = k * prime[j+1]时会重复筛选,因此这里提前结束。