埃氏筛、欧拉筛

前言

对于1~n范围内素数的查找,我们常用的二重循环暴力算法的复杂度是O(n2),如果利用开根缩小范围的时间复杂度也无非是在O( n n n\sqrt n ),而,这些算法对于n在105以内都是可以接受的,但是如果需要更大范围的素数表,这些算法将显得力不从心。下面将介绍更加高效的算法。

埃氏筛

埃氏筛也叫素数筛法,其关键在一个“筛”字。算法从小到大枚举所有数,对于每一个素数,筛去它的所有倍数,剩下的就都是素数了。埃氏筛的时间复杂度只有O( n log log n n\log \log n )。

下面是一个例子:求1~15中的所有素数。(高亮的是已经筛去的)

  1. 2是素数(唯一需要事先确定的),因此筛去所有2的倍数,即4、6、8、10、12、14。

    1,2,3,4,5,6,7,8,9,10,11,12,13,14,15

  2. 3没有被前面的步骤筛去,因此3是素数,筛去所有3的倍数,即6,9,12,15

    1,2,3,4,5,6,7,8910,11,12,13,1415

  3. 4已经被筛去,因此4不是素数。

  4. 5没有被前面的步骤筛去,因此5是素数,筛去所有5的倍数,即10,15

    1,2,3,4,5,6,7,8910,11,12,13,1415

  5. 6已经被筛去,因此6不是素数。

  6. 7没有被前面的步骤筛去,因此7是素数,筛去所有7的倍数 ,即14

    1,2,3,4,5,6,7,8910,11,12,13,1415

  7. 8已经被筛去,因此8不是素数。

  8. 9已经被筛去,因此9不是素数.

  9. 10已经被筛去,因此10不是素数.

  10. 11没有被前面的步骤筛去,因此11素数,但15以内没有11的倍数。

  11. 12已经被筛去,因此12不是素数.

  12. 13没有被前面的步骤筛去,因此13素数,但15以内没有13的倍数。

  13. 14已经被筛去,因此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中的所有素数。(高亮的是已经筛去的)

  1. 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

  2. 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

  3. 4已经被筛去,因此4不是素数,此时pNum = 2,i = 4;

    j = 1, i*prime[j] = 8

    1,2,3,4,5,6,7,89,10,11,12,13,14,15

  4. 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,8910,11,12,13,14,15

  5. 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,8910,11,12,13,14,15

  6. 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,8910,11,12,13,1415

此后的i*prime[1]>15,因此不必继续判断了。至此,1~15以内的所有素数已全部得到,即2、3、5、7、11、13。

首先,欧拉筛的过程中有两个值得我们关注的点:

  1. p[i*prime[j]] = true:这里不是用i的倍数来消去合数,而是把 prime里面纪录的素数,升序来当做要消去合数的最小素因子。

  2. 尤其注意的是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]时会重复筛选,因此这里提前结束。

发布了119 篇原创文章 · 获赞 22 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/lovecyr/article/details/104856745