筛选素数1(暴力枚举和埃式筛法)

本文写怎么确定n以内的素数个数,并且打印出每一个素数。(至于个数我的代码里就不专门写出来了)

1.暴力枚举

#include <cstdio>
#include <iostream>
#include <cstdlib>
#include <cstring>
#include <algorithm>
#include <vector>
using namespace std;
int isPrime(int n)
{
    if(n==1)
        return 0;
    if(n==2)
        return 1;
    for(int i=2;i<n;i++)
    {
        if(n%i==0)
            return 0;
    }
    return 1;
}
int main()
{
    int n;
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
    {
        if(isPrime(i))
            printf("%d\n",i);
    }
    return 0;
}

容易看出,这个时间复杂度为O(n^2),这里就不用做过多的解释了。

2.暴力枚举(优化,缩小查找范围)

一个数的因子分布在这个数开根号的两侧,比如12,他的因子有1,2,3,4,6,12其中1,2,3在sqrt(12)左侧,剩下的在右侧。那么我们只需要判断这个数%sqrt(12)左边的因子(包括sqrt(12))就可以知道这个数是不是素数。如果左边没有这样一个数,那么右边一定没有。例外,因为素数一定是奇数(2除外),所以这一点上还可以优化。

#include <cstdio>
#include <iostream>
#include <cstdlib>
#include <cstring>
#include <algorithm>
#include <cmath>
using namespace std;
int isPrime(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;
    scanf("%d",&n);
    if(n>=2)
        printf("2\n");
    for(int i=1;i<=n;i+=2)
    {
        if(isPrime(i))
            printf("%d\n",i);
    }
    return 0;
}

计算次数:(n/2)*sqrt(n);时间复杂度:n*sqrt(n)

3.埃式筛选法:

1.学长讲的(我觉得这算是埃式筛选的一种吧),直接贴代码,不做过多解释(~~~~就是直接删除找到的素数的倍数。剩下的保存)。

#include<cstdio>
#include<cstdlib>
#include<cstring>
int prime[10000];
int is_prime[10000];
int f(int n)
{
    memset(is_prime,1,sizeof(is_prime)); //下标表示这个数,刚开始把所有的数当成素数
    is_prime[0]=is_prime[1]=0;//0和1都不是素数
    int m=0;
    for(int i=2;i<=n;i++)
    {
        if(is_prime[i])
        {
            prime[m++]=i;  //是素数就保存
            for(int j=2*i;j<=n;j+=i)
                is_prime[j]=0;//把素数的倍数删除,注意这里重复删除,所以可以优化
        }
    }
    return m;
}
int main()
{
    int n;
    scanf("%d",&n);
    int x=f(n);
    for(int i=0;i<x;i++)
        printf("%d\n",prime[i]);
    return 0;
}

2.百度百科定义:要得到自然数n以内的全部素数,必须把小于等于  的所有素数的倍数剔除,剩下的就是素数。

详情介绍见百度百科(埃拉托斯特尼筛法)

先看源代码,在作解释:

#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
int prime[10000];
int is_prime[10000];
void f(int n)
{
    memset(is_prime,1,sizeof(is_prime));
    is_prime[0]=is_prime[1]=0;
    for(int i=2;i<=sqrt(n);i++)
    {
        if(is_prime[i])
        {
            for(int j=i*i;j<=n;j+=i)
                is_prime[j]=0;
        }
    }
}
int f1(int n)
{
    f(n);
    int j=0;
    for(int i=2;i<=n;i++)
        if(is_prime[i])
            prime[j++]=i;
    return j;
}
int main()
{
    int n;
    scanf("%d",&n);
    int x=f1(n);
    for(int i=0;i<x;i++)
        printf("%d\n",prime[i]);
    return 0;
}

首先为什么是我还没有想到合适的解释方法,在这里就简单举个例子吧。36以内的数,假设6(包括6)之前现在已经标记为0(不是素数),下面是7(素数),从2倍开始删除,前面其实已经删除过2,3,4,5,6倍的数了,如果从7开始,那么7*7>36无需再删除了。

for(int j=i*i;j<=n;j+=i)
                is_prime[j]=0;

下面就来说说它的意思,其实每次标记一个素数的倍数时候,那么倍数就应该是从这个素数开始的。上面的例子2从2*2开始删除,3从3*3开始删除(2的倍数删除过了,此处跳过2的倍数),5从5*5开始删除(2,3,4倍数跳过)(4的倍数也就是2的倍数,6的倍数也就是2,3的倍数),下面类似,就可以写出上面的程序。

上面暴力枚举提到素数只能是奇数,所以这个代码还可以在优化,这里就不贴代码了,上面的代码稍微改一下就行了。、

时间复杂度

要计算他的次数还要依赖于素数定理(详细介绍见百度百科):从不大于n的自然数随机选一个,它是素数的概率大约是1/ln n。

(我也不太会用素数定理来证,网上暂时找不到好的理解方法,下方可以忽略)

但是对于每一个素数a,都要进行n/a次的标记。那么(1-sqrt(n))\sum n*(1/a)就是总次数。小于n的素数个数:n/ln n

那么也就是算1/a(1--sqrt(n))的和,对比1/a------log(1/a)后面是O(logn)那么前面就是loglogn

这个程序的时间复杂度为O(nloglogn),以上证明我也很懵,其实就是要计算出1到sqrt(n)之间素数的和,有兴趣的可以自己百度过程。

据说素数筛法还有线性的,等我去学习学习再来写吧。。。

今天就感觉数学定理的证明都好难,很多都难以接受。。。。

在网上看了线性筛法(也就是复杂度为O(n))怎么感觉和上面我写的埃式筛选法:2中的思想类似,每次删除一次,合数一定会被删掉,目前还不知道到底有没有区别。。。。表示我自己也蒙了......暂时就不写这个方法了.......

猜你喜欢

转载自blog.csdn.net/qq_42217376/article/details/81134409
今日推荐