您的位置:首页 > 其它

找素数算法总结

2012-06-06 23:31 274 查看
问题描述:寻找素数

求小于自然数N的所有素数。

解决方案

程序 1-1 经典算法

经典的素数判定算法是这样:给定一个正整数n,用2到sqrt(n)之间的所有整数去除n,如果可以整除,则n不是素数,如果不可以整除,则n就是素数。所以求小于N的所有素数程序如下:

#include <stdio.h>
#include <stdlib.h>

#define N 1000000

int main(int argc, char *argv[])
{
int m, n;
for (n =2 ; n < N; n++)
{
for (m = 2; m * m <= n; m++)
if (n % m == 0) break;
if (m * m > n) printf("%6d",n);
}
system("PAUSE");
return 0;

算法的时间复杂度是O(N*sqrt(N)),求解规模较小的输入,尚且没有问题。但对于规模较大的N,算法就力不从心了。有一种算法叫厄拉多塞筛(sieve of Eratosthenes),它在求解时具有相当高的效率,但是要牺牲较多的空间。

程序 1-2 厄拉多塞筛算法

这个程序的目标是,若i为素数,则设置a[i] = 1;如果不是素数,则设置为0。首先,将所有的数组元素设为1,表示没有已知的非素数。然后将已知为非素数(即为已知素数的倍数)的索引对应的数组元素设置为0。如果将所有较小素数的倍数都设置为0之后,a[i]仍然保持为1,则可判断它是所找的素数。

#include <stdio.h>
#include <stdlib.h>

#define N 1000000

int a
;

int main(int argc, char *argv[])
{
int i, j;
for (i = 2; i < N; i++) a[i] = 1;
for (i = 2; i < N; i++)
{
if (a[i])
for (j = i + i; j < N; j += i)
a[j] = 0;
}
for (i =2; i < N; i++)
if (a[i]) printf("%6d ",i);
system("PAUSE");
return 0;
}

例如,计算小于32的素数,先将所有数组项初始化为1(如下第二列),表示还每发现非素数的数。接下来,将索引为2、3、5倍数的数组项设置成0,因为2、3、5倍数的数是非素数。保持为1的数组项对应索引为素数(如下最右列)。
i 2 3 5 a[i]
2 1 1
3 1 1
4 1 0
5 1 1
6 1 0
7 1 1
8 1 0
9 1 0
10 1 0
11 1 1
12 1 0
13 1 1
14 1 0
15 1 0
16 1 0
17 1 1
18 1 0
19 1 1
20 1 0
21 1 0
22 1 0
23 1 1
24 1 0
25 1 0
26 1 0
27 1 0
28 1 0
29 1 1
30 1 0
31 1 1

如何理解厄拉多塞筛算法呢?
我们一定记得小学时候就学过的素数和合数的性质:任何一个合数一定可以表示成若干个素数之积。如:4 = 2 * 2,6 = 2 * 3,12 = 2 * 2 * 3。也就是说,合数N一定是小于N的某个(或若干)素数的整数倍,反之,如果N不是任何比它小的素数的倍数,则N必然是素数。

程序 1-3 经典算法的改进版本

经典素数判定算法中,并不需要用2到sqart(n)之间的所有整数去除n,只需要用其间的素数就够了,原因也是合数一定可以表示成若干个素数之积。算法的改进版本如下:

#include <stdio.h>
#include <stdlib.h>

#define N 1000000

int prime
;

int main(int argc, char *argv[])
{
int i, n, flag;
prime[0] = 0; //保存素数的总个数
for (n =2 ; n < N; n++)
{
flag = 1;
for (i = 1; i <= prime[0] && prime[i] * prime[i] <= n; i++)
if (n % prime[i] == 0)
{
flag = 0;
break;
}
if (flag)
{
prime[0]++;
prime[prime[0]] = n;
printf("%6d ",n);
}
}
system("PAUSE");
return 0;
}

算法虽然在效率下,比先前版本有所提高,但需要牺牲空间记住已确定的素数。

程序 1-4 厄拉多塞筛算法的改进版

程序1-2使用一个数组来包含最简单的元素类型,0和1两个值,如果我们使用位的数组,而不是使用整数的数组,则可获得更高的空间有效性。

#include <stdio.h>
#include <stdlib.h>

#define N 1000000
#define BITSPERWORD 32
#define SHIFT 5
#define MASK 0x1F
#define COUNT 1+N/BITSPERWORD //Bit i 对映数i

void set(int a[],int i);
void clr(int a[],int i);
int test(int a[],int i);

int a[COUNT];

int main(int argc, char *argv[])
{
int i, j;
for (i = 0; i < COUNT; i++) a[i] = -1;
for (i = 2; i < N; i++) {
if (test(a,i))
for (j = i + i; j < N; j += i)
clr(a,j);
}
for (i =2; i < N; i++)
if (test(a,i)) printf("%4d ",i);
system("PAUSE");
return 0;
}

void set(int a[],int i) {
a[i >> SHIFT] |= (1<<(i & MASK));
}

void clr(int a[],int i) {
a[i >> SHIFT] &= ~(1<<(i & MASK));
}

int test(int a[],int i) {
return a[i >> SHIFT] & (1<<(i & MASK));
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: