簡體   English   中英

更好地理解查找字符串排列的解決方案 - javascript

[英]understanding better a solution for finding permutations of a string - javascript

我試圖更好地理解遞歸和函數式編程,我認為這是一個很好的實踐示例,即使用遞歸和現代方法(如reduce,filter和map)創建字符串的排列。

我找到了這段漂亮的代碼

 const flatten = xs => xs.reduce((cum, next) => [...cum, ...next], []); const without = (xs, x) => xs.filter(y => y !== x); const permutations = xs => flatten(xs.map(x => xs.length < 2 ? [xs] : permutations(without(xs, x)).map(perm => [x, ...perm]) )); permutations([1,2,3]) // [[1, 2, 3], [1, 3, 2], [2, 1, 3], [2, 3, 1], [3, 1, 2], [3, 2, 1]] 

來自JavaScript的Permutations? 作者:MártonSári

為了添加一些控制台日志來調試它並理解它在幕后做了什么,我已經分隔了一點

 const flatten = xs => { console.log(`input for flatten(${xs})`); return xs.reduce((cum, next) => { let res = [...cum, ...next]; console.log(`output from flatten(): ${res}`); return res; }, []); } const without = (xs, x) => { console.log(`input for without(${xs},${x})`) let res = xs.filter(y => y !== x); console.log(`output from without: ${res}`); return res; } const permutations = xs => { console.log(`input for permutations(${xs})`); let res = flatten(xs.map(x => { if (xs.length < 2) { return [xs] } else { return permutations(without(xs, x)).map(perm => [x, ...perm]) } })); console.log(`output for permutations: ${res}`) return res; } permutations([1,2,3]) 

我想我對每種方法的作用都有足夠的了解,但我似乎無法概念化如何將它們組合起來創建[[1,2,3],[1,3,2],[2] ,1,3],[2,3,1],[3,1,2],[3,2,1]]

有人可以一步一步地告訴我發動機罩下發生了什么嗎?

要獲得所有權限,我們會執行以下操作:

我們從左到右取一個數組元素。

 xs.map(x => // 1

對於所有其他元素,我們遞歸地生成排列:

 permutations(without(xs, x)) // [[2, 3], [3, 2]]

對於每個排列,我們添加我們在開始時取回的值:

 .map(perm => [xs, ...perm]) // [[1, 2, 3], [1, 3, 2]]

現在,對所有數組元素重復,結果是:

 [
  // 1
  [[1, 2, 3], [1, 3, 2]],
  // 2
  [[2, 1, 3], [2, 3, 1]],
  // 3
  [[3, 1, 2], [3, 2, 1]]
]

現在我們只需要flatten(...)該數組以獲得所需的結果。

整個事情可以表示為遞歸調用樹:

 [1, 2, 3]
        - [2, 3] -> 
                   - [3] -> [1, 2, 3]
                   - [2] -> [1, 3, 2]
        - [1, 3] ->
                  - [1] -> [2, 3, 1]
                  - [3] -> [2, 1, 3]
        - [1, 2] -> 
                 - [1] -> [3, 2, 1]
                 - [2] -> [3, 1, 2]

為了添加一些控制台日志來調試它,我稍微划了一下

這當然有幫助。 但請記住,簡單的遞歸定義通常會導致復雜的執行跟蹤。

這實際上是遞歸可能如此有用的原因之一。 因為某些算法具有復雜的迭代,所以允許簡單的遞歸描述。 因此,理解遞歸算法的目標應該是在其定義中找出歸納(而不是迭代)推理。

讓我們忘記javascript並專注於算法。 讓我們看看我們可以得到集合A的元素的排列,我們將表示P(A)

注意:在原始算法中輸入是一個列表並不重要,因為原始順序根本不重要。 同樣,我們將返回一組列表而不是列表列表,這是無關緊要的,因為我們不關心計算解決方案的順序。

基本情況:

最簡單的情況是空集。 對於0個元素的排列,只有一個解決方案,該解決方案是空序列[] 所以,

P(A) = {[]}

遞歸案例:

為了使用遞歸,您想要描述如何從P(A')獲得P(A)大小小於A某些A'

注意:如果你這樣做,它就完成了。 在操作上,程序將通過對具有越來越小的參數的P連續調用來計算,直到它到達基本情況,然后它將從較短的結果中恢復較長的結果。

所以這里有一種方法來編寫一個具有n + 1個元素的A的特定排列。 您需要為每個位置連續選擇A一個元素:

 _   _ ... _ 
n+1  n     1

所以你為第一個選擇一個x ∈ A

 x   _ ... _ 
     n     1

然后你需要在P(A\\{x})選擇一個排列。

這告訴你一種構建大小為n所有排列的方法。 考慮的所有可能的選擇xA (作為第一元件使用),並且對於每個選擇放x中的每一溶液的前面P(A\\{x}) 最后,為每個x選擇找到所有解決方案的聯合。

讓我們使用點運算符來表示將x放在序列s前面,並使用菱形運算符來表示將x放在每個s ∈ S前面。 那是,

x⋅s = [x, s1, s2, ..., sn] 
x⟡S = {x⋅s : s ∈ S}

然后換一個非空A

P(A) = ⋃ {x⟡P(A\{x}) : x ∈ A} 

此表達式與案例庫一起為您提供集合A中元素的所有排列。

javascript代碼

要了解您顯示的代碼如何實現此算法,您需要考慮以下內容

  • 當你有0或1個元素時,該代碼通過編寫xs.length < 2考慮兩個基本情況。 我們也可以這樣做,這是無關緊要的。 您可以將2更改為1,它仍然可以工作。

  • 映射對應於我們的運算x⟡S = {x⋅s : s ∈ S}

  • 不對應P(A\\{x})

  • flatten對應於連接所有解決方案的

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM