繁体   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