Permutation program issue, has something to do with global variables (Python) - python

this is an algorithm that is meant to write down some of the permutations of the list P and it does it well, but...
def p():
global P
P = ['a', 'b', 'c', 'd']
perm(4)
per = []
def perm(k):
global P
if k==1:
print(P)
per.append(P)
else:
for i in range(k):
P[i], P[k-1] = P[k-1], P[i]
perm(k-1)
P[i], P[k-1] = P[k-1], P[i]
when i want it to add the permutations to a global list (necessary for the rest of the program) there is a problem. It still prints all the permutations
['b', 'c', 'd', 'a']
['b', 'c', 'd', 'a']
['d', 'b', 'c', 'a']
['d', 'b', 'c', 'a']
['b', 'd', 'c', 'a']
['b', 'd', 'c', 'a']
['a', 'c', 'b', 'd']
['a', 'c', 'b', 'd']
['b', 'a', 'c', 'd']
['b', 'a', 'c', 'd']
['a', 'b', 'c', 'd']
['a', 'b', 'c', 'd']
['b', 'd', 'a', 'c']
['b', 'd', 'a', 'c']
['a', 'b', 'd', 'c']
['a', 'b', 'd', 'c']
['b', 'a', 'd', 'c']
['b', 'a', 'd', 'c']
['a', 'd', 'b', 'c']
['a', 'd', 'b', 'c']
['b', 'a', 'd', 'c']
['b', 'a', 'd', 'c']
['a', 'b', 'd', 'c']
['a', 'b', 'd', 'c']
but when i check the list it's filled with the default set
[['a', 'b', 'd', 'c'], ['a', 'b', 'd', 'c'], ['a', 'b', 'd', 'c'], ['a', 'b',
'd', 'c'], ['a', 'b', 'd', 'c'], ['a', 'b', 'd', 'c'], ['a', 'b', 'd', 'c'],
['a', 'b', 'd', 'c'], ['a', 'b', 'd', 'c'], ['a', 'b', 'd', 'c'], ['a', 'b',
'd', 'c'], ['a', 'b', 'd', 'c'], ['a', 'b', 'd', 'c'], ['a', 'b', 'd', 'c'],
['a', 'b', 'd', 'c'], ['a', 'b', 'd', 'c'], ['a', 'b', 'd', 'c'], ['a', 'b',
'd', 'c'], ['a', 'b', 'd', 'c'], ['a', 'b', 'd', 'c'], ['a', 'b', 'd', 'c'],
['a', 'b', 'd', 'c'], ['a', 'b', 'd', 'c'], ['a', 'b', 'd', 'c']]
could you please help me at least with what's the actual issue?

Try using built-in itertools link .
import itertools
P = [letter for letter in "abcd"]
def perm(permutate_this):
return list(itertools.permutations(permutate_this))
print(perm(P))

Use
per.append(P[:])
to copy the list. You are appending a reference to a list and thats always the same data. Your per contains the same reference over and over.
Comment-remark of cdarke:
Slicing a list is calles a shallow copy - if you have lists that contain other refs (f.e. inner lists) it will only copy the ref and you have the same problem for the inner lists - you would have to resort to copy.deepcopy in that case.
Example:
innerlist = [1,2,3]
l2 = [innerlist, 5, 6]
l3 = l2[:]
print(l2) # orig
print(l3) # the shallow copy
l3[2] = "changed" # l2[2] is unchanged
print(l2)
print(l3)
innerlist[2] = 999 # both (l2 and l3) will reflect this change in the innerlist
print(l2)
print(l3)
Output:
[[1, 2, 3], 5, 6] # l2
[[1, 2, 3], 5, 6] # l3
[[1, 2, 3], 5, 6] # l2 unchanged by l3[2]='changed'
[[1, 2, 3], 5, 'changed'] # l3 changed by -"-
[[1, 2, 999], 5, 6] # l2 and l3 affected by change in innerlist
[[1, 2, 999], 5, 'changed']

It may be more convenient for you to use built-in permutations:
from itertools import permutations
arr = [1, 2, 3]
list(permutations(arr))
> [(1, 2, 3), (1, 3, 2), (2, 1, 3), (2, 3, 1), (3, 1, 2), (3, 2, 1)]

Related

Generate All Replacements for List of Lists

I'm building an application in Python where I need to define the following sort of function:
generate_replacements(['a', 'b', ['c', ['e', 'f']]], 1)
The expected output is all possible versions of the input list where just one element has been replaced
[
[1, 'b', ['c', ['e', 'f']]],
['a', 1, ['c', ['e', 'f']]],
['a', 'b', 1],
['a', 'b', [1, ['e', 'f']]],
['a', 'b', ['c', 1]],
['a', 'b', ['c', [1, 'f']]],
['a', 'b', ['c', ['e', 1]]]
]
I can see that recursion is the way to go, but I'm really stuck figuring out how to even best start this.
You can generate the replacements from the list, then if you notice you are replacing a list, also pass that list back through the function recursively. This is made a bit simpler if you use a generator:
def generate_replacements(l, rep):
for i in range(len(l)):
yield l[0:i] + [rep] + l[i+1: ]
if isinstance(l[i], list):
yield from (l[0:i] + [rec] + l[i+1: ]
for rec in generate_replacements(l[i], rep))
list(generate_replacements(['a', 'b', ['c', ['e', 'f']]], 1))
This give:
[[1, 'b', ['c', ['e', 'f']]],
['a', 1, ['c', ['e', 'f']]],
['a', 'b', 1],
['a', 'b', [1, ['e', 'f']]],
['a', 'b', ['c', 1]],
['a', 'b', ['c', [1, 'f']]],
['a', 'b', ['c', ['e', 1]]]]

