题目描述
在公元前3世纪,古希腊天文学家埃拉托色尼发现了一种找出不大于n的所有自然数中的素数的算法,即埃拉托色尼筛选法。这种算法能比比朴素的遍历法更快地找出素数,它首先需要按顺序写出2到n中所有的数。以n=20为例:
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
然后把第一个元素画圈,表示它是素数,然后依次对后续元素进行如下操作:如果后面的元素是画圈元素的倍数,就画X,表示该数不是素数。在执行完第一步后,会得到素数2,而所有是2的倍数的数将全被画掉,因为他们肯定不是素数。接下来,只需要重复上述操作,把第一个既没有被圈又没有画X的元素圈起来,然后把后续的是它的倍数的数全部画X。本例中这次操作将得到素数3,而所有是3的倍数的数都被去掉。以此类推,最后数组中所有的元素不是画圈就是画X。所有被圈起来的元素均是素数,而所有画X的元素均是合数。给定一个数字,编写一个程序实现埃拉托色尼筛选法找出小于或等于该数字的的所有素数。
【输入形式】
输入为一个正整数n, 2≤n≤2000000
【输出形式】
输出为一行,输出所有小于或等于输入的素数
数据之间用空格分隔
最后的一行输出后面无空格
【样例输入1】
5
【样例输出1】
2 3 5
【样例输入2】
20
【样例输出2】
2 3 5 7 11 13 17 19
解题思路(运行时间过长)
这道题要求使用埃拉托色尼筛法来做,即从第一个数2开始,对之后的数按照以下规则操作:
- 对从左开始,未被画×的数画圈
- 对之后的所有数中是该轮选定的画圈的数的倍数的数,画×
- 重新从左选择未被画×和圈的数画圈,重复操作2
这个操作映射到数组上,即用数字标志其状态,在这里初始状态均为0,画圈则为1,画×则为2,通过循环嵌套实现埃拉托色尼筛法。代码如下:
#include <iostream>
using namespace std;
int judge(int a[],int n){ //判断是否所有元素已经画×或画圈
for(int i=1;i<n;i++){
if(a[i]==0) //若存在有元素未被判断,则返回1.
return 1;
}
return 0;
}
int main()
{
int n;
cin>>n;
int a[2][n];
a[1][0]=2;
int tem=1;
int j;
int count1=0; //为了满足输出的空格格式,统计共有多少数为质数。
int counttem=0;
for(int i=0;i<n;i++){ //初始化
a[0][i] = i+1;
a[1][i] = 0;
}
while(judge(a[1],n)){ //只要仍有元素未判断,则一直循环下去
for(j=tem;j<n;j++){//从左寻找第一个未画圈、未画×的数
if(a[1][j] ==0){
a[1][j] = 1;
count1++;
tem = j;
break;
}
}
for(j=tem;j<n;j++){ //将该轮画圈的数的倍数画×
if(a[0][j]%a[0][tem]==0 && j!=tem){
a[1][j]=2;
}
}
}
for(j=1;j<n;j++){ //按照格式打印所有画圈的数
if(a[1][j]==1){
if(counttem<count1){
cout<<a[0][j]<<' ';
counttem++;
}
else{
cout<<a[0][j];
}
}
}
return 0;
}
改进
上述方法因为使用C风格的数组运行时间较长,并且考虑到当元素被画×之后,其实不需要再对其扫描一遍,只需要将其删除就好,在助教的提醒下,使用vector存储整性向量。
vector是C++特有的向量容器,可以存整型、字符等,具有size()、erase()等方法,可以直接调用,省去重复造轮子的时间。核心代码如下:
while(j<a.size()){//a为存储2~n的整型向量,这里size动态获取向量的长度
i=j+1;//j为画圈的数,从画圈的下一个开始计算是否整除
while(i<a.size()){
if(a[i]%a[j]==0){
a.erase(a.begin()+i);//删除该画×的元素,继续循环
}
else{
i++;
}
}
j++;
}
这一次编译全部通过,而且代码量小了不少QAQ,还是要对C++的各种类熟悉一下嗷~