Skip to main content
 首页 » 编程设计

python之理解排列

2025年05月04日15虾米姐

我正在阅读这篇文章,试图了解有关排列的更多信息:Finding all possible permutations of a given string in python

我卡在这段代码上了:

def permutations(string, step = 0): 
 
    # if we've gotten to the end, print the permutation 
    if step == len(string): 
        print "".join(string) 
 
    # everything to the right of step has not been swapped yet 
    for i in range(step, len(string)): 
 
        # copy the string (store as array) 
        string_copy = [character for character in string] 
 
        # swap the current index with the step 
        string_copy[step], string_copy[i] = string_copy[i], string_copy[step] 
 
        # recurse on the portion of the string that has not been swapped yet (now it's index will begin with step + 1) 
        permutations(string_copy, step + 1) 

我不明白我们将当前索引与步骤交换的行,这到底是做什么的?我知道我们向右移动一次 step 但是 string_copy[step], string_copy[i] = string_copy[i], string_copy[step] 是如何做到这一点的呢?我只是不知道发生了什么

请您参考如下方法:

a,b = b,a 是交换两个值的 Python 习语,也可以写成:

temp = a 
a = b 
b = temp 

在 for 循环内,在 permutations(...) 之上,添加这一行:

print "step", step, "index", i, string, "->", string_copy 

现在它将向您展示它运行时的内部工作原理:

>>> permutations("abc") 
 
step 0 index 0 abc -> ['a', 'b', 'c'] 
step 1 index 1 ['a', 'b', 'c'] -> ['a', 'b', 'c'] 
step 2 index 2 ['a', 'b', 'c'] -> ['a', 'b', 'c'] 
abc 
step 1 index 2 ['a', 'b', 'c'] -> ['a', 'c', 'b'] 
step 2 index 2 ['a', 'c', 'b'] -> ['a', 'c', 'b'] 
acb 
step 0 index 1 abc -> ['b', 'a', 'c'] 
step 1 index 1 ['b', 'a', 'c'] -> ['b', 'a', 'c'] 
step 2 index 2 ['b', 'a', 'c'] -> ['b', 'a', 'c'] 
bac 
step 1 index 2 ['b', 'a', 'c'] -> ['b', 'c', 'a'] 
step 2 index 2 ['b', 'c', 'a'] -> ['b', 'c', 'a'] 
bca 
step 0 index 2 abc -> ['c', 'b', 'a'] 
step 1 index 1 ['c', 'b', 'a'] -> ['c', 'b', 'a'] 
step 2 index 2 ['c', 'b', 'a'] -> ['c', 'b', 'a'] 
cba 
step 1 index 2 ['c', 'b', 'a'] -> ['c', 'a', 'b'] 
step 2 index 2 ['c', 'a', 'b'] -> ['c', 'a', 'b'] 
cab 
  1. 看看只要步骤和索引相同,一个字符就会与其自身交换,没有任何变化。所以有十五次调用生成六种排列。

我发现很难举出一个更清晰的例子。这个怎么样:

 # (step, index) 
 
 (0, 0) -- a 
    (1, 1) -- ab 
       (2, 2) -- abc        # reached the last character, print this! 
      <- 
    (1, 2) -- ac 
       (2, 2) -- acb        # reached the last character, print this! 
      <- 
   <- 
 (0, 1) -- b 
    (1, 1) -- ba 
       (2, 2) -- bac        # reached the last character, print this! 
      <- 
    (1, 2) -- bc 
       (2, 2) -- bca        # reached the last character, print this! 
      <- 
   <- 
 (0, 2) -- c 
    (1, 1) -- cb 
       (2, 2) -- cba        # reached the last character, print this! 
      <- 
    (1, 2) -- ca 
       (2, 2) -- cab        # reached the last character, print this! 
      <- 
   <- 
  • 这不仅显示了一个循环,还显示了一个层次结构,一棵树。右边的东西更深,它们在左边的东西下面。在他们下面。在他们里面。模式的第一部分是--a下的所有东西,第二部分是--b下的所有东西,第三部分是 -- c 下的所有内容。
  • 每次调用 permute() 都会将所有内容 -> 推到右边。
  • 每次 permute() 完成时,我们返回 <- 到左边。
    • 向右移动会增加步数。
    • 在同一缩进处向下移动会增加索引。
    • 向下和向右移动,同时增加 step 和 index。
  • 每次我们缩进 -> 我们从父事物中获取状态。 --a 下的所有内容都以--a 开头。 -- ab 下的所有内容都以-- ab 开头。
  • 缩进有 3 个左右位置,因为字符串有 3 个字符,4 个字符的字符串有另一个级别的缩进,因此有更多的行 - 用于更多的排列。
  • “步骤”数字与缩进的正确程度相匹配并非巧合。

并且,尝试回答您的评论问题:

  • for i in range(step, len(string)): 行从当前缩进/步进级别开始索引计数器。这就是它从 (1,1) 到 (2,2) 更深层次的原因。您可以看到,当我们从 (2,2) 返回时,我们选择了之前的状态 (1,1) 并在相同级别转到 (1,2),然后进入 (2, 2) 在更深的之下。

  • 我们从 (2,2) 回到 (0,0) 的原因是因为我们通过向右缩进尽可能多的次数到达字符串的末尾,直到我们已用完以“a”开头的排列。 'abc' 和 'acb' 然后我们回到开头,从 'b' 开始。 'bac' 和 'bca'。然后我们用完了,回到起点。

例如

 (0, 0) -- a 
    (1, 1) -- ab 
       (2, 2) -- abc    #STEP has reached the end of the string, move left 
 
    # We moved left, index was 1 so we've got more work to do 
    (1, 2) -- ac 
       (2, 2) -- acb    #STEP has reached the end of the string, move left 
 
    # We moved left, index was 2 so that has also reached the end of the string 
    # no more work to do inside the group (0,0) -- a, so move left again. 
  <-  
  # Now at this far left level, we can change the far left column from (0,0) to (0,1) 
  # and then repeat all of the above. 

在每个级别,都在发生相同的模式。更改此列/级别,并重复所有较低级别。这是一种自相似、递归、循环的模式。

我的示例代码中有 8 个版本的 print 语句,打印不同的事物组合以尝试显示正在发生的事情。我认为以上是最清楚的,但我鼓励您放入打印语句并重新运行它。在交换前后打印'string'和'string_copy',打印'string_copy[step:]'并打印""*(3*(step+1))打印缩进。

据我所知没有简单的解释。没有什么可以替代盯着代码,一点一点地一遍又一遍地研究它,直到它更有意义。