闲暇时写了一个找出小于某个数字的素数的程序。
最常见的方法是筛选法吧。原理大致如下:
若要求得16以内的所有素数,
1)在数组中存放一下数据:
2
|
3
|
4
|
5
|
6
|
7
|
8
|
9
|
10
|
11
|
12
|
13
|
14
|
15
|
16
|
2)
先筛选掉是2的倍数:
2
|
3
|
4
|
5
|
6
|
7
|
8
|
9
|
10
|
11
|
12
|
13
|
14
|
15
|
16
|
同理,继续筛选掉3的倍数.
当要筛选4的倍数的时候,由于4已经被筛选过了,所以4的倍数也必定筛选过了。因而跳过4,到5.剩下的步骤就类似了。
实际编程的时候,对一个数组进行操作。首先初始化数组,令所有元素都为0.
const int c_size = 100;
int *data = new int[c_size+1];
memset( data, 0, sizeof(int)*(c_size+1) );
data浪费了第一个空间data[0],为了简化编程,使用data[100]。
使用筛选法的时候,将筛选过的元素置为-1。输出结果的时候,仅打印data[i]的值为0的下标i。
for (int i=2; i<=data_size; i++)
{
if (data[i] == 0)
cout << i << "/t";
}
下面给出整个算法的完整代码:
#include <iostream> //use cin and cout
using namespace std;
//计算2到c_size范围内的素数
const int c_size = 100;
void print(int *data, int data_size)
{
int num = 0;
for (int i=2; i<=data_size; i++)
{
if (data[i] == 0)
cout << i << "/t";
}
cout << endl;
}
//使用筛选法计算素数
void cal_prime(int *data, int data_size)
{
//从2开始使用筛选法计算
for (int i=2; i<=data_size; i++)
for (int j=2; i*j<=data_size; j++)
{
//如果该数尚未筛选过
//则标记该数选中
if (data[i*j] == 0)
data[i*j] = -1;
//如果已经筛选过,则跳出改次筛选
//进入下次筛选
else
continue;
}
}
int main()
{
//定义数组并初始化
//浪费第一个数组空间
int *data = new int[c_size+1];
memset( data, 0, sizeof(int)*(c_size+1) );
//计算素数
cal_prime(data, c_size);
//打印计算出的素数
print(data, c_size);
return 0;
}
有没有更快的算法呢?偶尔在图书馆看到了另外一种算法,其大致思想如下:
1)在给定范围内,挑出6n±1的数字;
2)计算根号下(给定范围)内的所有素数(除了2,3)。比如要求出在100内的素数,先要求出在sqrt(100)内的素数,即10以内的素数(2,3,5,7)。抛去2,3,得到的结果为5,7.得到一个检验数组
3)将第一步得到的6n±1数组的每个元素用检验数组中的素数来测试,如果不能被检验数组除尽,则该数必为素数。
证明我忘记了,在图书馆看到的。下面举一个例子,求100以内的素数。
1)将100以内6n±1的数字挑选出来。结果为:
5 7 11 13 17 19 23 25 29
31 35 37 41 43 47 49 53 55
59 61 65 67 71 73 77 79 83
85 89 91 95 97
2)计算出10以内的除了2、3外的素数,为5跟7.
3)分别用5和7来检验1)数组中的数字。比如11不能被5和7整除,所以是素数,25能被5整除,所以不是素数。依次类推。得到的结果为:
5 7 11 13 17 19 23 25 29
31 35 37 41 43 47 49 53 55
59 61 65 67 71 73 77 79 83
85 89 91 95 97
其中红色数字为能被5或7整除的数字。
最后,加上2,3即为所有100以内的素数。
在编程的时候,求10以内的素数用筛选法,然后再用该算法求出素数。具体程序如下:
#include <iostream> //use cin and cout
#include <vector> //use vector
#include <cmath> //use sqrt()
using namespace std;
//void cal_prime(int *data, int data_size)函数为之前定义的筛选法求素数
//初始化数组,将6n+1,6n-1的数字加入数组中
void initialize_vector(vector< int > &data)
{
int i = 1;
while(1)
{
int n = 6* i;
if (n<c_size)
{
data.push_back(n-1);
data.push_back(n+1);
}
else if (n == c_size)
data.push_back(n-1);
else
break;
i++;
}
}
//快速计算该数是否为素数
//将该数字与检验数组中的所有数字相除
int quick(int item, const vector< int > prime)
{
for (int i=0; i<prime.size(); i++)
{
//一旦能被检验数组中的素数除尽
//该数是素数,返回-1标志
if (item%prime[i] == 0)
return -1;
}
//如果检验数组的数字都不能除尽该数
//说明是素数,返回该数字.
return item;
}
void quick_cal_prime(void)
{
vector< int > data;
int i = 1;
initialize_vector(data);
//检验数组的产生
//使用了筛选法求素数
//1)用筛选发求出sqrt(c_size)内的所有素数
int t_size = sqrt(c_size);
int *temp = new int[t_size+1];
memset( temp, 0, sizeof(int)*(t_size+1) );
cal_prime(temp, t_size);
//2)将素数放入检验数组
vector< int > temp_prime;
for (i=5; i<=t_size; i++)
if (temp[i] == 0)
temp_prime.push_back(i);
//3)对素数进行快速检验
for (i=0; i<data.size(); i++)
data[i] = quick(data[i], temp_prime);
//4)打印所有的c_size范围内的所有素数
//需要手工补充2和3
//cout << 2 << "/t" << 3 << "/t";
for (i=0; i<data.size(); i++)
if (data[i] != -1)
cout << data[i] <<endl;
}
实际测试的时候,发现快速发并不”快”,为什么呢?从理论上来说快速法减少了筛选法筛选的次数,估计有两个地方阻碍了它的快速:1)快速法编程的时候使用了vector,效率上不如直接使用数组快;2)快速法使用了多余的语句(为了编程方便).
或许还有其它的原因,比如一个根号运算.
如果大家有更好的算法或者更高效的编程方法请告诉我,谢谢.