质数筛检法

来源:互联网 发布:mysql如何导出sql文件 编辑:程序博客网 时间:2024/06/05 21:07

我们要知道什么是质数,简单来说就是:除了1和本身能整除以外,其余的数都不可以。

 让我们来一步一步的了解一下质数选取的“进化”。

在我们刚刚接触质数的时候我们是在小学的时候,那时候就是直接用1到n之间的所有的数去让n除一遍,就可以得出我们想要的结果了。

具体代码如下:

#include <stdio.h>int main(){    int n,i,j;    scanf("%d",&n);    for( i=1;i<=n;i++)    {        for( j=2;j<i;j++)        {            if(i%j==0) break;        }        if(j==i) printf("%d\n",i);    }    return 0;}

时间复杂度:O(n)
之后我们在不断的学习数学的时候,我们发现其实我们除的数,只需要到n/2就可以了,甚至到sqrt(n)就可以了。不需要这么多。
跟进一步的我们学习了,求到根号下n,也可以了。

#include <stdio.h>#include <math.h>int cmp(int n){    if(n<=1) return 0;    for(int i=2; i<=sqrt(n); i++)    {        if(n%i==0) return 0;    }    return 1;}int main(){    int n,i,j;    scanf("%d",&n);    for( i=1; i<=n; i++)    {        if(cmp(i)==1)         printf("%d\n",i);    }    return 0;}


时间复杂度:O(sqrt(n))
不过随着时代的脚步,我们发现根本最不上了,很多代码都是TimeLimitExceeded,搞的我好烦,(每次都想砸电脑。气死人的存在。)
附上一个比较简单部分代码:
   for(i=2; i<N; i++){       if(i%2) prime[i]=1;       else prime[i]=0;   }   for(i=3; i<=sqrt(N); i++)   {   if(prime[i]==1)       for(j=i+i; j<N; j+=i)         prime[j]=0;   }
随我们不断学习,其实我们发现了一个规律。
就是如果一个数他是质数,那么他的倍数,是不是就是代表着那个数不是质数。
对,如果是这样的话,时间应该是减少了不知道多少倍。

看一个样例具体解释一下:看一组数字:2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
我们先全部假设他为质数:我们先找到了质数2,那么我们就把他的倍数,全部删除掉。之后我们得到的数组是这个。
变化一:2 3 5 7 9 11 13 15 17 19 21 23 25
之后我们选择后面一个质数:3,那么我们就把他的倍数,全部删除掉。之后我们得到的数组是这个。
变化二:2 3 5 7 11 13 17 19 23 25
之后我们选择后面一个质数:5,那么我们就把他的倍数,全部删除掉。之后我们得到的数组是这个。
变化三:2 3 5 7 11 13 17 19 23 25
以此类推,我们就可以选择1到25之间所有的质数了。


通过上面的案例分析我们就可以对应的敲出代码了
具体代码如下:
#include<stdio.h>int main(){    ///假如他们都是质数,初始化    memset(a,0,sizeof(a));    int i,j,n;    ///注意a[1] = 1,因为我们最先排除的非质数就是他了。    a[1]=1;    for(i=2; i<=n; i++)    {        if(a[i]==0)        {            ///把他的倍数全部去掉。            for(j=i+i; j<=n; j+=i)            {                a[j]=1;            }        }    }    while(scanf("%d",&n),n)    {        for(int i=2; i<=n; i++)        {            if(a[i]==0)            {                printf("%d ",i);            }        }        printf("\n");    }    return 0;}

在一道题目中,我加深了质数筛检法的应用,以上面的例子为例,我们可以看见其实他只要筛检个3次就可以了,对不对。但是为了保险我们还是得全部遍历一遍,那有什么办法可以在他的基础上面更加的节约呢?因为我们可能出现10^30次方的数,那我们又怎么办呢?不禁思考了一番。学习java的伙伴们就好了,有自带的判断器。具体代码如下:

