0

I have a function that generates all permutations of a string. It prints out all the possible permutations just fine. But now I want a list of all such permutations.

I tried making the global list as well as tried passing it as a parameter, but post appending the permutation all the lists previously in the main list get changed to the list last appended. Please explain this behavior

def permutationNum(a,lower,upper,perm):
    if(lower==upper):
        print(a)
        print(perm)
        perm.append(a)
        # perm = perm.append(a)
    else:
        for i in range(lower,upper+1):
            a[lower],a[i] = a[i],a[lower]
            permutationNum(a,lower+1,upper, perm)
            a[lower],a[i] = a[i],a[lower]

listy = [1,2,3]
perm = []
permutationNum(listy, 0, len(listy)-1, perm) 
print(perm)
Output : [[1, 2, 3], [1, 2, 3], [1, 2, 3], [1, 2, 3], [1, 2, 3], [1, 2, 3]]
Expected Output : [[1, 2, 3], [1, 3, 2], [2, 1, 3], [2, 3, 1], [3, 2, 1], [3, 1, 2]]

UPDATE:
Turns out it was indeed deep copy problem after all. I just had a temp variable store a deep copy of a and appended that temp variable to the list. It all worked out.

  • Common. Without reading the code, I know it's not a copy, they're both referencing the same list. Let me see if I can find a dup. Probably this one https://stackoverflow.com/questions/2612802/how-to-clone-or-copy-a-list/2612815#2612815 – Kenny Ostrom Aug 10 '19 at 03:57
  • When you pass `listy` as a parameter to the function, you're not passing a copy of it; you're passing the actual list. When you mutate the list in the function, you're mutating the same object as in the main scope. – John Gordon Aug 10 '19 at 04:02
  • incidentally that's not the output of the code you posted – Kenny Ostrom Aug 10 '19 at 04:04

2 Answers2

0

In python, certain data types are copied when passed into functions as arguments, while others are referenced.

If an argument is copied, any changes to it inside the function will not affect the original variable that was passed in.

If an argument is referenced, any changes to it inside the function will affect the original.

Strings, Ints, Floats are copied, while objects and lists are referenced. This behaviour is also replicated when assigning one variable to another:

a = 5
b = a
b = 6
print(a)
>>> 5

a = [5]
b = a
b.append(6)
print(a)
>>> [5, 6]

If you want to copy a list and not just reference it, there's multiple ways you can achieve this:

Copy Module

import copy

a = [5]
b = copy.copy(a)
b.append(6)
print(a)
>>> [5]

Slicing

a = [5]
b = a[:]
b.append(6)
print(a)
>>> [5]

.copy()

a = [5]
b = a.copy()
b.append(6)
print(a)
>>> [5]

list()

a = [5]
b = list(a)
b.append(6)
print(a)
>>> [5]

So in your case, you would change the following line from:

permutationNum(a,lower+1,upper, perm) to

permutationNum(a[:],lower+1,upper, perm)

Jay Mody
  • 3,727
  • 1
  • 11
  • 27
  • You don't need to import `copy` module, there are many built-in ways, name a few `a[:]`, `a.copy()`, `list(a)`. – iBug Aug 10 '19 at 04:16
0

Change this line - to append new instance of list everytime

perm.append(list(a))

Another way to get permutations:

import itertools
def permutationNum(a):
for x in itertools.permutations(a):
    perm.append(list(x))

listy = [1,2,3]
perm = []
permutationNum(listy)
print(perm)

or

import itertools

def permutationNum(a):
    return [list(x) for x in itertools.permutations(a)]
listy = [1,2,3]
print(permutationNum(listy))
Himanshu
  • 666
  • 1
  • 8
  • 18