Project Euler上有很多有意思的問題,剛做到第27題,對這個問題做個小結。
Problem 27:
Euler有一個著名的方程n^2+n+41,當n=0到39時,方程結果均爲質數。如今人們用電腦計算,發現了另一個方程n^2-79n+1601,當n=0到79時,方程結果也均爲質數。
設一方程形如n^2+an+b,其中|a|<1000,|b|<1000,n爲從0開始的連續自然數。
求使得公式能獲得最多質數的a和b,輸出結果爲a*b。
問題很簡單,首先寫好判斷質數的方法:
bool isPrime(int Num)
{
for(unsigned long int n=2; n<=sqrt(Num);n++)
{
if(Num%n == 0)
{
return false;
}
}
return true;
}
然後直接窮舉遍歷,搜索符合條件的a和b,寫出程序第一版:
void Euler_Cal_27()
{
int n=0;
int maxn = 0;
int expr = 0;
int result = 0;
for(int a=-999; a<1000; a++)
{
for(int b=-999; b<1000; b++)
{
n = 0;
expr = n*n + a*n + b;
while(isPrime(expr) &&expr>0)
{
n++;
expr = n*n + a*n + b;
}
if( n>maxn )
{
maxn = n;
result = a*b;
}
}
}
}
cout<< result <<endl;
}
電腦CPU是E6600,得到正確結果用時1.4s。雖然用時不長,但是搜索範圍很小,所以肯定有提高的餘地。優化自然先從循環着手,仔細檢查一下發現while循環條件順序有誤。&&運算只要前一個條件爲假就不進行後面條件的判斷,而這裏恰恰將判斷質數放在判斷正負這種簡單運算前面,影響速度。所以調換條件順序,此時程序運行時間減少至0.3s。又看了一會兒之後再沒想到其他優化方法,所以上論壇看看其他人是如何解決的。一看果然有收穫。
1、 n須從0開始,所以當n=0時,方程結果必爲質數,否則此時的a和b肯定不是最優結果。而n=0時,方程結果是b,所以b必須爲質數。
2、 當n=1時,方程結果爲1+a+b,質數必須大於0,所以有a>-b-1。即確定b後,可以縮小a的搜索範圍。
還有人事先生成質數表,通過查表來判斷質數,減少運行時間。但是這種方法在改變搜索範圍時需要重新生成質數表,有些麻煩,所以沒有使用。僅依照以上提到的兩個條件進行優化,第二版代碼如下:
voidEuler_Cal_27()
{
int n=0;
int maxn = 0;
int expr = 0;
int result = 0;
for(int b=1; b<1000; b++)
{
if(isPrime(b))
{
for(int a=-b; a<1000; a++)
{
n = 0;
expr = n*n + a*n + b;
while( expr>0 && isPrime(expr) )
{
n++;
expr = n*n + a*n + b;
}
if( n>maxn )
{
maxn = n;
result = a*b;
}
}
}
}
cout<< result <<endl;
}
此時程序運行時間減少至0.17s。
從這次優化小程序發現,優化程序時大多從代碼本身入手,忽略問題,不去想原理,在進一步提高程序性能的時候容易遇到瓶頸。以後要注重鍛鍊自己理解問題的能力,基礎還是很差,需要提高。