了解排列

时间:2014-03-27 04:12:52

标签: python permutation

我正在阅读这篇文章,试图了解有关排列的更多信息: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]如何帮助它?我不知道发生了什么

2 个答案:

答案 0 :(得分:5)

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

temp = a
a = b
b = temp

在for循环中,在排列(...)之上,添加以下行:

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. 每当步骤和索引相同时,就会看到一个字符与自身交换,没有任何变化。所以有十五个调用来产生六个排列。
  2. 我发现很难做出一个更加清晰的例子。怎么样:

     # (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()都会推送所有内容 - &gt;在右边。
    • 每次permute()结束,我们返回&lt; - 向左。
      • 向右移动会增加步骤。
      • 向下移动相同的缩进会增加索引。
      • 向下和向右移动,一起增加步骤和索引。
    • 每次我们缩进 - &gt;我们从父母的事情中取得状态。 --a下的所有内容均以-- a开头。 -- ab下的所有内容均以-- ab开头。
    • 缩进有3个左右位置,因为字符串有3个字符,4个字符的字符串得到另一个缩进级别,因此有更多的行 - 用于更多的排列。
    • 'step'数字与缩进的正确程度相匹配并非巧合。

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

    • 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'。然后我们就跑了,回到开始。

    e.g。

     (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语句,打印不同的事物组合以尝试显示正在发生的事情。我认为上面的内容是最明确的,但我鼓励你将print语句放入并重新运行。在交换之前和之后打印'string'和'string_copy',打印'string_copy [step:]'并打印" "*(3*(step+1))以打印缩进。

    我所知道的并不容易解释。没有什么可以替代盯着代码,一遍又一遍地处理它直到它更有意义。

答案 1 :(得分:1)

这是一段很好的代码。交换是在这里创建不同排列的步骤。 我试着用另外两个打印语句来运行它,如下所示:

def permutations(string, step = 0):
    print string ############ADDED
    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)):
        string_copy = [character for character in string]
        string_copy[step], string_copy[i] = string_copy[i], string_copy[step]
        print step, i, string_copy ##############ADDED
        permutations(string_copy, step + 1)

permutations("abc")

运行结果如下:

  • ABC
  • 0 0 ['a','b','c']
  • ['a','b','c']
  • 1 1 ['a','b','c']
  • ['a','b','c']
  • 2 2 ['a','b','c']
  • ['a','b','c']
  • ABC
  • 1 2 ['a','c','b']
  • ['a','c','b']
  • 2 2 ['a','c','b']
  • ['a','c','b']
  • ACB
  • 0 1 ['b','a','c']
  • ['b','a','c']
  • 1 1 ['b','a','c']
  • ['b','a','c']
  • 2 2 ['b','a','c']
  • ['b','a','c']
  • BAC
  • 1 2 ['b','c','a']
  • ['b','c','a']
  • 2 2 ['b','c','a']
  • ['b','c','a']
  • BCA
  • 0 2 ['c','b','a']
  • ['c','b','a']
  • 1 1 ['c','b','a']
  • ['c','b','a']
  • 2 2 ['c','b','a']
  • ['c','b','a']
  • CBA
  • 1 2 ['c','a','b']
  • ['c','a','b']
  • 2 2 ['c','a','b']
  • ['c','a','b']
  • 驾驶室

如果您观察到第一次传递返回'abc'本身作为步骤匹配我使交换琐碎。 然后交换b和c来创建acb,依此类推。