(ZZ)素數算法大全,及C程序實現優化詳解 (一) 試除法

常常有初學者詢問求解N內全部素數(質數)的問題,對此,網上的解答也不少,但不少要麼不夠專業,要麼只有程序沒有算法解析,因此三藏大廈對此問題作個小結,探討一下求解素數的常見算法,同時給出相應的C語言程序及其解析。爲了方便初學者理解,本文將從易到難闡述不一樣算法,高手能夠直接看後面的高效算法

質數的定義 算法

一個數,若是隻有1和它自己兩個因數,這樣的數叫作質數,又稱素數。  編程

試除判斷法 函數

算法描述:從上述定義可知,素數不能被1和它自己以外的數整除,因此,判斷一個數x是否素數只要看它是否能被2~sqrt(x)間的數整除便可;而求N內全部素數則是循環重複上述過程。 學習

C語言實現優化

#include <time.h>
#include <malloc.h> 
#define N 100000
// 簡單試除判斷法 Ver1 
int SimpleDivisionV1(int n)
{
 int i,j;
 // 素數數量統計 
 int count = 0;
 // 分配存放結果的空間 
 int* primes = (int*)malloc( sizeof(int)*n ); 
 
 // 2是素數誰都知道,不算了 
 primes[count++] = 2;
 // 循環計算3~n間的數 
 for (i=3; i<=n; i++)
 {
  // 爲何是sqrt(i),思考一下 
  for (j=2; j<=sqrt(i); j++)
  {
   // i被j整除,顯然不是素數了 
   if (i%j == 0) break;
  }
  // i不能被2~sqrt(i)間的數整除,素數也 
  if (j > sqrt(i))
  {
   primes[count++] = i;
  }
 }
 
 // 因輸出費時,且和算法核心相關不大,故略
  
 // 釋放內存,別忘了傳說中的內存泄漏 
 free(primes);
 
 return count;
} 

void main()
{
 int count;
 clock_t start, end;
 // time函數不夠精確,用clock湊合一下吧 
 start = clock(); 
 count = SimpleDivisionV1(N);
 
 end = clock();
 printf("[%d]之內素數個數:%d, 計算用時:%d毫秒\n", N, count, end-start);
 getch(); 
}

計算結果:
[100000]之內素數個數:9592, 計算用時:468毫秒
[1000000]之內素數個數:78498, 計算用時:10859毫秒
[5000000]之內素數個數:348513, 計算用時:103560毫秒 code

噢噢,算算十萬還行,百萬就10秒多了,並且時間增加很快,這不行,得優化一下! 內存

優化分析get

仔細研究一下SimpleDivisionV1咱們能夠發現如下幾個問題: io

  1. 在循環條件中重複調用sqrt(i)顯然是比較浪費時間的
  2. 判斷素數,真的須要拿2~sqrt(i)間的全部整數去除嗎?咱們知道,合數均可以分解成若干質數,因此只要2~sqrt(i)間的質數不能整除i便可

根據上面兩點,咱們可將SimpleDivisionV1升級爲SimpleDivisionV2,以下 效率

 // 簡單試除判斷法 Ver2 
int SimpleDivisionV2(int n)
{
 int i, j, k, stop;
 // 素數數量統計 
 int count = 0;
 // 分配存放結果的空間 
 int* primes = (int*)malloc( sizeof(int)*n ); 
 
 // 2是素數誰都知道,不算了 
 primes[count++] = 2;
 stop = count;
 // 循環計算3~n間的數 
 for (i=3; i<=n; i++)
 {
  k = sqrt(i);
  // 在循環條件中重複調用sqrt是低效作法,故引入k 
  while (primes[stop] <= k && stop < count)
   stop++;
  // stop幹什麼用,思考一下
  for (j=0; j<stop; j++)
  {
   if (i%primes[j] == 0) break;
  }
  // i不能被2~sqrt(i)間的素數整除,天然也不能被其餘數整除,素數也 
  if (j == stop)
  {
   primes[count++] = i;
  }
 }
 
 // 因輸出費時,且和算法核心相關不大,故略
  
 // 釋放內存,別忘了傳說中的內存泄漏 
 free(primes);
 
 return count;
}

而後將main中調用的函數替換爲SimpleDivisionV2,在看一下執行結果:

[100000]之內素數個數:9592, 計算用時:46毫秒
[1000000]之內素數個數:78498, 計算用時:546毫秒
[5000000]之內素數個數:348513, 計算用時:3515毫秒
[10000000]之內素數個數:664579, 計算用時:8000毫秒

很開心的看到,通過優化,速度提升了幾十倍,尤爲是時間增加曲線的坡度變小了,N值越大,V2函數比V1的效率就越高

對於試除判斷這種質數算法來講,三藏認爲SimpleDivisionV2基本已經接近極限,不大可能有量級上的突破了,有興趣的朋友能夠本身進一步優化。初學者除了參看上述例子外,能夠嘗試作各類修改及細節優化,也能夠將除法變乘法,多加練習是學習編程的好方法。

雖然,上例中V2已經比V1快了不少了,但隨着N的增大,耗時仍是很多,那麼咱們還有更好的方法嗎?

相關文章
相關標籤/搜索