质数筛检法

 

我们要知道什么是质数,简单来说就是:除了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 2000000
int 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 2000001
bool 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
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值