找素数的一点思考
2016.7.17
本文代码均由C++编写
1.经典算法
经典的素数判定算法是这样:给定一个正整数n,用2到sqrt(n)之间的所有整数去除n,如果可以整除,则n不是素数,如果不可以整除,则n就是素数。所以求小于N的所有素数程序如下:
#include<iostream>
#include<cmath>
using namespace std;
#define N 1001
bool isprime(int n)
{
int i, m, co = 0;
m = sqrt (n);
for(i = 1; i < m + 1; i++)
{
if(n % i == 0)
co++;
if(co > 1)
break;
}
if(co == 1)
return true;
else
return false;
}
int main()
{
int i;
for(i = 2; i < N; i++)
{
if(isprime(i))
cout<<i<<endl;
}
}
算法的时间复杂度是O(N*sqrt(N)),求解规模较小的输入,尚且没有问题。但对于规模较大的N,算法就力不从心了。有一种算法叫埃拉托斯特尼筛法(sieve of Eratosthenes),它在求解时具有相当高的效率,但是要牺牲较多的空间。
2.埃拉托斯特尼筛法
这个程序的算法是,若i为素数,则设置isprime[i] = 1;如果是合数,则设置为0。首先,将所有的数组元素设为1,表示没有已知的非素数。然后将已知为非素数(即为已知素数的倍数)的索引对应的数组元素设置为0。如果将所有较小素数的倍数都设置为0之后,a[i]仍然保持为1,则可判断它是所找的素数。
#include<iostream>
#include<cmath>
using namespace std;
#define MAX 101
bool isprime[N];
void The_Sieve_of_Eratosthenes()
{
int i, j;
for(i = 2; i < N; i++)
isprime[i] = 1;
for(i = 2; i < N; i++)
{
if(isprime[i])
for(j = 2 * i; j < N; j += i)
isprime[j] = 0;
}
}
int main()
{
int i;
The_Sieve_of_Eratosthenes();
for(i = 2; i < N; i++)
{
if(isprime[i])
cout<<i<<endl;
}
return 0;
}
如何理解埃拉托斯特尼筛法呢?合数n一定是小于n的某个或若干素数的整数倍。反之,如果N不是任何比它小的素数的倍数,则N必然是素数。
3.经典算法改进版
经典素数判定算法中,并不需要用2到sqart(n)之间的所有整数去除n,只需要用其间的素数就够了,原因也是合数一定可以表示成若干个素数之积。算法的改进版本如下:
#include<iostream>
#include<cmath>
using namespace std;
#define N 1001
int isprime[N];
int main()
{
int i, n, flag;
isprime[0] = 0; //保存素数的总个数
for (n = 2 ; n < N; n++)
{
flag = 1;
for (i = 1; i <= isprime[0] && isprime[i] * isprime[i] <= n; i++)
if (n % isprime[i] == 0)
{
flag = 0;
break;
}
if (flag)
{
isprime[0]++;
isprime[isprime[0]] = n;
cout<<n<<endl;
}
}
return 0;
}
此算法相比于普通经典算法,可谓是用空间换时间。