import java.util.Scanner;  import java.math.*;  public class Main {      public static void main(String[] args) {          Scanner cin = new Scanner(System.in);          BigInteger x;              x=cin.nextBigInteger();              if(x.isProbablePrime(1))                  System.out.println("Yes");              else                  System.out.println("No");      }  }  

但是我们没有学习java的怎么办,采用什么办法呢?对此,我只能说一句“抱歉,我现在也没有想出更好的办法。”
但是我可以把质数筛检的时间剪短一点,让他可以检测更大的数字,是不是质数。(缩减代码范围:n<=2000000,更大的应该不要超过10^8到10^10左右。更大我没有试过,但是大致是这个范围左右)
方法一:“缩半的质数筛检法”
上面的例子,我们其实只要3次就可以了,是不是。最大值是25,因为我们的范围不能超过25,所以我们找一个数字使得他的平方最接近于最大值,检测2到他的倍数,用于循环去数,是不是代表着,我们可以在质数筛检法的时间复杂度上面,减少一倍啊。(想想都有点激动啊),话不多说,我们试一试这种思路,检测一下1到100,之间的质数是不是可以的。

样例输入100:(质数筛检法)(缩半的质数筛检法)




在此我们发现,运行结果一样的那么我们是不是可以去试一试呢?(激动,更加激动了。)
具体代码如下:(求2000000之间的质数代码)

#include <stdio.h>#include <math.h>#define MAX 2000000int a[MAX];int main(){    int i,j,n;    for(i=2;i<=sqrt(MAX)+1;i++)    {        if(a[i]==0)        {            for(j=i+i;j<=MAX;j+=i)            {                a[j]=1;            }        }    }    while(scanf("%d",&n),n)    {        for(i=2;i<=n;i++)        if(!a[i])        printf("%d ",i);        printf("\n");    }    return 0;}

方法二:“废物利用质数筛检法”
假如我们全部设为质数,存起来,如果一个数是之前的某一个的质数的倍数,那么我们上面的代码,是不是直接continue了,不要了。是质数,是不是质数用了那么一次呢?
NO,NO,NO。下面我们讲一个更好的办法,时间可以更短的办法,
如果他不是质数或者他是质数,那们我就用他去乘以我们存起来的质数,得到的数是不是不是质数,只要他不超过我们的最大值n,是不是都可以淘汰掉一个值,哈哈,全部都得利用起来。
具体代码如下:

#include<stdio.h>#include<string.h>#define MAX 2000001#define MAX_PRIME 2000001bool nums[MAX];int prime[MAX_PRIME];void MakePrime(){    int i,j;    int pl=0;    nums[0]=1,nums[1]=1;    memset(prime,true,sizeof(prime));    for(i=2; i<MAX; i++)    {        if(!nums[i])            prime[pl++]=i;        for(j=0; j<pl&&i*prime[j]<=MAX; j++)        {            nums[i*prime[j]]=1;            if(!(i%prime[j]))                break;        }    }}int main(){    int n,j;    MakePrime();    while(scanf("%d",&n)!=EOF)    {        if(n>1)        {            printf("2");            for(j=1; prime[j]<=n; j++)            {                printf(" %d",prime[j]);            }        }        printf("\n");    }    return 0;}

该代码来自于:http://acm.nyist.net/JudgeOnline/problem.php?pid=187中的最优代码,(先得过,之后才可以看到的代码,要积分的、)

好了,方法都说完了,写的也差不多了。如果数据达到10^30次方大,我只能说一句,那不是质数筛检法了,因为那是用字符串处理的数据了。质数筛检法,前提就是要用long long或者int存的下,之后就是数组开辟,要开辟成功。否则,就不能使用了。
有兴趣的可以去写一下这道题目:http://acm.nyist.net/JudgeOnline/problem.php?pid=187
或者想找10^30次方那么大的,你们可以去这个链接写一下:https://vjudge.net/contest/174696#problem/Q
原创粉丝点击