zoukankan      html  css  js  c++  java
  • 素数筛法讲解

    首先看一看判断素数的方法,就是看一个数n能否被2~n-1内的数整除,如果能整除就不是素数,反之则是,直接上优化后的代码:

    1 bool isprime(int x)
    2 {
    3     for(int i=2;i<=sqrt(x);i++)
    4     {
    5         if(x%i==0)
    6         return false;
    7     }
    8     return true;
    9 }

    对于一些题目,需要判断的素数非常大且多,用这种方法的话时间复杂度是绝不允许的,因此就有了素数筛法,顾名思义,是提前把素数筛选出来,这样之后判断的时候就快了。

    先说一下素数筛法的原理:

     从第一个素数2开始,将所有2的倍数筛掉,因为2的倍数一定不是素数,然后往后,3的倍数筛掉.....一直到只有素数剩下,如图,黄色的数为素数(图片来源于网络)

     这种筛法有两种算法:

    一是Eratosthenes(埃拉托斯特尼)筛法,这个筛法是直接把原理应用上了,从第一个素数开始,只要是自身倍数的,全都检查一遍并筛掉。

    代码如下:primelist[ ]代表素数表,isprime[ ]代表是否是素数。

     1 bool isprime[maxn+5];
     2 void MakePrimeList()
     3 {
     4     int cnt=1;
     5     memset(isprime,true,sizeof(isprime));//先假设所有数是素数 
     6     for(int i=2;i<=maxn;i++)//将所有数遍历 
     7     {
     8         if(isprime[i])//判断是否素数 
     9         primelist[++cnt]=i;//是的话加入素数表 
    10         for(int j=i+i;j<=maxn;j+=i)//从这个数的最小的倍数开始 
    11         {
    12             isprime[j]=false;//标记为非素数 
    13         }
    14 //        for(j=2;j*i<=maxn;j++)这是原型,上面是改进的 
    15 //        { 
    16 //            isprime[i*j]=false;
    17 //        } 
    18     }
    19     return ;
    20 }

     这种埃氏筛法时间复杂度氏O(nlognlogn),显然时间复杂度仍然较大。

    二是Euler(欧拉)筛法,埃氏筛法复杂度高的原因是很多数都被重复筛了好几次甚至很多次,做了很多不必要的工作,而欧拉筛法就避免了重复筛数。

    先看代码:

     1 void MakePrimeList()
     2 {
     3     int cnt=0;
     4     memset(isprime,true,sizeof(isprime));//先假设所有数是素数 
     5     for(int i=2;i<=maxn;i++)//将所有数遍历
     6     {
     7         if(isprime[i])//判断是否素数 
     8         primelist[++cnt]=i;//是的话加入素数表
     9         for(int j=1;j<=cnt;j++)//这里是从素数表的已知是素数的第一个数开始,到已知数目结束 
    10         {
    11             if(i*primelist[j]>maxn)
    12             break;
    13             isprime[i*primelist[j]]=false;//这样循环下去,就把所有非素数找到并标记了 
    14             if(i%primelist[j]==0)//这里和关于j的for循环是关键,保证不重复筛数 
    15             break;
    16         }
    17     }
    18     return ;
    19 }

    百度上关于if(i%primelist[j]==0)有这样解释:(这里prime[]相当于primelist[])

    prime[]数组中的素数是递增的,当i能整除prime[j],那么i*prime[j+1]这个合数肯定被prime[j]乘以某个数筛掉。
    因为i中含有prime[j],prime[j]比prime[j+1]小,即i=k*prime[j],那么i*prime[j+1]=(k*prime[j])*prime
    [j+1]=k’*prime[j],接下去的素数同理。所以不用筛下去了。因此,在满足i%prime[j]==0这个条件之前以及第一次
    满足改条件时,prime[j]必定是prime[j]*i的最小因子。

    举个例子:从第一个素数2开始,此时 i 等于2,i*2=4(始终是从j=1即第一个素数开始乘以 i,下同),4不是素数,标记,i%2==0,停止。i++;

                  第二个数也是第二个素数3,此时 i 等于3,i*2=6,6被标记不是素数,i%2!=0,i*3=9,9被标记,i%3==0,停止。i++;

                   第三个数4不是素数,不加入表, 此时 i 等于4,i*2=8,8被标记,i%2==0,停止,i++;

                  .......等等..

    它始终是把离当前素数最近的数筛掉,而且不重不漏,时间复杂度是o(n),确实被证明过是O(n)。

    例题:lightoj1259 - Goldbach`s Conjecture    

    题意就是给你一个数,让你把它拆成两个素数数相加,问有多少种方法(a<b就是不能重复)

    直接在素数表中找判断即可。

    代码如下:

     1 #include<iostream>
     2 #include<cstring>
     3 #include<cstdio>
     4 #include<cmath>
     5 using namespace std;
     6 const int maxn=10000000;
     7 int primelist[maxn/10];
     8 bool isprime[maxn+5];
     9 void MakePrimeList()
    10 {
    11     int cnt=0;
    12     memset(isprime,true,sizeof(isprime));
    13     for(int i=2;i<=maxn;i++)
    14     {
    15         if(isprime[i])
    16         primelist[++cnt]=i;
    17         for(int j=1;j<=cnt;j++)
    18         {
    19             if(i*primelist[j]>maxn)
    20             break;
    21             isprime[i*primelist[j]]=false;
    22             if(i%primelist[j]==0)
    23             break;
    24         }
    25     }
    26     return ;
    27 }
    28 int main()
    29 {
    30     int n,t;
    31     int cas=0;
    32     cin>>t;
    33     MakePrimeList();
    34     while(t--)
    35     {
    36         int num=0,cnt=1;
    37         scanf("%d",&n);
    38         for(int i=1;primelist[i]<=n/2;i++)//从素数表中第一个数到大小为n/2是因为a<b
    39         {
    40             if(isprime[n-primelist[i]])
    41                 num++;
    42         }
    43         printf("Case %d: %d
    ",++cas,num);
    44     }
    45     return 0;
    46 }
  • 相关阅读:
    17-7-20-electron中主进程和渲染进程区别与通信
    17-7-19-书写规范和任务的延续性
    17-7-19----起
    一年没回来了
    django-BBS(2)
    django-BBS(1)
    nmap使用教程
    利用谷歌黑客语法挖掘漏洞
    PHP代码审计之XSS操作
    PHP安装文件的审计
  • 原文地址:https://www.cnblogs.com/theshorekind/p/12690298.html
Copyright © 2011-2022 走看看