問題說明:
除了自身之外,無法被其它整數整除的數稱之為質數,要求質數很簡單,但如何快速的求出質數則一直是程式設計人員與數學家努力的課題, 在這邊介紹一個着名的 Eratosthenes求質數方法。
解法:
首先知道這個問題可以使用回圈來求解,將一個指定的數除以所有小於它的數,若可以
整除就不是質數,然而如何減少回圈的檢查次數?如何求出小於N的所有質數?
我們先來看一個喪心病狂的低效率的解決方式:
//檢驗質數 bool checkZS(int a) { for (int i = 2;i < a;i++) { if (0 == a%i) { return false; } } return true; }
首先我們寫一個檢驗質數的函數,下面我們在主函數調用:
int n = 99999; clock_t start,end;//用於計時 start = clock() ; for(int i = 1;i <= n;i++) { if (checkZS(i)) { cout<<i<<" "; } } end = clock(); cout<<"\n總共花費了"<<(long double)(end - start)/CLK_TCK<<"秒"<<endl;
好了,讓我們看下在99999以內的質數算出來的運行結果:
時間花費了17秒,太慢了;下面我們想想怎樣來改進算法!
首先知道這個問題可以使用回圈來求解,將一個指定的數除以所有小於它的數,若可以整除就不是質數,然而如何減少回圈的檢查次數?如何求出小於N的所有質數?
首先假設要檢查的數是N好了,則事實上只要檢查至N的開根號就可以了,道理很簡單,假設A*B = N,如果A大於N的開根號,則事實上在小於A之前的檢查就可以先檢查到這個數可以整除N。 不過在程式中使用開根號會精確度的問題, 所以可以使用 i*i <= N進行檢查, 且執行更快 。
再來假設有一個篩子存放1~N,例如:
2 3 4 5 6 7 8 9 10 11 12 ........N
先將2的倍數篩去:
2 3 5 7 9 11 13........N
再將3的倍數篩去:
2 3 5 7 11 13 17 19........N
再來將5的倍數篩去,再來將7的質數篩去,再來將11的倍數篩去........,如此進行到最后留下的數就都是質數,這就是Eratosthenes篩選方法(Eratosthenes Sieve Method)
檢查的次數還可以再減少,事實上,只要檢查6n+1與6n+5就可以了,也就是直接跳過2與3的倍
數,使得程式中的if的檢查動作可以減少。
下面我們上代碼:

/* 問題: 除了自身之外,無法被其它整數整除的數稱之為質數,要求質數很簡單,但如何快速的 求出質數則一直是程式設計人員與數學家努力的課題, 在這邊介紹一個着名的 Eratosthenes求質 數方法。 2013/7/18 張威 */ #include <iostream> #include <time.h> using namespace std; #define n 99999 int main() { int a[n+1];//建立一個數組,使a[i] == i,這樣通過篩選,將非質數所在位置置0 for (int i = 0;i <= n;i++) { a[i] = i; } clock_t start,end;//用於計時 start = clock() ; //每次進行篩選的數,進行優化,實際上只要篩選到 N開放就行 for (int i = 2;i*i <= n;) { //從i處開始篩選(比i小的肯定不能被i整除) for (int j = i;j <= n;j++) { //通過while循環.跳過中間置0區域 while(0 == a[j] && j <= n) { j++; } //假如a[j]能被i整除而且不相等(也就是說不是本身),就把這個位置數值置為0 if (0 == a[j]%i && i != a[j]) { a[j] = 0; } } //i的步進值優化,即跳過2或3的倍數,每次遞增數加大 if((i-1)%6 == 0) i += 4; else if((i-5)%6 == 0) { i += 2; } else { i++; } } end = clock(); for(int i = 2;i <= n;i++) { if (a[i] != 0) { cout<<a[i]<<" "; } } cout<<"\n總共花費了"<<(long double)(end - start)/CLK_TCK<<"秒"<<endl; return 1; }
上面標出了在減少算法中循環次數的優化方面所進行的修改,下面是運行結果:
兩者之間的差距的話.......不說了,自己寫的東西和這些算法相比就是渣渣!
下面上示例上面的代碼:

#include <stdio.h> #include <stdlib.h> #define N 1000 int main(void) { int i, j; int prime[N+1]; for(i = 2; i <= N;i++) prime[i] = 1; for(i = 2; i*i <= N;i++) { // 這邊可以改進 if(prime[i] == 1) { for(j = 2*i; j <= N;j++) { if(j % i == 0) prime[j] = 0; } } } for(i = 2; i < N;i++) { if(prime[i] == 1) { printf("%4d ", i); if(i % 16 == 0) printf("\n"); } } printf("\n"); return 0; }
可以看到其實還是上面自己寫的在示例的基礎上還是做了些改善的!