Python non-unique permutations processing [duplicate] - python

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]

Related

How can I select a specific number in a list with duplicates without removing the previous same values?

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)

Permutations of a list in Python - converting JS solution

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)

Generate the initial game board of a Candy-Crush-like game

I need to implement a function,
which returns a 6 by 6 matrix that fulfills the following requirements:
The 36 numbers on the board must be 9 ones, 9 twos, 9 threes and 9 fours
Any row or column must not contain 3 or more direct neighbours that are the same number
The function return value must not be a constant
Obviously it’s not allowed to use pre-calculated answers
correct answer:
[[3, 2, 4, 1, 3, 2],
[2, 2, 1, 1, 4, 4],
[4, 4, 1, 3, 3, 2],
[4, 1, 3, 2, 2, 4],
[3, 1, 2, 4, 3, 1],
[3, 3, 1, 1, 2, 4]]
[[3, 3, 1, 2, 2, 4],
[1, 1, 3, 3, 2, 4],
[4, 4, 2, 1, 1, 3],
[2, 2, 3, 4, 4, 1],
[4, 4, 1, 1, 2, 2],
[3, 1, 2, 3, 3, 4]]
wrong answer:
[[3, 3, 3, 2, 2, 4],
[1, 1, 1, 3, 2, 4],
[4, 4, 2, 1, 1, 3],
[2, 2, 3, 4, 4, 1],
[4, 4, 1, 1, 2, 2],
[3, 1, 2, 3, 3, 4]]
[[3, 3, 1, 2, 2, 4],
[1, 1, 2, 3, 2, 4],
[4, 4, 1, 1, 2, 3],
[2, 2, 3, 4, 4, 1],
[4, 4, 1, 1, 2, 2],
[3, 1, 2, 3, 3, 4]]
Don’t need to worry too much about the academic time/space complexity. Focus more on the engineering point of view. Is there any good idea?
This should work. Note that this solution just generates a random board, checks if the conditions hold, and if not, generates another, so is not the most elegant solution.
Code:
from random import shuffle
def check_board(board):
for row in board:
if check_list(row):
return False
for i in range(len(board[0])):
col = [row[i] for row in board]
if check_list(col):
return False
return True
def check_list(lst):
return any(lst[i]==lst[i+1] and lst[i]==lst[i+2] for i in range(len(lst)-2))
board = [[]]
while check_board(board):
board = [1,2,3,4]*9
shuffle(board)
board = [board[i:i + 6] for i in range(0, len(board), 6)]
print(board)
Example boards generated:
[[3, 2, 4, 3, 3, 2],
[1, 1, 2, 3, 1, 3],
[1, 3, 3, 2, 2, 2],
[4, 4, 1, 4, 1, 2],
[1, 1, 4, 4, 2, 4],
[2, 4, 4, 3, 3, 1]]
[[2, 3, 4, 1, 4, 1],
[3, 4, 1, 1, 3, 4],
[3, 1, 4, 1, 3, 4],
[3, 4, 2, 4, 2, 1],
[2, 1, 4, 2, 3, 2],
[2, 2, 1, 3, 3, 2]]
Create an array of size 36 and fill it with your desired values => [1,1,1....4,4,4]
Apply Fisher-Yates shuffle to create a permutation of that array in O(n)
Check for the "3 in a row" rule and swap a random value if nesseccary, check again until the grid is free of that.

Creating a dictionary given values from a list and dictionary

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)

Generating all lists that satisfy certain constraints in Python

I would like to generate the following lists in Python:
[1, 1, 1, 2, 2]
[1, 1, 2, 1, 2]
... etc
[2, 1, 2, 1, 1]
[2, 2, 1, 1, 1]
There are always two "2"s and three "1"s in any list.
My intuition suggests that I will need to use the itertools module to do this. However, I am not sure where to begin, though I have read the documentation and looked at examples. Any suggestions?
You can notice that the number of such lists is equal to the number of ways to place two "2"s in a sequence of length 5. This suggests the following solution:
n = 5 # total length
n2 = 2 # number of "2"s
for idx in itertools.combinations( xrange(n), n2 ):
print [ 2 if i in idx else 1 for i in xrange(n) ]
It's easy to see that the answer using permutations is iterating over n! solutions, while my solution iterates over n!/( (n-n2)! * n2!). For example if the input list is [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2], the solution using permutations is ~90,000,000 times slower (10! * 4!)
You can use itertools.permutations and set (to eliminate duplicates):
>>> from itertools import permutations
>>> for combo in set(permutations([1, 1, 1, 2, 2])):
... print(list(combo))
...
[1, 2, 1, 1, 2]
[2, 1, 1, 1, 2]
[2, 1, 2, 1, 1]
[2, 1, 1, 2, 1]
[1, 1, 2, 1, 2]
[1, 1, 1, 2, 2]
[1, 2, 1, 2, 1]
[1, 1, 2, 2, 1]
[1, 2, 2, 1, 1]
[2, 2, 1, 1, 1]
>>>
If the combinations need to be in order, then you can use sorted:
>>> for combo in sorted(set(permutations([1, 1, 1, 2, 2]))):
... print(list(combo))
...
[1, 1, 1, 2, 2]
[1, 1, 2, 1, 2]
[1, 1, 2, 2, 1]
[1, 2, 1, 1, 2]
[1, 2, 1, 2, 1]
[1, 2, 2, 1, 1]
[2, 1, 1, 1, 2]
[2, 1, 1, 2, 1]
[2, 1, 2, 1, 1]
[2, 2, 1, 1, 1]
>>>

Categories

Resources