用数组实现打印小于10000的素数的快速算法

      这是从塞奇威克的《算法:C语言实现》第二部分拾得的一个用数组实现打印小于10000的素数的例子。代码和部分说明摘自本书。

      这个方法可以追溯到公元前三世级,被称为埃拉托色尼筛法(sieve of Eratosthenes)。

 

  
  
  
  
  1. #define N   10000 
  2. main() { 
  3.     int i, j, a[N]; 
  4.     for ( i = 2; i < N; i++ ) {  /* 初始化所有的元素为1 */
  5.         a[i] = 1; 
  6.     } 
  7.     for ( i = 2; i < N; i++ ) {  /* 从2开始遍历数组,找出小于N的所有素数 */
  8.         if ( a[i] ) {  /* 判断i是否为素数,避免重复计算 */
  9.             for ( j = i; i*j < N; j++ ) {/* 标记i的倍数为非素数,注意是从i的i倍开始 */
  10.                 a[i*j] = 0; 
  11.             } 
  12.         } 
  13.     } 
  14.     for ( i = 2; i < N; i++ ) {  /* 从2开始遍历数组 */
  15.         if ( a[i] ) {  /* 如果a[i]为0,说明 i 是素数,打印之 */
  16.             printf( "%4d ", i ); 
  17.         } 
  18.     } 
  19.     printf( "\n" ); 

       这个算法充分利用了数组快速索引的优点。如果自然数 i 为素数,则设 a[i]为1,否则设为0。首先把数组中所有元素标记为1,表示没有任何数已被证明是非素数(L4-L6)。然后,把数组中所对应索引处已证明是非素数(已知素数的倍数)的元素标记为0(L7-L13)。如果比 i 小的所有素数的倍数都已标记为0,a[i]仍然为1,则可知它是素数(L14-L19)。

      L8的 if ( a[i] ) 判断语句能够避免重复计算,提高计算速度。

      (1)若a[i] == 0,表明 i 为非素数,则可知在 i 被标记为非素数的那个循环中,所有 i 的倍数也被标记为非素数,这是根据一个简单的数学原理:(| 表示整除)如果a | b,b | c,则a | c。比如,我们不需要找6的倍数(12,18,24...)然后将它们标记为非素数——因为在找2的倍数时我们已经标记了!

          (2)若a[i] == 1,则表明 i 为素数,我们不能保证它的所有倍数都已为标记为非素数,比如:5的7倍为35,它是个非素数,但在5前遍历2和3的倍数时都没有被标记上,所以我们自然进入第三个for循环找寻它的倍数。注意,我们从 i 的 i 倍开始找寻,这是因为所有 i 的 k 倍( 1 < k < i)在前面的遍历中一定被标记为非素数了的,这里就不用重复找寻了。比如:5的3倍为15,它在遍历3时已作为3的5倍被标记上了。

   

           由于数组a[]的每个元素都只需要两个数(比如 0 和 1)来表明是素数或是非素数,我们可以用char型替代int型。素数标记为'1',非素数标记为'0'。判断语句改为if ( a[i] == '1' )。如果int型占4字节,char型占1字节,N取10,000,则可以节约 ( 4 - 1 ) * 10,000 = 30,000 字节,约 30 KB的内存。

         本书数组那一节还有一个例子,讲如何利用数组快速索引的优点模拟伯努利实验,也值得学习学习。

 

        这两本书从当当订购上周末刚到,书评区小骂了一下可恶的快递和快递员,结果书评被和谐了(意料之中),唉。幸好,书没被和谐掉就OK啦。

你可能感兴趣的:(算法,素数,数组,休闲,C语言实现)