Python recursive function wont change list defined in main() - python

Here I wrote a recursive function to find the permutation of a list.
def backtrack(arr,tmp):
if len(tmp)==len(arr):
res.append(tmp)
#==print res here==
print(res)
else:
for i in range(len(arr)):
if arr[i] in tmp: continue
tmp.append(arr[i])
backtrack(arr,tmp)
tmp.pop()
if __name__ == '__main__':
points=[1,2,3]
res=[]
tmp=[]
backtrack(points,tmp)
print(res)
#code result
#[[1, 3, 2], [1, 3, 2]]
#[[2, 1, 3], [2, 1, 3], [2, 1, 3]]
#[[2, 3, 1], [2, 3, 1], [2, 3, 1], [2, 3, 1]]
#[[3, 1, 2], [3, 1, 2], [3, 1, 2], [3, 1, 2], [3, 1, 2]]
#[[3, 2, 1], [3, 2, 1], [3, 2, 1], [3, 2, 1], [3, 2, 1], [3, 2, 1]]
#[[], [], [], [], [], []]
I have no idea why the list 'res' defined in main() wont be updated when we pass it to a recursive function. Any suggestion about how to update 'res'?

When you do:
res.append(tmp)
you are appending to the global list res a reference to the list tmp. Any future changes to tmp will be visible through res as it just contains multiple references to the same tmp list.
Instead you probably want to append a copy of tmp:
res.append(list(tmp))
Make that change and your output is now:
[[1, 2, 3]]
[[1, 2, 3], [1, 3, 2]]
[[1, 2, 3], [1, 3, 2], [2, 1, 3]]
[[1, 2, 3], [1, 3, 2], [2, 1, 3], [2, 3, 1]]
[[1, 2, 3], [1, 3, 2], [2, 1, 3], [2, 3, 1], [3, 1, 2]]
[[1, 2, 3], [1, 3, 2], [2, 1, 3], [2, 3, 1], [3, 1, 2], [3, 2, 1]]
[[1, 2, 3], [1, 3, 2], [2, 1, 3], [2, 3, 1], [3, 1, 2], [3, 2, 1]]
Alternatively you could convert the tmp list into a tuple:
res.append(tuple(tmp))
If you do that the option is then identical to the output returned by itertools.permutations:
>>> import itertools
>>> list(itertools.permutations([1,2,3]))
[(1, 2, 3), (1, 3, 2), (2, 1, 3), (2, 3, 1), (3, 1, 2), (3, 2, 1)]

Related

How to combine lists in list accordingly. (without numpy)

I have list of lists of lists and need to combine the inner lists accordingly.
For example:
1. mylist=[[[1]], [[2]]]
2.
mylist= [[[1, 1, 1], [1, 1, 1], [1, 1, 1], [1, 1, 1]],
[[2, 2, 2], [2, 2, 2], [2, 2, 2], [2, 2, 2]],
[[3, 3, 3], [3, 3, 3], [3, 3, 3], [3, 3, 3]]]
(in short-[[[1]*3]*4, [[2]*3]*4, [[3]*3]*4])
Expected output-
[[[1, 2]]]
[[[1, 2, 3], [1, 2, 3], [1, 2, 3]],
[[1, 2, 3], [1, 2, 3], [1, 2, 3]],
[[1, 2, 3], [1, 2, 3], [1, 2, 3]],
[[1, 2, 3], [1, 2, 3], [1, 2, 3]]]
(in short-[[[1, 2, 3]]*3]*4)
This is what I have untill now-
def combine_channels(mylist):
elements = [[] for _ in range(len(mylist[0]))]
for l1 in mylist:
for idx, l2 in enumerate(l1):
elements[idx] += l2
return [elements]
The problem is that the output is (for input example 2)-
[[[1, 1, 1, 2, 2, 2, 3, 3, 3],
[1, 1, 1, 2, 2, 2, 3, 3, 3],
[1, 1, 1, 2, 2, 2, 3, 3, 3],
[1, 1, 1, 2, 2, 2, 3, 3, 3]]]
and not-
[[[1, 2, 3], [1, 2, 3], [1, 2, 3]],
[[1, 2, 3], [1, 2, 3], [1, 2, 3]],
[[1, 2, 3], [1, 2, 3], [1, 2, 3]],
[[1, 2, 3], [1, 2, 3], [1, 2, 3]]]
mylist = [[[1, 1, 1], [1, 1, 1], [1, 1, 1], [1, 1, 1]],
[[2, 2, 2], [2, 2, 2], [2, 2, 2], [2, 2, 2]],
[[3, 3, 3], [3, 3, 3], [3, 3, 3], [3, 3, 3]]]
def combine_channels(mylist):
def list_zip(_list):
return list(zip(*_list))
elements = []
for l in list_zip(mylist):
elements.append(list_zip(l))
return elements
combine_channels(mylist)

how to merge lists in list (without numpy)

