# Two lists – find all permutations – even multiple to one – Round 2

## Question:

I asked this question previously, but things changed so new question is in order.

I have two lists and I need to find all permutations.

The kicker is that multiple "number" items (as per example below) can be assigned. No assignments are also allowed.

So this:

``````names = ['a', 'b']
numbers = [1, 2]
``````

Would become this:

``````[
"not only this"
{'a': [1], 'b': [2]},
{'a': [2], 'b': [1]},
"but also"
{'a': [], 'b': []},
{'a': [1], 'b': []},
{'a': [2], 'b': []},
{'a': [1, 2], 'b': []},
{'a': [2, 1], 'b': []},
{'a': [], 'b': [1]},
{'a': [], 'b': [2]},
{'a': [], 'b': [1, 2]},
{'a': [], 'b': [2, 1]},
]
``````

The difference to my previous question is that in this case the list order DOES in face matter, so `[1, 2] != [2, 1]` – both cases need to be encompassed.

Also, the lists above are only for illustration purposes, since in the real scenario I will be using classes:

``````class sample_class():
def __init__(self, parameter_1, parameter_2):
self.parameter_1 = parameter_1
self.parameter_2 = parameter_2

first_group_example_list = [sample_class(x, x+1) for x in range(2)]
second_group_example_list = [sample_class(x+5, x+5+1) for x in range(2)]
``````

Now, since the number of permutations gets very high very quickly, speed is king. Not sure if using classes compared to list of ints has any effect on speed, but I suspect so, so if there are any good practices when it comes to that, please let me know.

Not sure if it has any effect on speed / memory, but since the lists are ordered, I was thinking about using element indexes when generating the permutations, instead of the classes themselves, and pull the actual class instances only in the subsequent processing steps.

EDIT: Let’s not complicate things with the classes and stick with names and numbers (as long the solution doesn’t care what type the two input lists are (either ints or strings)). I will figure out the rest, hopefully, as I go.

EDIT2: HERE is the solution that will calculate the number of "permutations".

``````import math

numbers = 6 # length of numbers list
names = 7 # length of names list

result = 0
for j in range(numbers + 1):
result += math.factorial(j + names - 1) / (math.factorial(j) * math.factorial(numbers - j))

result *= math.factorial(numbers) / math.factorial(names - 1)

print(result) # number of "permutations"
``````

Here’s a fast solution. Note that it doesn’t do any searching – it simply rolls the solutions out:

``````import itertools

def perm_lists(d, names, ix):
if ix == len(names):
yield d.copy()
return

name = names[ix]
for m in itertools.permutations(d[name]):
d[name] = m
yield from perm_lists(d, names, ix+1)

def f(p, names, numbers):
d = {name : [] for name in names}
for i, j in enumerate(p):
if j >= 0:
d[names[j]].append(numbers[i])

yield from perm_lists(d, names, 0)

def val_gen(names, numbers):
p_iter = itertools.product(range(-1, len(names)), repeat=len(numbers))

for p in p_iter:
yield from f(p, names, numbers)
``````

This implements a generator, so you don’t have to pay the cost of the entire top-level list if all you want to do is iterate over the values, but you can of course convert it to a list if desired.

Also note that this uses tuples in place of the value lists. These are more memory-efficient than lists, and fall out naturally from the implementation, but it’s trivial to convert them to lists if needed.

Here’s an example:

``````names = ['a', 'b']
numbers = [1, 2]

for d in val_gen(names, numbers):
print(d)
``````

This produces:

``````{'a': (), 'b': ()}
{'a': (2,), 'b': ()}
{'a': (), 'b': (2,)}
{'a': (1,), 'b': ()}
{'a': (1, 2), 'b': ()}
{'a': (2, 1), 'b': ()}
{'a': (1,), 'b': (2,)}
{'a': (), 'b': (1,)}
{'a': (2,), 'b': (1,)}
{'a': (), 'b': (1, 2)}
{'a': (), 'b': (2, 1)}
``````

If you want the value lists to be lists rather than tuples, just change:

``````d[name] = m
``````

to:

``````d[name] = list(m)
``````

in `perm_lists`. This will produce:

``````{'a': [], 'b': []}
{'a': [2], 'b': []}
{'a': [], 'b': [2]}
{'a': [1], 'b': []}
{'a': [1, 2], 'b': []}
{'a': [2, 1], 'b': []}
{'a': [1], 'b': [2]}
{'a': [], 'b': [1]}
{'a': [2], 'b': [1]}
{'a': [], 'b': [1, 2]}
{'a': [], 'b': [2, 1]}
``````

If you want to create the full top-level list, rather than using the generator to iterate over it, you can use:

``````full_list = list(val_gen(names, numbers))
``````

I suggest doing some timing experiments. I believe this will be fast when used on large amounts of data.

Categories: questions
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.