有個人想上一個50級的臺階,每次只能邁1級或者邁2級臺階,問:這個人有多少種方法可以把臺階走完?例如:總共3級臺階,可以先邁1級再邁2級,或者先邁2級再邁1級,或者邁3次1級總共3中方式。
這個題目我剛看到的時候覺得很有意思,值得思考一下,也和同學討論過思路,並沒有直接去網上找答案,覺得那樣的話就浪費了,所以自己先嚐試著解了。
說這麼多是希望大家也不要直接去看解答,因為看完之後一個星期也就忘了。可以先收藏一手,自己去試試然後再回來看,我會把我自己解的思路和正解都放在這篇文章裡。
從初中生的思路來看,第一個能想到的方法肯定是一個二元方程,也就是x+2y=50,其中x代表邁1步,y代表邁2步,無論怎麼邁總共50級臺階。
接下來的問題就是求這個方程解的數量(不是某一個解),x+2y-50=0這個方程的所有解應該是一條直線,所以我很單純的去把圖給畫了,就像下面這個樣子:
再考慮x和y都必須是整數才能符合題意,y取值在0至25之間,可以看出只要y是整數解那麼x一定為整數解,0也可以作為一種特殊情況,綜上y可能的取值是:0至25共26個。那麼相對應的x取值也就可以算出來了。
這裡還有一種情況,如果總檯階數不是50,那麼y的取值範圍有可能是0至24.5,那麼我認為y只能取值到24,也就是向下取整。
如果僅從數量上來看,xy總共有26種組合的方式,但這肯定不是最後答案,例如,邁2次1步再邁24次2步和先邁24次2步再邁2次1步是兩種不同的方式,那麼接下來就是排列組合的問題。
所以,我們要求的是所有xy同為整數的解的排列之和,是不是感覺有點複雜了,但是沒關係,前面說的內容都是用PHP可以實現的,排列組合也是可以用PHP實現的。然後,排列組合演算法的第一個問題就是如何實現階乘?
在PHP中range函式可以建立一個包含指定範圍單元的陣列,允許傳入三個引數起始值、終止值和步長值,例如rang(1,3)會返回包含1、2、3這三個數的一個陣列。
另外PHP中的array_product函式可以計算陣列中所有值的乘積。
有了這兩個函式我們就可以自己寫出階乘的函式了,雖然我不知道效率怎麼樣,但起碼我們不用往遞迴的方面去想了,另外還需要考慮一下0和1階乘的情況,就是下面這個樣子:
有了階乘,排列也就有了:
回到問題上來,我們需要用到組合嗎?
首先,總數量是確定的x+y,而且第一次邁2步和後面無論第幾次邁2步都是相同的,所以這種排列組合其實可以轉化為“把雞蛋放入籃子”的問題——將n個無差別的雞蛋放入m個籃子中共有幾種放法。那麼求組合數函式也是需要的:
接下來就是迴圈每一個可能解,可以總是將x值作為雞蛋的數量,將xy之和作為籃子的數量,也就是C(x+y,x),舉個例子來說就是,總共要邁26次腳,選擇其中2次只邁1步,有多少種邁法。
有了前面那麼多的思考,我們來寫程式碼:
經過測試,最終50級臺階的走法有20365011074種,二百零三億六千五百零一萬一千零七十四種(醉了)。
那麼到這裡我就開始去網路上找正確答案了,讓我震驚了,發現我的思路果然是初中生的,且是從來不參加任何數學競賽的那種,下面我分析一下網路上答案的思路。
網上的解只有一種,那就是把這個問題歸結為斐波那契數列的問題,完全看不出來好嗎!
答案窮舉了總檯階數從1級到5級的情況,得出結論1級共1種走法,2級2種走法,3級3中走法,4級5種走法,5級有八種走法,然後就開始研究斐波那契數列去了!
那你出個題目出個那麼懸乎幹嘛!直接讓我們用PHP寫個斐波那契函式不就好了嗎!要考察思維能力也得給點提示啊!比如說設定兩個問題:第一問,當臺階總數為5時有幾種走法?第二問,當臺階總數為n時有幾種走法?
總之呢就是在面試的時候,如果你見過這題那麼恭喜你,如果沒見過那麼就可以去下一題了。
當然也有大神給出了比較讓人信服的邏輯:
當我們走到第50層的時候,最後有兩種選擇,從49開始邁1級或者從48開始邁2級。那麼到達50層的走法等於到達49層的走法+到達48層的走法,以此類推,可以得到總的走法符合斐波那契數列,我們的程式碼就好寫了。
恩............膜拜奧數大神的思維。
PHP寫斐波那契應該是比較簡單的問題,那最後讓我們用答案中的斐波那契函式來驗證一下自己的答案。
可以看到,自己的思路和最終答案是相同的,只是過程相對復qing雜xi。
還是不太放心,測試了臺階總數從1至7的所有情況,也都是符合答案的,甚至我沒有特殊考慮的只有1至2層的情況也能返回正確的答案。