寻找素数

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

解决方案

程序 1-1 经典算法

经典的素数判定算法是这样:给定一个正整数n,用2sqrt(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("%d\n",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[N];

 

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("%d\n",i);

  system("PAUSE"); 

  return 0;

}

 

例如,计算小于32的素数,先将所有数组项初始化为1(如下第二列),表示还每发现非素数的数。接下来,将索引为235倍数的数组项设置成0,因为235倍数的数是非素数。保持为1的数组项对应索引为素数(如下最右列)。

                                 a[i]

                                        1

                                        1

                                    

                                        1

                 

                                        1

            0

                      0

10           0

11                                       1

12           0

13                                       1

14           0

15                     0

16           0

17                                       1

18           0

19                                       1

20           0

21                     0

22           0

23                                       1

24           0

25                              0

26           0

27                     0

28           0

29                                       1

30           0

31                                       1

 

如何理解厄拉多塞筛算法呢?

我们一定记得小学时候就学过的素数和合数的性质:任何一个合数一定可以表示成若干个素数之积。如:4 = 2 * 26 = 2 * 312 = 2 * 2 * 3。也就是说,合数N一定是小于N的某个(或若干)素数的整数倍,反之,如果N不是任何比它小的素数的倍数,则N必然是素数。

 

 

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

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

 

#include <stdio.h>

#include <stdlib.h>

 

#define N 1000000

 

int prime[N];

 

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("%d\n",n);

        }

    }

    system("PAUSE");   

    return 0;

}

 

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

 

 

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

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

 

#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("M ",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));

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值