How do you split a string into different sections inside a list?

For example, I have a list:
[['aabbbb'], ['bbbbab'], ['babbab'], ['baaaaa'], ['bbbaaa'], ['bbbbaa']]
how do I split it so that I get [['a', 'a', 'b', 'b', 'b', 'b'],... etc? It would be very useful thanks!
You can use list comprehension.
mylist = [['aabbbb'], ['bbbbab'], ['babbab'], ['baaaaa'], ['bbbaaa'], ['bbbbaa']]
new_list = [list(item[0]) for item in mylist]
This will return,
[['a', 'a', 'b', 'b', 'b', 'b'], ['b', 'b', 'b', 'b', 'a', 'b'], ['b', 'a', 'b', 'b', 'a', 'b'], ['b', 'a', 'a', 'a', 'a', 'a'], ['b', 'b', 'b', 'a', 'a', 'a'], ['b', 'b', 'b', 'b', 'a', 'a']]
Try the code below
oldList = [['aabbbb'], ['bbbbab'], ['babbab'], ['baaaaa'], ['bbbaaa'], ['bbbbaa']]
newList = []
for i in oldList:
newList.append(list(i[0]))

How do I make independent lists within in a list?

seats = 4 # user can choose an even input, I put 4 for this example
rows = 4 # user can choose an even or odd input, I put 4 for this example
seats_in_row_list = [i for i in string.ascii_uppercase[:seats]]
main_seat_list = [seats_in_row_list for i in range(rows)]
The output is:
[['A', 'B', 'C', 'D'], ['A', 'B', 'C', 'D'], ['A', 'B', 'C', 'D'], ['A', 'B', 'C', 'D']]
But when I try to change 'A' to 'X' in the first list all of the lists change:
[['X', 'B', 'C', 'D'], ['X', 'B', 'C', 'D'], ['X', 'B', 'C', 'D'], ['X', 'B', 'C', 'D']]
What I'm looking for is this output:
[['X', 'B', 'C', 'D'], ['A', 'B', 'C', 'D'], ['A', 'B', 'C', 'D'], ['A', 'B', 'C', 'D']]
Use copy method to have a copy of the individual list before assigning
main_seat_list = [seats_in_row_list.copy() for i in range(rows)]
If you aren't using seats_in_row_list for anything other than the construction of main_seat_list, you should just inline the definition. Calling list here would be simpler than using a list comprehension.
seats = 4
rows = 4
main_seat_list = [list(string.ascii_uppercase[:seats]) for i in range(rows)]

Product of lists of lists with variable members

Say I have this input data
my_input_list = [[A],[A,B,C],[D],[D,E,F],[A,B,C,D,E,F],[A,C,E]]
items_that_appear_twice = [A,B,C]
items_that_appear_four = [D,E,F]
And I want to create an expansion such that some elements are only allowed to appear twice.
my_output_list = [
[A],[A],
[A,B,C],[A,B,C],
[D],[D],[D],[D],
[D,E,F],[D,E,F],[D,E,F],[D,E,F],
[A,B,C,D,E,F],[A,B,C,D,E,F],[D,E,F],[D,E,F],
[A,C,E],[A,C,E],[E],[E]]
I tired a few ideas and didn't find a really neat solution, like building lists of four and list.remove() from them which generated two empty lists.
For example list removal techniques on my_input_list[0]*4 gives [A],[A],[],[] (two empty lists) when I want [A],[A] .
I have a working version: See Pyfiddle.
my_input_list = [['A'],['A','B','C'],['D'],['D','E','F'],['A','B','C','D','E','F'],['A','C','E']]
items_that_appear_twice = ['A','B','C']
items_that_appear_four = ['D','E','F']
my_output_list = []
for my_input in my_input_list:
items_allowed_to_appear_twice = list(filter(
lambda value: (value in items_that_appear_twice
or value in items_that_appear_four),
my_input))
items_allowed_to_appear_four = list(filter(
lambda value: value in items_that_appear_four,
my_input))
my_output_list += 2*[items_allowed_to_appear_twice]
if len(items_allowed_to_appear_four):
my_output_list += 2*[items_allowed_to_appear_four]
print(my_output_list)
my_input_list = [['A'],
['A', 'B', 'C'],
['D'],
['D', 'E', 'F'],
['A', 'B', 'C', 'D', 'E', 'F'],
['A', 'C', 'E']]
items_that_appear_twice = ['A', 'B', 'C']
items_that_appear_four = ['D', 'E', 'F']
my_output_list = []
for sub in my_input_list:
my_output_list.append(sub)
my_output_list.append(sub)
sub = [x for x in sub if x in items_that_appear_four]
if sub:
my_output_list.append(sub)
my_output_list.append(sub)
assert my_output_list == [
['A'], ['A'],
['A', 'B', 'C'], ['A', 'B', 'C'],
['D'], ['D'], ['D'], ['D'],
['D', 'E', 'F'], ['D', 'E', 'F'], ['D', 'E', 'F'], ['D', 'E', 'F'],
['A', 'B', 'C', 'D', 'E', 'F'], ['A', 'B', 'C', 'D', 'E', 'F'], ['D', 'E', 'F'], ['D', 'E', 'F'],
['A', 'C', 'E'], ['A', 'C', 'E'], ['E'], ['E']]
Below is my solution, sort the sub list first, then append to the result according different situation.
my_input_list = [['A'],['A','B','C'],['D'],['D','E','F'],['A','B','C','D','E','F'],['A','C','E']]
items_that_appear_twice = ['A','B','C']
items_that_appear_four = ['D','E','F']
result = []
for sublist in my_input_list:
appearence = {1:[], 2:[], 4:[]}
for item in sublist:
appearence[2].append(item) if item in items_that_appear_twice else (appearence[4].append(item) if item in items_that_appear_four else appearence[1].append(item))
if len(appearence[2]) > 0:
result.append([appearence[2] + appearence[4]] * 2 + ([appearence[4]] * 2 if appearence[4] and len(appearence[4]) > 0 else []))
else:
result.append([appearence[4]] * 4)
for item in result:
print(item)
output:
[['A'], ['A']]
[['A', 'B', 'C'], ['A', 'B', 'C']]
[['D'], ['D'], ['D'], ['D']]
[['D', 'E', 'F'], ['D', 'E', 'F'], ['D', 'E', 'F'], ['D', 'E', 'F']]
[['A', 'B', 'C', 'D', 'E', 'F'], ['A', 'B', 'C', 'D', 'E', 'F'], ['D', 'E', 'F'], ['D', 'E', 'F']]
[['A', 'C', 'E'], ['A', 'C', 'E'], ['E'], ['E']]
[Finished in 0.178s]

Multiplying strings in a list by numbers from another list, element by element

I have two lists, ['A', 'B', 'C', 'D'] and [1, 2, 3, 4]. Both lists will always have the same number of items. I need to multiply each string by its number, so the final product I am looking for is:
['A', 'B', 'B', 'C', 'C', 'C', 'D', 'D', 'D', 'D']
Nested list comprehension works too:
>>> l1 = ['A', 'B', 'C', 'D']
>>> l2 = [1, 2, 3, 4]
>>> [c for c, i in zip(l1, l2) for _ in range(i)]
['A', 'B', 'B', 'C', 'C', 'C', 'D', 'D', 'D', 'D']
In above zip returns (char, count) tuples:
>>> t = list(zip(l1, l2))
>>> t
[('A', 1), ('B', 2), ('C', 3), ('D', 4)]
Then for every tuple the second for loop is executed count times to add the character to the result:
>>> [char for char, count in t for _ in range(count)]
['A', 'B', 'B', 'C', 'C', 'C', 'D', 'D', 'D', 'D']
I would use itertools.repeat for a nice, efficient implementation:
>>> letters = ['A', 'B', 'C', 'D']
>>> numbers = [1, 2, 3, 4]
>>> import itertools
>>> result = []
>>> for letter, number in zip(letters, numbers):
... result.extend(itertools.repeat(letter, number))
...
>>> result
['A', 'B', 'B', 'C', 'C', 'C', 'D', 'D', 'D', 'D']
>>>
I also think it is quite readable.
The code is pretty straight forward, see inline comments
l1 = ['A', 'B', 'C', 'D']
l2 = [1, 2, 3, 4]
res = []
for i, x in enumerate(l1): # by enumerating you get both the item and its index
res += x * l2[i] # add the next item to the result list
print res
OUTPUT
['A', 'B', 'B', 'C', 'C', 'C', 'D', 'D', 'D', 'D']
You use zip() to do it like this way:
a = ['A', 'B', 'C', 'D']
b = [1, 2, 3, 4]
final = []
for k,v in zip(a,b):
final += [k for _ in range(v)]
print(final)
Output:
>>> ['A', 'B', 'B', 'C', 'C', 'C', 'D', 'D', 'D', 'D']
Or you can do it, too, using zip() and list comprehension:
a = ['A', 'B', 'C', 'D']
b = [1, 2, 3, 4]
final = [k for k,v in zip(a,b) for _ in range(v)]
print(final)
Output:
>>> ['A', 'B', 'B', 'C', 'C', 'C', 'D', 'D', 'D', 'D']
You can use NumPy and then convert the NumPy array to a list:
letters = ['A', 'B', 'C', 'D']
times = [1, 2, 3, 4]
np.repeat(letters, times).tolist()
#output
['A', 'B', 'B', 'C', 'C', 'C', 'D', 'D', 'D', 'D']

Categories