This is what i have-
def merge_list(mylist)
list1 = []
one_len = len(mylist)
two_len = len(mylist[0][0])
for index in range(two_len):
combine_list = []
for index2 in range(one_len):
combine_list.extend([a[index] for a in mylist[
index2]])
list1.append(combine_list)
return list1
But i have a problem with the output-
for example:
input-
mylist=[[[1, 1, 1], [1, 1, 1], [1, 1, 1], [1, 1, 1]], [[2, 2, 2], [2, 2, 2], [2, 2, 2], [2, 2, 2]], [[3, 3, 3], [3, 3, 3], [3, 3, 3], [3, 3, 3]]]
in short-
[[[1]*3]*4, [[2]*3]*4, [[3]*3]*4]
the output is -
[[[1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3], [1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3], [1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3]]]
and not -
[[[1, 2, 3], [1, 2, 3], [1, 2, 3]], [[1, 2, 3], [1, 2, 3], [1, 2, 3]], [[1, 2, 3], [1, 2, 3], [1, 2, 3]], [[1, 2, 3], [1, 2, 3], [1, 2, 3]]]
in short-
[[[1, 2, 3]]*3]*4
I would be happy to solve the problem and advise how to shorten the code.
In order to fix your code, you just need to update the return statement like this:
return [list1]
Though there is still a hidden problem when your inner lists lengths are longer than one element. Check your outputs with sample inputs like [[[2,1], [3,1]]].
One further - more compact - solution may be obtained by concatenating the inner lists within a cycle, then recreate the number of dimensions you need, moving the complexity from O(n^2) to O(n).
mylist = [[[2,1]],[[3,1]]]
def merge_list(mylist):
l_out = []
for l in mylist:
l_out += l[0]
return [[l_out]]
EDIT: In case of more complex inputs, you can extract first all elements and eventually fix the dimensions. This will still bring O(n^2) complexity though:
def merge_list(lst):
elements = [[] for _ in range(len(mylist[0]))]
for l1 in mylist:
for idx, l2 in enumerate(l1):
elements[idx] += l2
return [elements]
merge_list(mylist)
Input:
[[[1], [1]], [[2], [2]], [[3], [3]]]
Output:
[[[1, 2, 3], [1, 2, 3]]]

Sorting a list of lists in a list

Say I have a list inside a list, that is contained inside a list. For example:
foo = [[[2, 2, 2], [1, 1, 1], [3, 3, 3]], [[2, 2, 2], [1, 1, 1], [3, 3, 3]]]
And I wanted to sort it in order:
foo = [[[1, 1, 1], [2, 2, 2], [3, 3, 3]], [[1, 1, 1], [2, 2, 2], [3, 3, 3]]]
I could use [sorted(i) for i in foo] to achieve this. However is there some way to sort this list without list comprehension (or creating a new list)?
The values inside the lists themselves will change but do not need to be sorted.
Everything I have tried just boils down to the same method as above.
If you want to avoid creating a new list, then just iterate over the lists and call .sort()
>>> foo = [[[2, 2, 2], [1, 1, 1], [3, 3, 3]], [[2, 2, 2], [1, 1, 1], [3, 3, 3]]]
for i in foo:
i.sort()
>>> foo
[[[1, 1, 1], [2, 2, 2], [3, 3, 3]], [[1, 1, 1], [2, 2, 2], [3, 3, 3]]]
If you don't mind creating a new list, how about using map function:
In [1]: foo = [[[2, 2, 2], [1, 1, 1], [3, 3, 3]], [[2, 2, 2], [1, 1, 1], [3, 3, 3]]]
In [2]: list(map(sorted, foo))
Out[2]: [[[1, 1, 1], [2, 2, 2], [3, 3, 3]], [[1, 1, 1], [2, 2, 2], [3, 3, 3]]]

python: assign and read out the values of a 2-d list [duplicate]

This question already has answers here:
List of lists changes reflected across sublists unexpectedly
(17 answers)
Closed 4 years ago.
Here is a snippet of python code:
a = [[0]*3]*4
for i in range(4):
for j in range(3):
a[i][j] = i+j
print(a[i][j])
print(a)
However, the outputs of the two prints are different.
The former one prints what I want. And the second prints all the same for the 4 sublists.
It seems the problem of shallow copying. I really don't understand how and why it happens.
Update:
After I have solved this, I found another problem:
a = [[0]*3]*4
for i in range(4):
a[i] = [i, 2*i, 3*i]
The result is also what I want. I'm once again confused about this.
Who can tell me the difference?
a = [[0]*3]*4
for i in range(4):
for j in range(3):
a[i][j] = i+j
print(a)
print(a[i][j])//show the execution at every step
print(a)
At each step the list with same column is updated with the same value.
output is
[[0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0]]
0
[[0, 1, 0], [0, 1, 0], [0, 1, 0], [0, 1, 0]]
1
[[0, 1, 2], [0, 1, 2], [0, 1, 2], [0, 1, 2]]
2
[[1, 1, 2], [1, 1, 2], [1, 1, 2], [1, 1, 2]]
1
[[1, 2, 2], [1, 2, 2], [1, 2, 2], [1, 2, 2]]
2
[[1, 2, 3], [1, 2, 3], [1, 2, 3], [1, 2, 3]]
3
[[2, 2, 3], [2, 2, 3], [2, 2, 3], [2, 2, 3]]
2
[[2, 3, 3], [2, 3, 3], [2, 3, 3], [2, 3, 3]]
3
[[2, 3, 4], [2, 3, 4], [2, 3, 4], [2, 3, 4]]
4
[[3, 3, 4], [3, 3, 4], [3, 3, 4], [3, 3, 4]]
3
[[3, 4, 4], [3, 4, 4], [3, 4, 4], [3, 4, 4]]
4
[[3, 4, 5], [3, 4, 5], [3, 4, 5], [3, 4, 5]]
5
[[3, 4, 5], [3, 4, 5], [3, 4, 5], [3, 4, 5]]
The multiplier of operator taking a list and an int will make makes shallow copies of the elements of the list, so you're on the right track.
Initialise like this instead
a = [[0] * 3 for _ in range(4)]

