I try to solve a problem and one part of it is to find all paths from (0, 0) to the right most point of the 2d array. This is my code:
def route_finder_helper(x, y, current_path, filler, list_of_lists):
current_path[filler] = (x, y)
if x == 0 and y == 0:
print(current_path)
list_of_lists.append(current_path)
return list_of_lists
if x == 0:
return route_finder_helper(x, y - 1, current_path, filler - 1, list_of_lists)
if y == 0:
return route_finder_helper(x - 1, y, current_path, filler - 1, list_of_lists)
return route_finder_helper(x-1, y, current_path, filler - 1, list_of_lists) + \
route_finder_helper(x, y-1, current_path, filler - 1, list_of_lists)
where x and y is current coordinate, current_path is a list of tuples of the current path, filler is index what position of the list to change and list_of_lists should be all the paths. However, when I run this program and print the return value I get this output:
[(0, 0), (0, 1), (0, 2), (1, 2), (2, 2)]
[(0, 0), (0, 1), (1, 1), (1, 2), (2, 2)]
[(0, 0), (1, 0), (1, 1), (1, 2), (2, 2)]
[(0, 0), (0, 1), (1, 1), (2, 1), (2, 2)]
[(0, 0), (1, 0), (1, 1), (2, 1), (2, 2)]
[(0, 0), (1, 0), (2, 0), (2, 1), (2, 2)]
[[(0, 0), (1, 0), (2, 0), (2, 1), (2, 2)], [(0, 0), (1, 0), (2, 0), (2, 1), (2, 2)], [(0, 0), (1, 0), (2, 0), (2, 1), (2, 2)], [(0, 0), (1, 0), (2, 0), (2, 1), (2, 2)], [(0, 0), (1, 0), (2, 0), (2, 1), (2, 2)], [(0, 0), (1, 0), (2, 0), (2, 1), (2, 2)], [(0, 0), (1, 0), (2, 0), (2, 1), (2, 2)], [(0, 0), (1, 0), (2, 0), (2, 1), (2, 2)], [(0, 0), (1, 0), (2, 0), (2, 1), (2, 2)], [(0, 0), (1, 0), (2, 0), (2, 1), (2, 2)], [(0, 0), (1, 0), (2, 0), (2, 1), (2, 2)], [(0, 0), (1, 0), (2, 0), (2, 1), (2, 2)], [(0, 0), (1, 0), (2, 0), (2, 1), (2, 2)], [(0, 0), (1, 0), (2, 0), (2, 1), (2, 2)], [(0, 0), (1, 0), (2, 0), (2, 1), (2, 2)], [(0, 0), (1, 0), (2, 0), (2, 1), (2, 2)], [(0, 0), (1, 0), (2, 0), (2, 1), (2, 2)], [(0, 0), (1, 0), (2, 0), (2, 1), (2, 2)], [(0, 0), (1, 0), (2, 0), (2, 1), (2, 2)], [(0, 0), (1, 0), (2, 0), (2, 1), (2, 2)], [(0, 0), (1, 0), (2, 0), (2, 1), (2, 2)], [(0, 0), (1, 0), (2, 0), (2, 1), (2, 2)], [(0, 0), (1, 0), (2, 0), (2, 1), (2, 2)], [(0, 0), (1, 0), (2, 0), (2, 1), (2, 2)], [(0, 0), (1, 0), (2, 0), (2, 1), (2, 2)]]
So I get the correct paths, but I do not know how to save them to the list of lists. Could someone help me?
This is how I call my function:
x_coordinate = coordinates[0]
y_coordinate = coordinates[1]
path_length = (x_coordinate + 1) + (y_coordinate + 1) - 1
start_filling = path_length - 1
current_path = [0] * path_length
paths = route_finder_helper(x_coordinate, y_coordinate, current_path,
start_filling, [])
This is what it should return:
[[(0, 0), (0, 1), (0, 2), (1, 2), (2, 2)],[(0, 0), (0, 1), (1, 1), (1, 2), (2, 2)], [(0, 0), (1, 0), (1, 1), (1, 2), (2, 2)], [(0, 0), (0, 1), (1, 1), (2, 1), (2, 2)], [(0, 0), (1, 0), (1, 1), (2, 1), (2, 2)], [(0, 0), (1, 0), (2, 0), (2, 1), (2, 2)]]
This is the corrected answer:
def route_finder_helper(x, y, current_path, filler, list_of_lists):
current_path[filler] = (x, y)
if x == 0 and y == 0:
return list_of_lists + [current_path[:]]
if x == 0:
return route_finder_helper(x, y - 1, current_path, filler - 1, list_of_lists)
if y == 0:
return route_finder_helper(x - 1, y, current_path, filler - 1, list_of_lists)
return route_finder_helper(x-1, y, current_path, filler - 1, list_of_lists) + \
route_finder_helper(x, y-1, current_path, filler - 1, list_of_lists)
x_coordinate = 2
y_coordinate = 2
path_length = (x_coordinate + 1) + (y_coordinate + 1) - 1
start_filling = path_length - 1
current_path = [0] * path_length
paths = route_finder_helper(x_coordinate, y_coordinate, current_path, start_filling, [])
print(paths)
Output:
[[(0, 0), (0, 1), (0, 2), (1, 2), (2, 2)],
[(0, 0), (0, 1), (1, 1), (1, 2), (2, 2)],
[(0, 0), (1, 0), (1, 1), (1, 2), (2, 2)],
[(0, 0), (0, 1), (1, 1), (2, 1), (2, 2)],
[(0, 0), (1, 0), (1, 1), (2, 1), (2, 2)],
[(0, 0), (1, 0), (2, 0), (2, 1), (2, 2)]]
The correction here is:
return list_of_lists + [current_path[:]]
where I add a copy by using current_path[:]
The logic of the code is correct, the problem that you have is that you are dealing with a list which is a mutable item. This means that every variable which has a reference to the list, gets an updated list when you change it for only one variable:
a = [1, 2, 3]
b = a
a[0] = -1
print(b)
>>> [-1, 2, 3]
Something like this also happens in your code when you append to the list_of_lists. Simply changing the append to this:
# return list_of_lists.append(current_path) <- old code
return list_of_lists + [current_path] <- new code
This guarantees that you start a new list every time the code ends up there, instead of appending to a list where other parts of the code have a reference to as well.
If this is a new concept for you in Python there are plenty of nice blogs which explain this in more detail than me, e.g. here
complete code:
def route_finder_helper(x, y, current_path, filler, list_of_lists):
current_path[filler] = (x, y)
if x == 0 and y == 0:
return list_of_lists + [current_path]
if x == 0:
return route_finder_helper(x, y - 1, current_path, filler - 1, list_of_lists)
if y == 0:
return route_finder_helper(x - 1, y, current_path, filler - 1, list_of_lists)
return route_finder_helper(x-1, y, current_path, filler - 1, list_of_lists) + \
route_finder_helper(x, y-1, current_path, filler - 1, list_of_lists)
x_coordinate = 2
y_coordinate = 2
path_length = (x_coordinate + 1) + (y_coordinate + 1) - 1
start_filling = path_length - 1
current_path = [0] * path_length
paths = route_finder_helper(x_coordinate, y_coordinate, current_path, start_filling, [])
print(paths)
>>> [[(0, 0), (1, 0), (2, 0), (2, 1), (2, 2)], [(0, 0), (1, 0), (2, 0), (2, 1), (2, 2)], [(0, 0), (1, 0), (2, 0), (2, 1), (2, 2)], [(0, 0), (1, 0), (2, 0), (2, 1), (2, 2)], [(0, 0), (1, 0), (2, 0), (2, 1), (2, 2)], [(0, 0), (1, 0), (2, 0), (2, 1), (2, 2)]]
I have a number of bins. Each bin accommodates only one ball.
Let's say, I have
Na number of red balls
Nb number of blue balls
Nc number of green balls
and so on.
I want to find out all possible ways of placing the balls into bins of length (Na+Nb+Nc).
For example, let's say I have only two red balls and two blue balls. I want to place them into 4 bins. The possible ways of arrangements are:
R R B B
R B R B
R B B R
B R R B
B R B R
B B R R
( I hope I did not miss any combination)
Is there any easy way to generate the indices for the different colors, for example:
First row is : R=(0,1) B=(2,3)
Second row is : R=(0,2) B=(1,3)
Is there an easy way to generate this in numpy?
The bins actually have different weights, something like:
[0.1, 0.3, 0.2, 0.5]
So for the combination R R B B represented as R at (0,1) and B at (2,3) , the total weight for R is 0.1+0.3=0.4 and for B is 0.2+0.5=0.7
I ultimately am interested in the total weight for each color in the different arrangements and want to choose the best one from another cost function f(total_weight(R), total_weight(B)). Any comments if the generation of total weight could be done in a different easy way in numpy?
Here's a "multi-combinations" implementation that doesn't require eliminating duplicate permutations. The first argument, n, is the list [Na, Nb, Nc, ...].
It is implemented as a recursive generator, so you can iterate through the combinations without having them all in memory at once. You say in a comment that Na + Nb + ... is typically around 20 but could be as high as 50 or 100. That means you almost certainly do not want to store all the combinations in memory. Consider an example with four "colors" where Na = Nb = Nc = Nd = 5. The number of combinations is choose(20, 5) * choose(15, 5) * choose(10, 5) = 11732745024, where choose(n, k) is the binomial coefficient. My computer has just 16 GB of RAM, so the storage required for that number of combinations would far exceed my computer's memory.
from itertools import combinations
def multicombinations(n, bins=None):
if bins is None:
bins = set(range(sum(n)))
if len(n) == 0:
yield []
else:
for c in combinations(bins, n[0]):
for t in multicombinations(n[1:], bins - set(c)):
yield [c] + t
It generates a list of tuples. That is, where your description of the first row is "First row is : R=(0,1) B=(2,3)", the first value generated by multicombinations([2, 2]) is [(0, 1), (2, 3)]. (This might not be the best format for the result, given the description of the weight calculation that you want to do next.)
Some examples:
In [74]: list(multicombinations([2, 2]))
Out[74]:
[[(0, 1), (2, 3)],
[(0, 2), (1, 3)],
[(0, 3), (1, 2)],
[(1, 2), (0, 3)],
[(1, 3), (0, 2)],
[(2, 3), (0, 1)]]
In [75]: list(multicombinations([3, 2]))
Out[75]:
[[(0, 1, 2), (3, 4)],
[(0, 1, 3), (2, 4)],
[(0, 1, 4), (2, 3)],
[(0, 2, 3), (1, 4)],
[(0, 2, 4), (1, 3)],
[(0, 3, 4), (1, 2)],
[(1, 2, 3), (0, 4)],
[(1, 2, 4), (0, 3)],
[(1, 3, 4), (0, 2)],
[(2, 3, 4), (0, 1)]]
In [76]: list(multicombinations([2, 3, 2]))
Out[76]:
[[(0, 1), (2, 3, 4), (5, 6)],
[(0, 1), (2, 3, 5), (4, 6)],
[(0, 1), (2, 3, 6), (4, 5)],
[(0, 1), (2, 4, 5), (3, 6)],
[(0, 1), (2, 4, 6), (3, 5)],
[(0, 1), (2, 5, 6), (3, 4)],
[(0, 1), (3, 4, 5), (2, 6)],
[(0, 1), (3, 4, 6), (2, 5)],
[(0, 1), (3, 5, 6), (2, 4)],
[(0, 1), (4, 5, 6), (2, 3)],
[(0, 2), (1, 3, 4), (5, 6)],
[(0, 2), (1, 3, 5), (4, 6)],
[(0, 2), (1, 3, 6), (4, 5)],
[(0, 2), (1, 4, 5), (3, 6)],
[(0, 2), (1, 4, 6), (3, 5)],
[(0, 2), (1, 5, 6), (3, 4)],
[(0, 2), (3, 4, 5), (1, 6)],
[(0, 2), (3, 4, 6), (1, 5)],
[(0, 2), (3, 5, 6), (1, 4)],
[(0, 2), (4, 5, 6), (1, 3)],
[(0, 3), (1, 2, 4), (5, 6)],
[(0, 3), (1, 2, 5), (4, 6)],
[(0, 3), (1, 2, 6), (4, 5)],
[(0, 3), (1, 4, 5), (2, 6)],
[(0, 3), (1, 4, 6), (2, 5)],
[(0, 3), (1, 5, 6), (2, 4)],
[(0, 3), (2, 4, 5), (1, 6)],
[(0, 3), (2, 4, 6), (1, 5)],
[(0, 3), (2, 5, 6), (1, 4)],
[(0, 3), (4, 5, 6), (1, 2)],
[(0, 4), (1, 2, 3), (5, 6)],
[(0, 4), (1, 2, 5), (3, 6)],
[(0, 4), (1, 2, 6), (3, 5)],
[(0, 4), (1, 3, 5), (2, 6)],
[(0, 4), (1, 3, 6), (2, 5)],
[(0, 4), (1, 5, 6), (2, 3)],
[(0, 4), (2, 3, 5), (1, 6)],
[(0, 4), (2, 3, 6), (1, 5)],
[(0, 4), (2, 5, 6), (1, 3)],
[(0, 4), (3, 5, 6), (1, 2)],
[(0, 5), (1, 2, 3), (4, 6)],
[(0, 5), (1, 2, 4), (3, 6)],
[(0, 5), (1, 2, 6), (3, 4)],
[(0, 5), (1, 3, 4), (2, 6)],
[(0, 5), (1, 3, 6), (2, 4)],
[(0, 5), (1, 4, 6), (2, 3)],
[(0, 5), (2, 3, 4), (1, 6)],
[(0, 5), (2, 3, 6), (1, 4)],
[(0, 5), (2, 4, 6), (1, 3)],
[(0, 5), (3, 4, 6), (1, 2)],
[(0, 6), (1, 2, 3), (4, 5)],
[(0, 6), (1, 2, 4), (3, 5)],
[(0, 6), (1, 2, 5), (3, 4)],
[(0, 6), (1, 3, 4), (2, 5)],
[(0, 6), (1, 3, 5), (2, 4)],
[(0, 6), (1, 4, 5), (2, 3)],
[(0, 6), (2, 3, 4), (1, 5)],
[(0, 6), (2, 3, 5), (1, 4)],
[(0, 6), (2, 4, 5), (1, 3)],
[(0, 6), (3, 4, 5), (1, 2)],
[(1, 2), (0, 3, 4), (5, 6)],
[(1, 2), (0, 3, 5), (4, 6)],
[(1, 2), (0, 3, 6), (4, 5)],
[(1, 2), (0, 4, 5), (3, 6)],
[(1, 2), (0, 4, 6), (3, 5)],
[(1, 2), (0, 5, 6), (3, 4)],
[(1, 2), (3, 4, 5), (0, 6)],
[(1, 2), (3, 4, 6), (0, 5)],
[(1, 2), (3, 5, 6), (0, 4)],
[(1, 2), (4, 5, 6), (0, 3)],
[(1, 3), (0, 2, 4), (5, 6)],
[(1, 3), (0, 2, 5), (4, 6)],
[(1, 3), (0, 2, 6), (4, 5)],
[(1, 3), (0, 4, 5), (2, 6)],
[(1, 3), (0, 4, 6), (2, 5)],
[(1, 3), (0, 5, 6), (2, 4)],
[(1, 3), (2, 4, 5), (0, 6)],
[(1, 3), (2, 4, 6), (0, 5)],
[(1, 3), (2, 5, 6), (0, 4)],
[(1, 3), (4, 5, 6), (0, 2)],
[(1, 4), (0, 2, 3), (5, 6)],
[(1, 4), (0, 2, 5), (3, 6)],
[(1, 4), (0, 2, 6), (3, 5)],
[(1, 4), (0, 3, 5), (2, 6)],
[(1, 4), (0, 3, 6), (2, 5)],
[(1, 4), (0, 5, 6), (2, 3)],
[(1, 4), (2, 3, 5), (0, 6)],
[(1, 4), (2, 3, 6), (0, 5)],
[(1, 4), (2, 5, 6), (0, 3)],
[(1, 4), (3, 5, 6), (0, 2)],
[(1, 5), (0, 2, 3), (4, 6)],
[(1, 5), (0, 2, 4), (3, 6)],
[(1, 5), (0, 2, 6), (3, 4)],
[(1, 5), (0, 3, 4), (2, 6)],
[(1, 5), (0, 3, 6), (2, 4)],
[(1, 5), (0, 4, 6), (2, 3)],
[(1, 5), (2, 3, 4), (0, 6)],
[(1, 5), (2, 3, 6), (0, 4)],
[(1, 5), (2, 4, 6), (0, 3)],
[(1, 5), (3, 4, 6), (0, 2)],
[(1, 6), (0, 2, 3), (4, 5)],
[(1, 6), (0, 2, 4), (3, 5)],
[(1, 6), (0, 2, 5), (3, 4)],
[(1, 6), (0, 3, 4), (2, 5)],
[(1, 6), (0, 3, 5), (2, 4)],
[(1, 6), (0, 4, 5), (2, 3)],
[(1, 6), (2, 3, 4), (0, 5)],
[(1, 6), (2, 3, 5), (0, 4)],
[(1, 6), (2, 4, 5), (0, 3)],
[(1, 6), (3, 4, 5), (0, 2)],
[(2, 3), (0, 1, 4), (5, 6)],
[(2, 3), (0, 1, 5), (4, 6)],
[(2, 3), (0, 1, 6), (4, 5)],
[(2, 3), (0, 4, 5), (1, 6)],
[(2, 3), (0, 4, 6), (1, 5)],
[(2, 3), (0, 5, 6), (1, 4)],
[(2, 3), (1, 4, 5), (0, 6)],
[(2, 3), (1, 4, 6), (0, 5)],
[(2, 3), (1, 5, 6), (0, 4)],
[(2, 3), (4, 5, 6), (0, 1)],
[(2, 4), (0, 1, 3), (5, 6)],
[(2, 4), (0, 1, 5), (3, 6)],
[(2, 4), (0, 1, 6), (3, 5)],
[(2, 4), (0, 3, 5), (1, 6)],
[(2, 4), (0, 3, 6), (1, 5)],
[(2, 4), (0, 5, 6), (1, 3)],
[(2, 4), (1, 3, 5), (0, 6)],
[(2, 4), (1, 3, 6), (0, 5)],
[(2, 4), (1, 5, 6), (0, 3)],
[(2, 4), (3, 5, 6), (0, 1)],
[(2, 5), (0, 1, 3), (4, 6)],
[(2, 5), (0, 1, 4), (3, 6)],
[(2, 5), (0, 1, 6), (3, 4)],
[(2, 5), (0, 3, 4), (1, 6)],
[(2, 5), (0, 3, 6), (1, 4)],
[(2, 5), (0, 4, 6), (1, 3)],
[(2, 5), (1, 3, 4), (0, 6)],
[(2, 5), (1, 3, 6), (0, 4)],
[(2, 5), (1, 4, 6), (0, 3)],
[(2, 5), (3, 4, 6), (0, 1)],
[(2, 6), (0, 1, 3), (4, 5)],
[(2, 6), (0, 1, 4), (3, 5)],
[(2, 6), (0, 1, 5), (3, 4)],
[(2, 6), (0, 3, 4), (1, 5)],
[(2, 6), (0, 3, 5), (1, 4)],
[(2, 6), (0, 4, 5), (1, 3)],
[(2, 6), (1, 3, 4), (0, 5)],
[(2, 6), (1, 3, 5), (0, 4)],
[(2, 6), (1, 4, 5), (0, 3)],
[(2, 6), (3, 4, 5), (0, 1)],
[(3, 4), (0, 1, 2), (5, 6)],
[(3, 4), (0, 1, 5), (2, 6)],
[(3, 4), (0, 1, 6), (2, 5)],
[(3, 4), (0, 2, 5), (1, 6)],
[(3, 4), (0, 2, 6), (1, 5)],
[(3, 4), (0, 5, 6), (1, 2)],
[(3, 4), (1, 2, 5), (0, 6)],
[(3, 4), (1, 2, 6), (0, 5)],
[(3, 4), (1, 5, 6), (0, 2)],
[(3, 4), (2, 5, 6), (0, 1)],
[(3, 5), (0, 1, 2), (4, 6)],
[(3, 5), (0, 1, 4), (2, 6)],
[(3, 5), (0, 1, 6), (2, 4)],
[(3, 5), (0, 2, 4), (1, 6)],
[(3, 5), (0, 2, 6), (1, 4)],
[(3, 5), (0, 4, 6), (1, 2)],
[(3, 5), (1, 2, 4), (0, 6)],
[(3, 5), (1, 2, 6), (0, 4)],
[(3, 5), (1, 4, 6), (0, 2)],
[(3, 5), (2, 4, 6), (0, 1)],
[(3, 6), (0, 1, 2), (4, 5)],
[(3, 6), (0, 1, 4), (2, 5)],
[(3, 6), (0, 1, 5), (2, 4)],
[(3, 6), (0, 2, 4), (1, 5)],
[(3, 6), (0, 2, 5), (1, 4)],
[(3, 6), (0, 4, 5), (1, 2)],
[(3, 6), (1, 2, 4), (0, 5)],
[(3, 6), (1, 2, 5), (0, 4)],
[(3, 6), (1, 4, 5), (0, 2)],
[(3, 6), (2, 4, 5), (0, 1)],
[(4, 5), (0, 1, 2), (3, 6)],
[(4, 5), (0, 1, 3), (2, 6)],
[(4, 5), (0, 1, 6), (2, 3)],
[(4, 5), (0, 2, 3), (1, 6)],
[(4, 5), (0, 2, 6), (1, 3)],
[(4, 5), (0, 3, 6), (1, 2)],
[(4, 5), (1, 2, 3), (0, 6)],
[(4, 5), (1, 2, 6), (0, 3)],
[(4, 5), (1, 3, 6), (0, 2)],
[(4, 5), (2, 3, 6), (0, 1)],
[(4, 6), (0, 1, 2), (3, 5)],
[(4, 6), (0, 1, 3), (2, 5)],
[(4, 6), (0, 1, 5), (2, 3)],
[(4, 6), (0, 2, 3), (1, 5)],
[(4, 6), (0, 2, 5), (1, 3)],
[(4, 6), (0, 3, 5), (1, 2)],
[(4, 6), (1, 2, 3), (0, 5)],
[(4, 6), (1, 2, 5), (0, 3)],
[(4, 6), (1, 3, 5), (0, 2)],
[(4, 6), (2, 3, 5), (0, 1)],
[(5, 6), (0, 1, 2), (3, 4)],
[(5, 6), (0, 1, 3), (2, 4)],
[(5, 6), (0, 1, 4), (2, 3)],
[(5, 6), (0, 2, 3), (1, 4)],
[(5, 6), (0, 2, 4), (1, 3)],
[(5, 6), (0, 3, 4), (1, 2)],
[(5, 6), (1, 2, 3), (0, 4)],
[(5, 6), (1, 2, 4), (0, 3)],
[(5, 6), (1, 3, 4), (0, 2)],
[(5, 6), (2, 3, 4), (0, 1)]]
Generate the permutations and remove duplicates.
>>> import itertools
>>> Na = 2
>>> Nb = 2
>>> p = itertools.permutations(['R']*Na + ['B']*Nb)
>>> for perm in sorted(list(set(p)), reverse=True):
... print perm
...
('R', 'R', 'B', 'B')
('R', 'B', 'R', 'B')
('R', 'B', 'B', 'R')
('B', 'R', 'R', 'B')
('B', 'R', 'B', 'R')
('B', 'B', 'R', 'R')
You can solve that with itertools:
import itertools
last = None
result = []
for v in itertools.permutations(['A','A','B','B']):
key = "".join(v)
if key == last: continue
last = key
result.append(v)
print result
Here is one possible way of implementing:
def permutations(colors, l):
if l == 0:
yield []
else:
for i in range(len(colors)):
if colors[i]:
la = colors[:i]
lb = [colors[i][1:]]
lc = colors[i + 1:]
choice = colors[i][0]
for rest in permutations(la + lb + lc, l - 1):
yield [choice] + rest
Usage:
for choice in permutations([['R'] * 2, ['B'] * 2], 4):
print(choice)
['R', 'R', 'B', 'B']
['R', 'B', 'R', 'B']
['R', 'B', 'B', 'R']
['B', 'R', 'R', 'B']
['B', 'R', 'B', 'R']
['B', 'B', 'R', 'R']