前天,得知一個朋友去了網易應聘,只是在筆試中遇到幾個比較麻煩的演算法問題(主要是在考試限時內有難度),告訴我們幾個相熟的共同探討,任意自然數N分解為連續自然數和問題就是其中之一。這個問題很容易就能得到其長為k+1的序列和必然有等於0~k之和加上k+1個m的規律,但是要想得到高效的演算法就必須先找到一個理想的邊界條件,這樣才能有效減少不必要的尋找,一旦邊界不對則得不到完整正確的解。一開始我總想直接求出最長序列以便得到其長度,然而實在是數學功底差,不得不求其次另找了個差一些的條件,這個條件就是:噹噹前序列的長度k對應的0~k之和大於該自然數N時,測試終止。這個原理很容易理解吧?
以下就是代碼:
#include <iostream>
#include <cmath>
using std::cout;
using std::cin;
using std::endl;
int main(void)
...{
int N = 0;
cout<<"請輸入N: ";
while(!(cin>>N))
...{
cout<<"input fail,please retry!"<<endl;
cout<<"請輸入N: ";
cin.clear();
}
int k = 0;
int m = N;
int i = 0;
int big,small,pre_i;//這裡的幾個變數不過十位了讓代碼的含義更清晰,其實完全可以省略.
int j = 0; //這個主要是為了測試和分析方便,也不是必要的.
do
...{
pre_i = k = k+i;
m = (N-k)/(i+1);
if(N == m*(i+1)+k)
...{
big = m+i;
small = m;
++j;
cout<<"circle ["<<i+1<<"]; from "<<small<<" to "<<big<<endl;
}
++i;
}while(N>pre_i+1);//如果N<pre_i(前i項和),說明達到最長序列
cout<<"circles["<<i<<"] total "<<j<<endl;
system("pause");
return 0;
}
下面是測試1:
可以看出,就這個數字來說,複雜度相差很小,近乎理想。那麼再試試另一個:
看看,和理想的640次迴圈相比多了將近一倍。然而這個差距並非單調遞增的。
不過效率還是可以的,在我的酷睿E6300機器上可以說一閃而過,主要是輸出操作佔用了比較長的時間。
然而。。。。。當N接近資料類型上限時,溢出問題太嚴重了,補救方法是將while迴圈條件中的資料強制轉換成無符號以提高資料上限。儘管數學理論沒有錯,但是顯然不適合電腦實現。這讓我想起有人認為,研究演算法的可以完全不會寫代碼,甚至不瞭解電腦原理,這可能嗎?就像上面這個演算法,理論上有錯嗎?然而實際條件給限制了。不理解資料怎麼溢出,就不能提前預見這種錯誤,那麼費了半天神想出的演算法只能是空談。
而另一重解決方案是預估計序列的最大長度,再用這個長度作為迴圈結束條件,經分析,最大長度應該為:
int maxLength = pow(N* 2.0,0.5) + 1;
從而程式修改如下:
#include <iostream>
#include <cmath>
using std::cout;
using std::cin;
using std::endl;
int main(void)
...{
int N = 0;
cout<<"請輸入N: ";
while(!(cin>>N))
...{
cout<<"input fail,please retry!"<<endl;
cout<<"請輸入N: ";
cin.clear();
}
int k = 0;
int m = N;
int i = 0;
int big,small,pre_m;//這裡的幾個變數不過十位了讓代碼的含義更清晰,其實完全可以省略.
int j = 0; //這個主要是為了測試和分析方便,也不是必要的.
int maxLength = pow(N* 2.0,0.5) + 1;//這是我們估計的最長序列長度。
// int maxLength = log((double)N);
cout<<"maxLength = "<<maxLength<<endl;
do
...{
k = k+i;
// pre_m =k;
m = (N-k)/(i+1);
if(N == m*(i+1)+k)
...{
big = m+i;
small = m;
++j;
cout<<"circle ["<<i+1<<"]; from "<<small<<" to "<<big<<endl;
}
++i;
// }while((unsigned)N>(unsigned)pre_m);//如果N<pre_i(前i項和),說明達到最長序列
}while(i<maxLength);
cout<<"circles["<<i<<"] total "<<j<<endl;
system("pause");
return 0;
}