一、前言
昨晚下班后,經理出於興趣給我們技術組講了講算法相關的東西,全程一臉懵逼的聽,中途還給我們出了一道比較有趣的爬樓問題,問題如下:
假設一個人從地面開始爬樓梯,規定一步只能爬一坎或者兩坎,人只能往上走,例如爬到第一坎,很明顯從地面到第一坎只有一種可選方式,從地面爬到第二坎,他可以從地面直接跨到第二坎,也可以先從地面到第一坎,再從第一坎到第二坎,也就是2種可選方式,那么求他爬到N樓一共有幾種可選方式。
這道題涉及到了斐波那契數列,要求使用遞歸來求值,技術賊菜的我也是一臉懵逼,所以本着學習的心還是記錄下來了。
二、有趣的斐波那契數列
我們將地面理解為數字0,第一坎理解為數字1,以此來進行簡單的分析:
例如從0到1,就一種選擇,如圖(公司不讓用破解軟件,沒PS畫圖,湊合看吧,畫圖的手微微顫抖。。。)
那么,現在要求到第二坎,從0到2呢,兩種選擇,看圖分析(記住,一步只能跨一坎或者兩坎):
當要求從0到3,三種選擇,如圖:
紫色:一坎坎的走;黃色:先走兩坎,再走一坎;藍色:先走一坎,再走兩坎。
從0-4,一共五種情況,如圖:
這里我分開畫了,左邊的2種情況加上右邊的三種情況
左邊:
第一種:一坎坎的走,0-1-2-3-4
第二種:兩坎兩坎走,0-2-4
右邊:
第三種:先走兩坎,再一坎坎的走0-2-3-4
第四種:先走一坎,再走兩坎,再走一坎0-1-3-4
第五種:先走一坎,再走一坎,再走兩坎0-1-2-4
當我們繼續往后畫,樓梯層對應走法會形成一個有趣的規律,
從第三層開始,第三層的走法等於一層與二層走法的和,第四層的走法等於第二層和第三層走法的和.....針對樓梯問題,我們可以得到如下公式:
F(n) = F(n-1) + F(n-2) n>=3
這就是著名的斐波那契數列(Fibonacci sequence),又稱黃金分割數列。有興趣的同學可以查查兔子繁殖問題。
百科里斐波那契數列的基數n>=4是根據實際情況來定的,這點不用糾結。
三、斐波那契數列與遞歸的結合
什么是遞歸?自己調用自己的函數就是遞歸,這么說完全沒問題。
我們回歸上面的問題,要求第N層的走法,那我們只需要知道N-1層和N-2層的走法就好了,假設N是10,求到第十層的走法。
十層走法=九層的走法+八層走法
九層和八層也可以拆分啊,九層走法 = 七層走法 + 八層走法 ,而八層走法 = 七層走法 + 六層走法
.......
當分到3層時,最后還可以拆分為1層+2層,因為規律是從第三層開始的,因為此公式不適用於1層和2層,分到1層和2層就代表分支的結束了。
使用閉包需要找到跳出自己調用自己的的臨界條件,不然會會陷入死循環,那對應我們的樓梯問題,只要N<3時就不需要繼續調用自己了,因為不需要繼續產生分支了。
這個閉包怎么寫呢?我們結合斐波那契數列公式嘗試一下吧。
現在有一個函數,里面申明一個走法變量var step = 0;輸入一個數字N,我們會對N判斷,只要N>=3,它就會自己調用自己,小於3時,我們分別判斷它等於1或者2,等於1就讓step加1,等於2就讓step加2,如下:
function recursion(n){ let step = 0; if (n === 1 ){ step += 1 }else if (n === 2){ step += 2; }else if (n >= 3){ step = recursion(n-1) + recursion(n-2); }; return step; }; console.log(recursion(10))//89種
為了驗證,我將上方分支圖中所有的1與2相加(1層只有1種走法,2層有2種),得出數字也確實是89。所以我不明白我為什么要把基數N設置為10,算的難受。
那么趁熱打鐵,活學活用,現在我們嘗試求正整數N與N之前所有正整數累加的和。
例如3之前累加的和就是3+2+1,數字0加不加沒意義,所以跳出遞歸的臨界條件就是當N>=2時,最后調用一次讓之前數字的和加一次1就好了。
var result = 0; function add(n){ result += n n>=2 ? add(n-1) : null; return result; }; console.log(add(10));//55
驗證一下,完全沒問題,有沒有覺得遞歸挺簡單。
當你看到這時,恭喜你,不僅了解了斐波那契數列,也簡單了解了遞歸的用法。其實本人在工作中需要操作數據,本能的總是想到窮舉,循環,那么現在又多了一種可行的解決方法,也算是對於思維的開拓了。
留個問題,回到上方兩段實現代碼,為什么第一個變量申明在函數體內,而第二個數字累加的變量申明要在函數體外呢?嘗試思考下。
當然,前提是,這篇博客如果有人願意看完就挺好了。