1

I am trying to get my head around how this permutation algorithm works:

def perm(n, i):
    if i == len(n) - 1: 
        print n
    else:
        for j in range(i, len(n)):
            n[i], n[j] = n[j], n[i]
            perm(n, i + 1)
            n[i], n[j] = n[j], n[i] # swap back, for the next loop

perm([1, 2, 3], 0)

Output:

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

Question

How is it that the original list is the first line printed?

In this example, the length of n is 3. Initially, i is 0. The code should skip the if statement, and then first iteration mutates the list. How do we get [1, 2, 3] as the first line of output?

3
  • 1
    I'd suggest using a debugger or running it through pythontutor.com Commented May 18, 2017 at 21:43
  • Are you sure it's not printing in the else? Commented May 18, 2017 at 21:44
  • Take a closer look at exactly what it's swapping on the first iteration. Are you sure that iteration really changes the list? Commented May 18, 2017 at 21:45

1 Answer 1

3

It does skip the if at the top level. It drops into the else and iterates j through the list. The first iteration has i == j == 0, so the swap does nothing, and you recur with ([1, 2, 3], 1).

This process repeats for the that instance, having i == j == 1. That recurs with ([1, 2, 3], 2) That instance is the one that print [1, 2, 3] as the first line of output.

Does that clear it up?

If not, learn how to insert useful print statements to trace execution. Perhaps this makes it more clear.

indent = ""

def perm(n, i):
    global indent
    indent += "  "
    print indent, "ENTER", n, i

    if i == len(n) - 1: 
        print n
    else:
        for j in range(i, len(n)):
            print indent, "RECUR", i, j
            n[i], n[j] = n[j], n[i]
            perm(n, i + 1)
            n[i], n[j] = n[j], n[i] # swap back, for the next loop

    indent = indent[2:]

perm([1, 2, 3], 0)

Output:

   ENTER [1, 2, 3] 0
   RECUR 0 0
     ENTER [1, 2, 3] 1
     RECUR 1 1
       ENTER [1, 2, 3] 2
[1, 2, 3]
     RECUR 1 2
       ENTER [1, 3, 2] 2
[1, 3, 2]
   RECUR 0 1
     ENTER [2, 1, 3] 1
     RECUR 1 1
       ENTER [2, 1, 3] 2
[2, 1, 3]
     RECUR 1 2
       ENTER [2, 3, 1] 2
[2, 3, 1]
   RECUR 0 2
     ENTER [3, 2, 1] 1
     RECUR 1 1
       ENTER [3, 2, 1] 2
[3, 2, 1]
     RECUR 1 2
       ENTER [3, 1, 2] 2
[3, 1, 2]
Sign up to request clarification or add additional context in comments.

3 Comments

algorithms are amazing
I like this indent approach to debugging a recursive function, will have to add that to my bag of tricks
@juanpa.arrivillaga: I know that there's an indentation package somewhere that can do this and more, but I keep losing the reference. I'm not even sure it's Python; might be C++ or Java. In any case, I've been doing this "pretty-print" since I got into advanced programming back in the days before object-oriented languages matured.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.