counting frequency in list

I have a list of lists:
countall = [[5, 0], [4, 1], [4, 1], [3, 2], [4, 1], [3, 2], [3, 2], [2, 3], [4, 1], [3, 2], [3, 2], [2, 3], [3, 2], [2, 3], [2, 3], [1, 4], [4, 1], [3, 2], [3, 2], [2, 3], [3, 2], [2, 3], [2, 3], [1, 4], [3, 2], [2, 3], [2, 3], [1, 4], [2, 3], [1, 4], [1, 4], [0, 5]]
I would like to find the frequency of sub-lists in the above list.
I have tried to use itertools:
freq = [len(list(group)) for x in countall for key, group in groupby(x)]
However, I am getting the wrong results:
[1, 2, 1, 1, 2, 1, 1, 2, 1, 1, 2, 1, 1, 2, 1, 1, 2, 1, 1, 2, 1, 1, 2, 1]
What is wrong with my list comprehension?
Groupby seems to deal with sequences that come after each other. To use it you would need to sort the list first. Another option is to use the Counter class:
from collections import Counter
countall = [[5, 0], [4, 1], [4, 1], [3, 2], [4, 1], [3, 2], [3, 2], [2, 3], [4, 1], [3, 2], [3, 2], [2, 3], [3, 2], [2, 3], [2, 3], [1, 4], [4, 1], [3, 2], [3, 2], [2, 3], [3, 2], [2, 3], [2, 3], [1, 4], [3, 2], [2, 3], [2, 3], [1, 4], [2, 3], [1, 4], [1, 4], [0, 5]]
Counter([tuple(x) for x in countall])
Output:
Counter({(3, 2): 10, (2, 3): 10, (1, 4): 5, (4, 1): 5, (5, 0): 1, (0, 5): 1})
as pointed by ForceBru first sort your list then use groupby:
from itertools import groupby
countall = [[5, 0], [4, 1], [4, 1], [3, 2], [4, 1], [3, 2], [3, 2], [2, 3], [4, 1], [3, 2], [3, 2], [2, 3], [3, 2], [2, 3], [2, 3], [1, 4], [4, 1], [3, 2], [3, 2], [2, 3], [3, 2], [2, 3], [2, 3], [1, 4], [3, 2], [2, 3], [2, 3], [1, 4], [2, 3], [1, 4], [1, 4], [0, 5]]
freq = [(key, len(list(x))) for key, x in groupby(sorted(countall))]
print(freq)
output:
[([0, 5], 1), ([1, 4], 5), ([2, 3], 10), ([3, 2], 10), ([4, 1], 5), ([5, 0], 1)]
your code has bugs:
freq = [len(list(group)) for x in countall for key, group in groupby(x)]
^paranthesis missing
Then you are grouping each individual list in countall which is not needed.
for x in countall for key, group in groupby(x)
yo can directly groupby on sorted(countall)
Also, as answered by #Bemmu you can use collections.Counter. But that does not support list so first you will have to convert your data to tupple or string then use Counter
As noted in comments you will need to sort if you are using groupby.
Code:
import itertools as it
freq = {tuple(key): len(list(group)) for key, group in it.groupby(sorted(countall))}
Test Code:
countall = [[5, 0], [4, 1], [4, 1], [3, 2], [4, 1], [3, 2], [3, 2], [2, 3],
[4, 1], [3, 2], [3, 2], [2, 3], [3, 2], [2, 3], [2, 3], [1, 4],
[4, 1], [3, 2], [3, 2], [2, 3], [3, 2], [2, 3], [2, 3], [1, 4],
[3, 2], [2, 3], [2, 3], [1, 4], [2, 3], [1, 4], [1, 4], [0, 5]]
print(freq)
Results:
{(3, 2): 10, (1, 4): 5, (2, 3): 10, (5, 0): 1, (0, 5): 1, (4, 1): 5}

Categories

Resources