1. 問題描述
圍繞着山頂有10個洞,狐狸想要吃兔子,兔子說:“可以,但必須找到我,我就藏身於這10個洞中,你從10號洞出發,先到1號洞找,第二次隔1個洞找,第三次隔2個洞找,以后如此類推,次數不限。”但狐狸從早到晚進進出出了1000次,仍沒有找到兔子。問兔子究竟藏在哪個洞里?
2. 問題分析
(1) 把10個洞編上序號:1,2,3,4,5,6,7,8,9,0
(2) 狐狸從0號洞出發,第1次在1號洞找;第2次隔1個洞,在3號洞找;第3次隔2個洞,在6號洞找;第4次隔3個洞,在0號洞找;第5次隔4個洞口,即在5號洞找;以此類推。
(3) 可見,其實上述問題其實是一個等差數列求和問題(首項為1,公差為1)。將求和結果對10取余(求和結果%10),即可得到狐狸每次進去的洞口號。
(4) 如下圖所示,仔細觀察可以發現,如果狐狸連續兩次都進入出發洞口(0號洞),表明接下來進入洞口的順序與之前的順序相同!因此遇到連續兩次都進入出發洞口的情況,即可停止尋找,因為接下來只是重復之前找過的洞口。

3. 算法描述
(1) 首項為1,公差為1的等差數列求和公式:n*(n+1)/2,n為尋找的次數
(2) 狐狸每次進入的洞口號為:(n*(n+1)/2)%10,對10取余是因為有10個洞口
(3) 設x為尋找的次數,連續兩次進入都是出發時的洞口(0號洞),可停止尋找。設
HOLENUM為洞口的個數,本例是10
即第x-1次:((x-1)*(x-1+1)/2)%HOLENUM == 0
第x次:(x*(x+1)/2)%HOLENUM == 0
一般的,x = 2*HOLENUM。即如果洞口有10個,則需要找20次,就會在遇到連續兩次進入出發的洞口(本例是第19次和第20次)
特殊的,如果HOLENUM是(2^k +1),則 x = HOLENUM,(k = 0,1,2,3......)。即如果洞口有15個, 則只需找15次,就會遇到連續兩次進入出發時的洞口(第14次和第15次)
(4) 可見,該算法的時間復雜度與洞口的個數有關,具體的時間復雜度為Θ(n)
4. 代碼實現
1 #include <stdio.h> 2 #define HOLENUM 10 3 4 void FindRibbit(int Hole[HOLENUM]); 5 6 int main() 7 { 8 int hole[HOLENUM] = {0}; // 10個山洞 9 FindRibbit(hole); 10 return 0; 11 } 12 13 void FindRibbit(int Hole[HOLENUM]) 14 { 15 int i = 0; 16 int currenthole = 0, nexthole = 0; 17 for(i = 1; i <= 2 * HOLENUM; ++i) 18 { 19 currenthole = (currenthole + i) % HOLENUM; // 當前進去的山洞 20 Hole[currenthole % HOLENUM] = 1; 21 nexthole = (currenthole + i + 1) % HOLENUM; // 下一次進去的山洞 22 23 if(currenthole == 0 && nexthole == 0) // 如果連續兩次進去的山洞都是出發時的洞口,則接下來的進洞順序與前面的順序重復,故可以跳出循環 24 break; // 本例是從10號(即0號)洞口出發 25 } 26 28 for(i = 0; i < HOLENUM; ++i) // 輸出兔子可能躲藏的洞口 29 { 30 if(Hole[i] == 0) 31 printf("the ribbit may be in %d hole\n", i); 32 } 33 }
5. 測試結果
the ribbit may be in 2 hole the ribbit may be in 4 hole the ribbit may be in 7 hole the ribbit may be in 9 hole

