zoukankan      html  css  js  c++  java
  • 线性筛素数、欧拉函数

    判断一个数n是否是素数,众所周知可以用O(sqrt(n))的方法。

    但是如果要求很多个数,这个方法就不太好了。(比如所有小于n的数,复杂度就是O(n1.5)。)

     

    埃拉托斯特尼筛法,大家都听说过。从2到n,去掉每个数的倍数,剩下来的就是质数。

    不过这个方法会重复删除,比如6是2、3的倍数,会被删2次,因子越多,删的次数就越多。

     

    改进之后的线性筛保证每个数只被最小的质因子删,所以是O(n)的。

    #include<cstdio>
    #include<cstring>
    #define MAXN 100005
    #define MAXL 1299710
    int prime[MAXN];
    int check[MAXL];
    
    int tot = 0;
    memset(check, 0, sizeof(check));
    for (int i = 2; i < MAXL; ++i)
    {
      if (!check[i])
      {
        prime[tot++] = i;
      }
      for (int j = 0; j < tot; ++j)
      {
        if (i * prime[j] > MAXL)
        {
          break;
        }
        check[i*prime[j]] = 1;
        if (i % prime[j] == 0)
        {
          break;
        }
      }
    }
    View Code

     

    tot是计数用的,prime保存质数,check是判断是否是质数。

    1.任意一个合数 A = p1p2...pn,(其中p1<=p2<=...<=pn) ,i=p2p3...pn时会删掉。

    2.A只会被p1删掉。若i是prime[j]的倍数,A不会被p[j+1]删掉,当i=A/p[j+1]时,i%p[j+1]==0,break。如果不退出,A就被p[j+1]删了。

    可以看出,这个方法需要额外的prime数组。而埃氏筛不必要。

     

     


     

    顺便可以求欧拉函数

    #include<cstdio>
    #include<cstring>
    #define MAXN 100005
    #define MAXL 1299710
    int prime[MAXN];
    int check[MAXL];
    int phi[MAXL];
    int tot = 0;
    phi[1] = 1;
    memset(check, 0, sizeof(check));
    for (int i = 2; i < MAXL; ++i)
    {
      if (!check[i])
      {
        prime[tot++] = i;
        phi[i] = i - 1;
      }
      for (int j = 0; j < tot; ++j)
      {
        if (i * prime[j] > MAXL)
        {
          break;
        }
        check[i*prime[j]] = 1;
        if (i % prime[j] == 0)
        {
          phi[i*prime[j]] = phi[i] * prime[j];
          break;
        }else
        {
          phi[i*prime[j]] = phi[i] * (prime[j]-1);
        }
      }
    }
    View Code

     

    n为质数,phi(n)=n-1

    n为合数,进行质因数分解。$large n = p_1^{k_1} imes p_2^{k_2} imes ... imes p_n^{k_n}$

    $$large varphi(n) = n prodlimits_{i = 1} ^ {n} frac{p_i - 1}{p_i}$$

    筛掉n=i*prime[j]时,求$varphi(n)$,筛和求是同步的,也是O(n)。

    设p1是n的最小质因子,n'=n/p1。

    若n'%p1=0,即k1>1,n'含有n的所有质因子,则有

     $$large egin{align} varphi(n)&= n prodlimits_{i = 1} ^ {n} frac{p_i - 1}{p_i} \ &= p_1 imes n' prodlimits_{i = 1} ^ {n} frac{p_i - 1}{p_i} \ &= p_1 imes varphi(n') \ end{align}$$

    若n'%p1≠0,即k1=1,n'与p1互质,有

    $$large egin{align} varphi(n)&= n prod_{i = 1} ^ {n} frac{p_i - 1}{p_i} \ &= p_1 imes n' imes frac{p_1-1}{p_1} prodlimits_{i = 2} ^ {n} frac{p_i - 1}{p_i} \ &= (p_1-1) imes varphi(n') \ end{align}$$

     (这也证明了欧拉函数是积性函数)

     

    ref:

    代码全是抄这里的。

    欧拉函数的公式全是抄这里的。

    延伸:

    只筛奇数。

    我推荐这个算法! 易于理解。 只算奇数部分,时空效率都还不错!  
    half=SIZE/2;   
    int sn = (int) sqrt(SIZE);   
    for (i = 0; i < half; i++)   
       p[i] = true;// 初始化全部奇数为素数。p[0]对应3,即p[i]对应2*i+3   
    for (i = 0; i < sn; i++) {      
    if(p[i])//如果 i+i+3 是素数  
    {       //每次j+=k,2*(j+k)+3=(2*j+3)+2k,也就是在上一个筛的数上+2k
        for(k=i+i+3, j=k*i+k+i; j < half; j+=k)   
        // 筛法起点是 p[i]所对应素数的平方 k^2                                          
        // k^2在 p 中的位置是 k*i+k+i  
        //    下标 i         k*i+k+i  
        //对应数值 k=i+i+3   k^2           
           p[j]=false;   
    }   
    }   
    //素数都存放在 p 数组中,p[i]=true代表 i+i+2 是素数。  
    //举例,3是素数,按3*3,3*5,3*7...的次序筛选,因为只保存奇数,所以不用删3*4,3*6.... 
    View Code

    来自:https://blog.csdn.net/dinosoft/article/details/5829550

     

  • 相关阅读:
    使用C# (.NET Core) 实现模板方法模式 (Template Method Pattern)
    使用C# (.NET Core) 实现单体设计模式 (Singleton Pattern)
    使用C# (.NET Core) 实现抽象工厂设计模式 (Abstract Pattern)
    使用C# (.NET Core) 实现简单工厂(Simple Factory) 和工厂方法设计模式 (Factory Method Pattern)
    使用 dynamic 类型让 ASP.NET Core 实现 HATEOAS 结构的 RESTful API
    使用静态基类方案让 ASP.NET Core 实现遵循 HATEOAS Restful Web API
    .NET Core/.NET之Stream简介
    使用C# (.NET Core) 实现装饰模式 (Decorator Pattern) 并介绍 .NET/Core的Stream
    MySQL 导入数据
    MySQL 导出数据
  • 原文地址:https://www.cnblogs.com/azureice/p/linear-sieve-for-prime-numbers.html
Copyright © 2011-2022 走看看