本文写怎么确定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次的标记。那么就是总次数。小于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中的思想类似,每次删除一次,合数一定会被删掉,目前还不知道到底有没有区别。。。。表示我自己也蒙了......暂时就不写这个方法了.......