還記得排列組合嗎?
在高中的時候最常接觸的莫過於排列組合了,畢竟高考必考的嘛。咱們先來回憶下這兩個的公式是啥:
排列組合公式
若是看到這個還有一丟丟的印象,說明你們的基礎都還不錯。那麼問題來了,你們都是學計算機的,咱們如何用程序去模擬這個過程,從而達到列出全部排列組合的可能呢?
全排列的實現
暴力求解(不可取,不可取)
相信不少初入門的小夥伴首先想到的就是就是直接經過嵌套多個for循環去遍歷不就行了,只要不相等就直接輸出,就像下面這樣:css
def force(): data = "abc" for i in range(len(data)): for j in range(len(data)): for k in range(len(data)): if data[i] != data[j] and data[j] != data[k] and data[k] != data[i]: print(data[i],data[j],data[k])if __name__ == '__main__': force()/*輸出 a b c a c b b a c b c a c a b c b a */
看上去還能夠的樣子,不過這樣有幾個壞處,若是不想全排列 abc 了,而是想對 abcd 進行全排列了,那麼咱們必需要修改源代碼增長一個for循環,並且若是排列的數不少的話,那這個循環也太多了吧。
遞歸求解
上面這種解法實在有點不優雅,那麼咱們如何在不修改源碼的狀況下就能夠求出全部的排列組合狀況呢?
解決全排列的重複問題
細心的小夥伴確定會發現,上面的代碼實際上是有問題的,若是排列的數組中有重複的元素那麼結果中也會有重複的排列,這是咱們不但願看到的。那麼咱們如何解決這個問題呢?
要想解決這個問題,咱們首先須要知道這個問題是怎麼來的,仍是參考剛剛的圖,咱們稍微修改下:
html
對於 abc 的排列,當咱們進行排列時,能夠先考慮第1位可能有哪些狀況,如上圖所示有a,b,c三種狀況,而後再對後面的兩位進行排列,採用相同的思路,因此咱們能夠很容易的就經過遞歸實現全排列了。前端
def rank(data, step): if len(data) == step+1: print(data) return else: for i in range(step, len(data)): data[step], data[i] = data[i], data[step] //讓當前首位依次爲後面的每個數 rank(data, step + 1) //遞歸後面的狀況 data[step], data[i] = data[i], data[step]if __name__ == '__main__': data = list("abc") rank(data, 0)
運行上面的代碼,能夠獲得和上面暴力求解一毛同樣的結果,且此次若是須要對其餘狀況進行全排列不須要再修改源代碼,且只用了一個循環(雖然用遞歸效率還不如多個循環^-^),不過至少代碼看上去仍是很優雅的。
程序員
就拿 cac 這個舉個栗子:
當以第一個c爲開頭時,咱們須要對 ac 進行全排列,沒問題
當以a爲開頭時,咱們須要對 cc 進行全排列,沒問題
當以第二個c爲開頭時,咱們須要對 ca 進行全排列,這就有問題了,ac和ca的全排列不就同樣的嘛,並且開頭也同樣,這個確定就會有重複了呀,咱們對源碼稍加修改下:web
def is_equal(data,left,right): #判斷left到當前right是否有相等的,若是有說明以前已經對這 for i in range(left,right): #個進行過全排序了 if data[i] == data[right]: return True return Falsedef rank(data, step): if len(data) == step+1: print(data) return else: for i in range(step, len(data)): if is_equal(data,step,i): #加一個判斷 continue else: data[step], data[i] = data[i], data[step] rank(data, step + 1) data[step], data[i] = data[i], data[step]if __name__ == '__main__': data = list("bcc") rank(data, 0)
ok,這樣運行上面的代碼的就不會有重複的問題了,這裏可能須要小夥伴們多思考下了,不過仍是很簡單的。
組合問題
問題描述
加入我有一個數組 [1, 2, 3, 4, 5, 6] ,我想從裏面隨機選出三個來,問有哪些取法。
解決思路
一樣是遞歸,由於咱們也不知道循環的具體次數嘛,可是要如何遞歸呢?
面試
不要急,假設咱們要從上面的數組中選出3個元素出來。
咱們首先從第一個元素下手,對於第一個元素,咱們有兩個選擇:要 or 不要。
若是要了,那麼咱們須要選擇的元素就少了一個了,咱們只須要從後面的元素中選出兩個就夠了。
若是不要,咱們就從第二個元素繼續看,此時咱們仍是要選出三個(原本想畫圖演示的,不過這個圖好像有點複雜,筆者畫圖實在是個菜鳥就偷會懶了)。算法
def combine(data, step, select_data, target_num): if len(select_data) == target_num: #選擇的元素已經夠了,就輸出並返回 print(select_data) return if step >= len(data): #沒有元素選了並且還沒夠,也是直接返回 return select_data.append(data[step]) #選擇當前元素 combine(data, step + 1, select_data, target_num) select_data.pop() #別忘了從已選擇元素中先刪除 combine(data, step + 1, select_data, target_num) #不選擇當前元素if __name__ == '__main__': data = [1, 2, 3, 4, 5, 6] combine(data, 0, [], 3)
運行上面的代碼就能夠得出全部的組合可能了,仍是比較優雅的。可是一樣當數組中有重複元素的時候也會有重複的組合,這個如何解決呢?這個就讓小夥伴們本身思考下吧。
總結
排列組合算法仍是很貼近咱們生活的,在各類算法,項目與面試中也會常常碰見,因此也算程序員必備算法了,上面代碼若是有問題也歡迎小夥伴與筆者留言私信交流,一塊兒學習交流一塊兒進步。編程
「小編每晚都會講解企業案例知識點,也有準備一份適合2018年學習的web前端教程,html+css+原生js到H5移動端及各類框架都有整理,送給每一位前端小夥伴,這裏是暴走前端院,歡迎初學和進階中的小夥伴。」
想要學習或者瞭解web前端編程的小夥伴,能夠加羣:575308719(學習交流/領取教程)