您的位置:首页 > 其它

判断素数或者求出素数的基本算法 《挑战程序设计竞赛》

2018-02-28 11:08 316 查看
2018-2-28

首先我们得明确一个概念,那就是什么是素数,据我的了解,素数就是除了1和它本身之外,不存在其他的因数的数。

1.素性测试

判断给定的数n是否是素数


这应该是最简单的了,直接从2至n,如果存在n对其求余为0的话,那就不是素数了,这里可以降低我们的复杂度,想一下,如果一个数i是n的因子,那么n/i也必定是n的因子,不妨设i<=n/i,那么i最大的值就是sqrt(n)了,那么我们的循环也只要从2到sqrt(n)就可以了,因为这个算法比较普遍,我就不附上代码了。

2.埃氏筛法

求出给定整数n以内有多少个素数


如果用上面的算法,那么n以内的所有数都要进行一遍素数测试,那么时间复杂度为O(n^2),可想而知这样的效果不是特别好。

对于2而言,2的倍数4,6,8…那就都不是素数了,

对于3而言,3的倍数6,9,12…那也都不是素数了,

如果当前的数m是素数,那么m的倍数就都不是倍数了,有的人可能会问,为什么m就是素数呢,我们想一下如果m不是素数的话,那么它一定可以表示为p*q的积,不妨我们设这里的p为素数(如果p不是素数还可以再拆,直至变为素数),那么我们在遇到p这个素数的时候,就已经把m,这个素数p的倍数划掉了,所以这样反复操作就能得到我们要的素数了。

#include<iostream>
#include<cstring>
using namespace std;

const int N = 100;
bool is_prime[N+1];
int prime[N+1];

int main(){
memset(is_prime,true,sizeof(is_prime));
is_prime[0]=false;is_prime[1]=false;
int cnt=0;
for (int i=2;i<=N;i++){
if (is_prime[i]){
prime[cnt++]=i;
for (int j=i*i;j<=N;j+=i){
is_prime[j]=false;
}
}
}
for (int i=0;i<cnt;i++){
cout<<prime[i]<<" ";
}
cout<<"cnt="<<cnt<<endl;
// for (int i=2;i*i<=N;i++){
//     if (is_prime[i]){
//         for (int j=i*i;j<=N;j+=i){
//             is_prime[j]=false;
//         }
//     }
// }
// int cnt=0;
// for (int i=0;i<=N;i++){
//     if (is_prime[i]) prime[cnt++]=i;
// }
// for (int i=0;i<cnt;i++){
//     cout<<prime[i]<<" ";
// }
// cout<<"cnt="<<cnt<<endl;
return 0;
}


这里的j有的人可能会想从2*i开始,其实从2*i到(i-1)*i都已经在’i’为2到i-1的时候被计算过了,所以我们这里会选择从i*i开始。

3.区间筛法

给定区间[a,b),求出他们两个数之间素数的个数


对于[a,b)内的合数而言,它们的因子应该在2到sqrt(b)之间,那么我们只要把2到sqrt(n)的倍数在给定区间[a,b)的数筛掉就可以了,为了节省我们的存储空间,我们可以将[a,b),左移到[0,b-a)就可以了。

#include<iostream>
#include<cstring>
using namespace std;

typedef long long int ll;
const int N = 1000000;
bool is_prime_small[N+1],is_prime[N+1];
ll a,b;

int main(){
while (cin>>a>>b){
memset(is_prime,true,sizeof(is_prime));
memset(is_prime_small,true,sizeof(is_prime_small));
is_prime_small[0]=false;is_prime_small[1]=false;
if (a==0){
is_prime[0]=false;
is_prime[1]=false;
}else if (a==1){
is_prime[0]=false;
}
for (ll i=2;i*i<b;i++){
if (is_prime_small[i]){
for (ll j=i*i;j<b;j+=i){
if (j>=a) is_prime[j-a]=false;
//在对应区间的数就不是素数了
}
for (ll j=i*i;j*j<b;j+=i) is_prime_small[j]=false;
//将[2,sqrt(b))内的素数筛出来
}
}
int cnt=0;
for (ll i=0;i<b-a;i++){
if (is_prime[i]){
cnt++;
cout<<i+a<<" ";
}
}
cout<<"cnt="<<cnt<<endl;
}
return 0;
}

4000

除此之外还有其他素数有关的算法,不过程序设计竞赛主要就是涉及这三种。

最后给一个第八届蓝桥杯b组的试题:

2,3,5,7,11,13,....是素数序列。
类似:7,37,67,97,127,157 这样完全由素数组成的等差数列,叫等差素数数列。
上边的数列公差为30,长度为6。

2004年,格林与华人陶哲轩合作证明了:存在任意长度的素数等差数列。
这是数论领域一项惊人的成果!

有这一理论为基础,请你借助手中的计算机,满怀信心地搜索:

长度为10的等差素数列,其公差最小值是多少?

注意:需要提交的是一个整数,不要填写任何多余的内容和说明文字。


如果我没记错的话,当时比赛的时候好像是错的。。。

首先我们需要把1000000以内的所有素数筛选出来,这样可以简化我们后边的计算,然后我们对d以及首项a1进行遍历,然后依次判断即可。

刚开始的时候我还在纠结一个问题,也就是说如果当前的cnt正好等于M=10,那么是否存在a1-d正好也是素数,那么这里是不是就多了一个了,变成M+1个了,后来我发现,不存在的,在我这里,以a1-d为首项的等差数列的d’一定小于d。

#include<iostream>
#include<cstring>
using namespace std;

const int N = 1000000, M = 10;
bool is_prime[N+1];

int main(){
memset(is_prime,true,sizeof(is_prime));
for (int i=2;i*i<=N;i++){
if (is_prime[i]){
for (int j=i*i;j<=N;j+=i){
is_prime[j]=false;
}
}
}
int i,cnt;
bool flag=false;
for (i=2;;i++){
for (int j=2;j<=N;j++){
if (!is_prime[j]) continue;
cnt=1;
for (int k=j+i;k<=N;k+=i){
if (!is_prime[k]||cnt>M) break;
if (is_prime[k]){
cnt++;
}
}
if (cnt==M){
flag=true;
break;
}
}
if (flag) break;
}
cout<<i<<endl;
return 0;
}


虽然运行的时候停顿了一下,但是最后还是得到了结果210。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  素数