I'm converting a working JavaScript solution for for calculating the permutations of an array over to Python, and I wanted to know why the translation doesn't work exactly the same way. Specifically, it appears as though the memo list I'm using to keep track of values is continually appended and never cleared, but I'm unsure as to what differences in Python's underpinnings cause this effect. Here's my code in Python:
def perms(input):
result = []
def permute(arr, m):
if len(arr) == 0:
print('this is m appending!: ', m)
print('=============================')
result.append(m)
else:
for i in range(len(arr)):
curr = arr.copy()
next = curr.pop(i)
m.append(next)
print('curr: ', curr)
print('next: ', next)
print('m: ', m)
permute(curr, m)
print('running')
permute(input, [])
return result
answer = perms([1,2,3])
print(answer)
and here is the output:
running
curr: [2, 3]next: 1m: [1]
curr: [3]
next: 2
m: [1, 2]
curr: []
next: 3
m: [1, 2, 3]
this is m appending!: [1, 2, 3]
=============================
curr: [2]
next: 3
m: [1, 2, 3, 3]
curr: []
next: 2m: [1, 2, 3, 3, 2]this is m appending!: [1, 2, 3, 3, 2]
=============================
curr: [1, 3]
next: 2
m: [1, 2, 3, 3, 2, 2]
curr: [3]
next: 1
m: [1, 2, 3, 3, 2, 2, 1]
curr: []
next: 3
m: [1, 2, 3, 3, 2, 2, 1, 3]
this is m appending!: [1, 2, 3, 3, 2, 2, 1, 3]
=============================
curr: [1]
next: 3
m: [1, 2, 3, 3, 2, 2, 1, 3, 3]
curr: []
next: 1
m: [1, 2, 3, 3, 2, 2, 1, 3, 3, 1]
this is m appending!: [1, 2, 3, 3, 2, 2, 1, 3, 3, 1]
=============================
curr: [1, 2]
next: 3
m: [1, 2, 3, 3, 2, 2, 1, 3, 3, 1, 3]
curr: [2]
next: 1
m: [1, 2, 3, 3, 2, 2, 1, 3, 3, 1, 3, 1]
curr: []
next: 2m: [1, 2, 3, 3, 2, 2, 1, 3, 3, 1, 3, 1, 2]this is m appending!: [1, 2, 3, 3, 2, 2, 1, 3, 3, 1, 3, 1, 2]
=============================
curr: [1]
next: 2
m: [1, 2, 3, 3, 2, 2, 1, 3, 3, 1, 3, 1, 2, 2]
curr: []
next: 1
m: [1, 2, 3, 3, 2, 2, 1, 3, 3, 1, 3, 1, 2, 2, 1]
this is m appending!: [1, 2, 3, 3, 2, 2, 1, 3, 3, 1, 3, 1, 2, 2, 1]
=============================
[[1, 2, 3, 3, 2, 2, 1, 3, 3, 1, 3, 1, 2, 2, 1], [1, 2, 3, 3, 2, 2, 1, 3, 3, 1, 3, 1, 2, 2, 1], [1, 2, 3, 3, 2, 2, 1, 3, 3, 1, 3, 1, 2, 2, 1], [1, 2, 3, 3, 2, 2, 1, 3, 3, 1, 3, 1, 2, 2, 1], [1, 2, 3, 3, 2, 2, 1, 3, 3, 1, 3, 1, 2, 2, 1], [1, 2, 3, 3, 2, 2, 1, 3, 3, 1, 3, 1, 2, 2, 1]]
As you can see, the first result ([1,2,3]) is processed as intended (as you can see where I print 'this is m appending' to the results list).However, m fails to "back out" appended items when the recursion steps backwards to execute. I'm assuming this is because Python manages the stack differently, but I'm unsure. If it's helpful, I can also post the JavaScript code and results.
If someone could explain to me why exactly this code is failing, that would be great!
Olvin's comment was correct that the issue was caused by nonlocal scoping, and Ajay's comment was a good fix for the problem - basically backing out the changes to the list manually after recursion. Here's the now-functioning code:
def perms(input):
result = []
def permute(arr, m = []):
if len(arr) == 0:
result.append(m.copy())
else:
for i in range(len(arr)):
curr = arr.copy()
nextItem = curr.pop(i)
m.append(nextItem)
permute(curr, m)
m.pop()
print('running')
permute(input)
return result
answer = perms([1,2,3])
print(answer)
Related
I have the following list in python3:
a = [1, 1, 1, 2, 2, 3, 3, 3, 1, 1, 1, 2, 2, 2, 4, 4, 4, 3, 3, 3, 3]
I want to have something like this as an output:
b = [1, 2, 3, 1, 2, 4, 3]
How can I do that? I have tried a = set(a) and other methods for getting rid of duplicates. But they remove all duplicates.
If you are willing to use a module, you can use itertools.groupby:
from itertools import groupby
a = [1, 1, 1, 2, 2, 3, 3, 3, 1, 1, 1, 2, 2, 2, 4, 4, 4, 3, 3, 3, 3]
output = [k for k, _ in groupby(a)]
print(output) # [1, 2, 3, 1, 2, 4, 3]
This would work:
a = [1, 1, 1, 2, 2, 3, 3, 3, 1, 1, 1, 2, 2, 2, 4, 4, 4, 3, 3, 3, 3]
b = [a[0]]
for k in a:
if k!=b[-1]:
b.append(k)
print(b)
If you comfortable with C, you may like this style:
a = [1, 1, 1, 2, 2, 3, 3, 3, 1, 1, 1, 2, 2, 2, 4, 4, 4, 3, 3, 3, 3]
b=len(a)
i=0
last_one=None
while i<b:
if a[i]==last_one:
del a[i]
b-=1
continue
else:
last_one=a[i]
i+=1
print(a)
I am breaking my head around this small issue which I am sure can (and should) be solved recursively.
# split list in sublists based on length of first element.
list = [3, 1, 2, 3, 4, 1, 2, 3, 4]
#* #*
# *number of elements of the sublist
It is better shown than explained, the above should result to:
[[1, 2, 3], [1, 2, 3, 4]]
The lists I am processing always respect this logic, the first element is always the length of the following n elements.
EDIT:
Based on some of the suggestions, I simply added a yield to get it done lazily.
def split(ls):
"""
func that given a list extracts sub lists with the length indicated by the first element
[2, 1, 2, 3, 1, 2, 3, 4, 1, 2, 3, 4] => [[1, 2], [1, 2, 3], [1, 2, 3, 4]]
"""
res = []
while ls:
dim = ls[0]
yield ls[1:dim + 1]
ls = ls[dim + 1:]
return res
>>> list(split([2, 1, 2, 3, 1, 2, 3, 4, 1, 2, 3, 4]))
[[1, 2], [1, 2, 3], [1, 2, 3, 4]]
Simple slicing will do:
>>> a = [3, 1, 2, 3, 4, 1, 2, 3, 4]
>>> c = []
>>> while len(a) :
... b = a[0]
... c.append( a[1:b+1] )
... a = a[b+1:]
...
>>> c
[[1, 2, 3], [1, 2, 3, 4]]
Here is a way to achieve what you want:
numbers = [3, 1, 2, 3, 4, 1, 2, 3, 4]
result = []
def split_list(list_):
first = list_.pop(0)
result.append(list_[:first])
if len(list_[first:]) > 0:
split_list(list_[first:])
split_list(numbers)
print(result)
You can use itertools.islice here.
>>> from itertools import islice
>>> lst = [3, 1, 2, 3, 4, 1, 2, 3, 4]
>>> def chunks(lst):
... t=iter(lst)
... c=next(t,None)
... while c:
... yield list(islice(t,None,c))
... c=next(t,None)
...
>>> list(chunks(lst))
[[1, 2, 3], [1, 2, 3, 4]]
I've edited my answer as inspired by someone else's in this thread. This doesn't consume the original array and uses recursion instead of looping.
numbers = [3, 1, 2, 3, 4, 1, 2, 3, 4, 3,1,1,1,1]
def do_take(numbers: []):
result = []
to_take = numbers[0]
result.append(numbers[1:to_take + 1])
if len(numbers) > to_take:
result.append(do_take(numbers[to_take + 1:]))
return result
print(do_take(numbers))
print(numbers)
Results in the following output:
# python /tmp/breakup.py
[[1, 2, 3], [[1, 2, 3, 4], [[1, 1, 1], [[]]]]]
[3, 1, 2, 3, 4, 1, 2, 3, 4, 3, 1, 1, 1, 1]
So, I am given a list
a =[[[0, 0, 3, 3, 3, 3], [0, 0, 1, 3, 3, 3, 3]], [[0, 1]], [[2, 2, 2, 3, 3, 3, 3], [2, 2, 2, 3, 3, 3, 3], [2, 2, 2, 3, 3, 3, 3]], [[0, 0, 2, 2, 2, 3, 3, 3], [0, 0, 2, 2, 2, 3, 3, 3], [0, 0, 2, 2, 2, 3, 3, 3, 3], [0, 0, 2, 2, 2, 3, 3, 3, 3]]]
and a dictionary d.
d = {0:2,1:1,2:3,3:4}
For the output, I want a dictionary:
output = {0:[0,3],1;[1],2:[2,3],3:[0,2]}
This output is formed by passing through each sublist of a and checking the number of times each element appears in d.
Let's look at index 0 of a. Now we look at a[0][0]and
a[0][1] and since 0 appears twice in both and 3 appears 4 times (comparing it to d), [0,3] are added to index 0. Similarly, at index 1, 0 appears just once and is not added to the dictionary at index 1.
What I tried so far:
def example(a,d):
for i in range(len(a)):
count = 0
for j in range(len(a[i])):
if j in (a[i][j]):
count+=1
if count == d[i]:
print(i,j)
Edit: A version that work
from collections import Counter
a = [[[0, 0, 3, 3, 3, 3], [0, 0, 1, 3, 3, 3, 3]], [[0, 1]],
[[2, 2, 2, 3, 3, 3, 3], [2, 2, 2, 3, 3, 3, 3], [2, 2, 2, 3, 3, 3, 3]],
[[0, 0, 2, 2, 2, 3, 3, 3], [0, 0, 2, 2, 2, 3, 3, 3], [0, 0, 2, 2, 2, 3, 3, 3, 3], [0, 0, 2, 2, 2, 3, 3, 3, 3]]]
d = {0: 2, 1: 1, 2: 3, 3: 4}
output = {i: [] for i in range(len(a))}
for j, sublist in enumerate(a):
counts = [Counter(i) for i in sublist]
for k,v in d.items():
try:
if all(counts[i][k] == v for i in range(len(counts))):
output[j].append(k)
except: continue
print(output)
output:
{0: [0, 3], 1: [1], 2: [2, 3], 3: [0, 2]}
The try except block is merely for convenience, If you insist you can if your way around this by checking if a key is in all counters (which is a requirement for it to be add)
This question already has answers here:
permutations with unique values
(20 answers)
Closed 4 years ago.
I have a problem that I'm trying to solve with itertools product. It is a permutations problem, but the values are not unique, example:
list = [1,1,2,2,3,3]
#result should be
[1,2,1,2,3,3], [2,2,1,1,3,3], .....
I tried using set(itertools.permutations(list))which is the straightforward answer, but the processing time for it is too much for different values and long lists.
I tried also x = itertools.product(set(list),repeat=len(list)) then cleaning out x from the items that don't fulfill the original list value counts (i.e. the generated lists must have two 1s, two 2s, and two 3s), this solution is more quick but this answer throws MemoryError with large numbers because it tries storing the output in the memory then doing the processing on it.
I tried also looping through the product result (i.e for i in itertools.product(set(list),repeat=len(list)) and selecting which iterations to store in and which iterations to throw away) and this solution solves the memory error problem, but is almost as slow as the first one, in which the code could run for hours.
Does anyone have any suggestions how would be a more efficient way to solve this problem?
Here's how I might approach the problem. Take each unique starting value and then all the unique combinations from the values with the starting value removed. It avoids generating any duplicates but only stores the unique values used in each position so worst case an n element list is n-squared in memory.
import itertools
def unique_permutations(values):
if not values:
yield []
return
seen = set()
for idx, first in enumerate(values):
if first in seen:
continue
seen.add(first)
rest = values[:idx] + values[idx+1:]
for perm in unique_permutations(rest):
yield [first] + perm
values = [1, 1, 2, 2, 3, 3]
for perm in unique_permutations(values):
print(perm)
Output:
[1, 1, 2, 2, 3, 3]
[1, 1, 2, 3, 2, 3]
[1, 1, 2, 3, 3, 2]
[1, 1, 3, 2, 2, 3]
[1, 1, 3, 2, 3, 2]
[1, 1, 3, 3, 2, 2]
[1, 2, 1, 2, 3, 3]
[1, 2, 1, 3, 2, 3]
[1, 2, 1, 3, 3, 2]
[1, 2, 2, 1, 3, 3]
[1, 2, 2, 3, 1, 3]
[1, 2, 2, 3, 3, 1]
[1, 2, 3, 1, 2, 3]
[1, 2, 3, 1, 3, 2]
[1, 2, 3, 2, 1, 3]
[1, 2, 3, 2, 3, 1]
[1, 2, 3, 3, 1, 2]
[1, 2, 3, 3, 2, 1]
[1, 3, 1, 2, 2, 3]
[1, 3, 1, 2, 3, 2]
[1, 3, 1, 3, 2, 2]
[1, 3, 2, 1, 2, 3]
[1, 3, 2, 1, 3, 2]
[1, 3, 2, 2, 1, 3]
[1, 3, 2, 2, 3, 1]
[1, 3, 2, 3, 1, 2]
[1, 3, 2, 3, 2, 1]
[1, 3, 3, 1, 2, 2]
[1, 3, 3, 2, 1, 2]
[1, 3, 3, 2, 2, 1]
[2, 1, 1, 2, 3, 3]
[2, 1, 1, 3, 2, 3]
[2, 1, 1, 3, 3, 2]
[2, 1, 2, 1, 3, 3]
[2, 1, 2, 3, 1, 3]
[2, 1, 2, 3, 3, 1]
[2, 1, 3, 1, 2, 3]
[2, 1, 3, 1, 3, 2]
[2, 1, 3, 2, 1, 3]
[2, 1, 3, 2, 3, 1]
[2, 1, 3, 3, 1, 2]
[2, 1, 3, 3, 2, 1]
[2, 2, 1, 1, 3, 3]
[2, 2, 1, 3, 1, 3]
[2, 2, 1, 3, 3, 1]
[2, 2, 3, 1, 1, 3]
[2, 2, 3, 1, 3, 1]
[2, 2, 3, 3, 1, 1]
[2, 3, 1, 1, 2, 3]
[2, 3, 1, 1, 3, 2]
[2, 3, 1, 2, 1, 3]
[2, 3, 1, 2, 3, 1]
[2, 3, 1, 3, 1, 2]
[2, 3, 1, 3, 2, 1]
[2, 3, 2, 1, 1, 3]
[2, 3, 2, 1, 3, 1]
[2, 3, 2, 3, 1, 1]
[2, 3, 3, 1, 1, 2]
[2, 3, 3, 1, 2, 1]
[2, 3, 3, 2, 1, 1]
[3, 1, 1, 2, 2, 3]
[3, 1, 1, 2, 3, 2]
[3, 1, 1, 3, 2, 2]
[3, 1, 2, 1, 2, 3]
[3, 1, 2, 1, 3, 2]
[3, 1, 2, 2, 1, 3]
[3, 1, 2, 2, 3, 1]
[3, 1, 2, 3, 1, 2]
[3, 1, 2, 3, 2, 1]
[3, 1, 3, 1, 2, 2]
[3, 1, 3, 2, 1, 2]
[3, 1, 3, 2, 2, 1]
[3, 2, 1, 1, 2, 3]
[3, 2, 1, 1, 3, 2]
[3, 2, 1, 2, 1, 3]
[3, 2, 1, 2, 3, 1]
[3, 2, 1, 3, 1, 2]
[3, 2, 1, 3, 2, 1]
[3, 2, 2, 1, 1, 3]
[3, 2, 2, 1, 3, 1]
[3, 2, 2, 3, 1, 1]
[3, 2, 3, 1, 1, 2]
[3, 2, 3, 1, 2, 1]
[3, 2, 3, 2, 1, 1]
[3, 3, 1, 1, 2, 2]
[3, 3, 1, 2, 1, 2]
[3, 3, 1, 2, 2, 1]
[3, 3, 2, 1, 1, 2]
[3, 3, 2, 1, 2, 1]
[3, 3, 2, 2, 1, 1]
I know how to get ALL combinations of a list in Python with itertools, but what if I want to limit the amount of repeats?
So, if I have [1, 2, 3, 4, 5]
But I want to limit combinations to only 3 repeats of each item (with a fixed length of the final list, say 10):
[1, 1, 1, 2, 3, 3, 5, 5, 5, 4]
[1, 2, 3, 3, 3, 4, 5, 5, 4, 4]
[4, 4, 1, 1, 1, 5, 2, 2, 2, 3]
and so on. How do I do this?
This would work:
import random
L = [1, 2, 3, 4, 5]
L3 = L * 3
random.shuffle(L3)
L3[:10]
I don't know if there is a more elegant way but that seems to work:
from itertools import combinations_with_replacement
a = [1, 2, 3, 4, 5] # can contain letters as well
all_combinations = combinations_with_replacement(a, 10)
filtered_results = []
for current in all_combinations:
throw_away = False
for item in current:
if current.count(item) > 3:
throw_away = True
break
if not throw_away:
filtered_results.append( current )
for j in filtered_results:
print(j)
You could do it the following way: Use [1, 1, 1, 2, 2, 2, 3, 3, 3, 4, 4, 4, 5, 5, 5] and use itertools.combination(the list from above, 10). That would mean that each element occurs at most three times in a resulting list.
Example with smaller list:
Edited to ensure every combination occurs exactly once, the combinations occur in random order and, finally, every combination has a random order.
Also incorporated the idea of Mike Müller to just multiply the list by 3.
import itertools
import random
combinations = set()
for c in itertools.combinations(sorted([1, 2, 3] * 3), 5):
combinations.add(c)
shuffledCombinations = list(combinations)
random.shuffle(shuffledCombinations)
for combination in shuffledCombinations:
copiedCombination = list(combination)
random.shuffle(copiedCombination)
print copiedCombination
which will give:
[2, 1, 3, 2, 2]
[2, 2, 1, 1, 2]
[3, 3, 3, 1, 2]
[2, 2, 3, 2, 3]
[1, 2, 3, 1, 2]
[1, 1, 1, 2, 3]
[2, 1, 1, 1, 2]
[3, 3, 1, 1, 1]
[1, 3, 3, 3, 1]
[3, 2, 3, 2, 3]
[3, 2, 1, 2, 3]
[1, 1, 2, 3, 3]