1. 程式人生 > >遞迴-python3解決n個數的排列問題

遞迴-python3解決n個數的排列問題

0.摘要

給定n個不相同的數字,輸出所有的排列方式。

1.思路

首先,我們回憶一下數學上解決排列問題的方法:

我們先從所有資料中選取一個,放在第一位;然後,再從剩下的資料中選取一個,放在第二位;不斷重複,直到最後一位。這樣我們就得到了所有的排列結果。

寫成具體可操作的步驟即:

step1:a[0],a[1]……a[n],從n個元素選擇一個

step2:除去step1已經選取的元素,從n-1個元素再選擇一個

……

step(n):除去step1-step(n-1)已經選取的n-1個元素,只剩一個元素可選。

根據排列組合的思路:n個不同的數,排列組合共有n!種。因此,直接的思路是,使用n重for迴圈遍歷,即可得到所有排列組合結果。

但是,這樣的思路存在兩個問題:

  • 問題一:題目中給定的n並沒有確定,所以for迴圈層數不確定。上述思路只適合n為確定數字的情況;
  • 問題二:已經選取的元素,需要有一個數組記錄狀態,這樣才能避免被重複選取

2.一個可行但不太好的方案:

首先,解決問題一:

分析上述問題,發現它符合運用遞迴的條件:

  • 可以把要解決的問題轉化為一個新問題,而這個新的問題的解決方法仍與原來的解決方法相同,只是所處理的物件有規律地遞增或遞減。
  • 可以應用這個轉化過程使問題得到解決。
  • 必定要有一個明確的結束遞迴的條件。

如果在遞迴過程中加入for迴圈,那麼隨著遞迴層數的變化,自然就形成了多層for迴圈巢狀的結構。for迴圈的層數完全由遞迴層數決定,或者由遞迴結束條件決定。

然後,我們解決問題二:

如何避免一個數字被多個位置重複選取呢?最直接的方法是,呼叫過之後,就刪除該資料。

由於在python中,形參和實參會相互影響。所以在使用之前,需要先copy資料,防止原資料被破壞。

解決完問題,我們看完整的程式碼:

import numpy as np

def fun(in_list,out_list):
    if len(in_list) == 0:
        print(out_list)
    else:
        for element in in_list:
            inner_in = in_list.copy()
            inner_out = out_list.copy()
            inner_in.remove(element)
            inner_out.append(element)
            fun(inner_in,inner_out)

def full_permutation(in_list):
    out_list = []
    fun(in_list,out_list)

a = np.arange(5).tolist()
full_permutation(a)

在fun()函式中,我們實現了遞迴操作,並添加了for迴圈語句。這樣,就能夠利用遞迴構建多層for迴圈。從而解決了問題一

程式最後給出了一個簡單的測試樣例,讀者可自己執行,從而驗證結果。

3.更優的方案

在第2節中,我們稱之為不太好的方案,原因是我們將列表大量copy,這樣防止了不同遞迴路徑的資料干擾,但卻需要佔用大量空間。如何在不復制列表的情況下,解決問題二呢?

方法當然是有的,引入輔助列表即可解決。引入一個列表,記錄資料是否已經被使用。

當該資料已被使用,則狀態為False,下一次取數的時候,就不會取到該資料。

import numpy as np

def fun(n,in_list,state,out_list):
    if (n == 0):
        print(out_list)
    else:
        for i in range(len(in_list)):
            if state[i]:
                out_list[n-1] = in_list[i]
                state[i] = False
                fun(n-1,in_list,state,out_list)
                state[i] = True

def full_permutation(in_list):
    n = len(in_list)
    state = [True for _ in range(n)]
    out_list = in_list.copy()
    fun(n,in_list,state,out_list)

a = np.arange(4).tolist()
full_permutation(a)

在這段程式碼中,我們使用了state[]記錄排列組合資料的狀態。

在遞迴過程中,資料使用後,狀態置為False;

本次遞迴結束後,資料狀態要恢復為True,不影響其他遞迴過程對資料的使用。

另外,為了方便書寫,排列組合取值從最後一位開始。