From choices [1,2,3], I want to output all possible combinations, allowing duplicates in the choices, in a list of 5 elements.
In each of the lists, there must be at least one of 1, at least one of 2, and at least one of 3.
A clumsy way as below. It firstly generates a list of 5 using either in [1,2,3]. All generated lists are examined to have at least each of [1,2,3]. The qualified ones are put into a big list. Then the duplicates in the big list are removed (loop it many times to make sure good coverage):
import random
import itertools
choices = [1,2,3]
big_list = []
for a in range(10000):
new_list = [random.choice(choices) for i in range(5)]
if new_list.count(1) >= 1 and new_list.count(2) >= 1 and new_list.count(3) >= 1:
big_list.append(new_list)
big_list.sort()
final_list = list(big_list for big_list, _ in itertools.groupby(big_list))
# this line to remove the duplicates in the list of lists
print (final_list)
Considering the sequence matters, that is, [1,1,1,2,3] and [2,3,1,1,1] are two different lists.
What would be the smarter and more comprehensive way to do so? Thank you.
CodePudding user response:
Maybe you could could use itertools.combinations_with_replacement
, itertools.permutations
along with collections.Counter
:
>>> from collections import Counter
>>> from itertools import combinations_with_replacement, permutations
>>>
>>> def is_valid_combination(comb: tuple) -> bool:
... digit_counts = Counter(comb)
... return digit_counts[1] >= 1 and \
... digit_counts[2] >= 1 and \
... digit_counts[3] >= 1
...
>>> choices = [1, 2, 3]
>>> valid_combinations = [
... c for c in combinations_with_replacement(choices, r=5)
... if is_valid_combination(c)
... ]
>>>
>>> valid_combinations
[(1, 1, 1, 2, 3), (1, 1, 2, 2, 3), (1, 1, 2, 3, 3), (1, 2, 2, 2, 3), (1, 2, 2, 3, 3), (1, 2, 3, 3, 3)]
>>>
>>> all_permutations_of_valid_combinations = {
... p
... for c in valid_combinations for p in permutations(c)
... }
>>>
>>> all_permutations_of_valid_combinations
{(2, 1, 3, 1, 2), (2, 1, 3, 2, 1), (3, 3, 2, 1, 3), (1, 2, 3, 2, 3), (1, 2, 1, 3, 1), (3, 1, 2, 3, 2), (3, 3, 3, 2, 1), (3, 2, 2, 1, 1), (1, 2, 2, 3, 1), (1, 3, 2, 2, 3), (1, 3, 2, 3, 2), (1, 2, 1, 1, 3), (3, 1, 3, 3, 2), (3, 1, 1, 2, 3), (2, 1, 3, 2, 3), (1, 2, 2, 1, 3), (1, 2, 1, 3, 3), (2, 3, 3, 1, 2), (2, 3, 3, 2, 1), (3, 3, 1, 2, 1), (3, 2, 3, 2, 1), (1, 2, 2, 3, 3), (3, 2, 1, 1, 1), (2, 2, 1, 3, 1), (2, 3, 1, 1, 1), (1, 3, 1, 2, 3), (3, 3, 1, 1, 2), (3, 2, 3, 1, 2), (2, 1, 2, 3, 1), (2, 2, 1, 1, 3), (3, 2, 1, 3, 1), (2, 3, 1, 3, 1), (1, 1, 3, 2, 1), (2, 3, 2, 1, 2), (2, 3, 2, 2, 1), (2, 1, 2, 1, 3), (3, 2, 1, 1, 3), (2, 2, 1, 3, 3), (2, 3, 1, 1, 3), (2, 3, 1, 2, 2), (3, 2, 3, 3, 1), (1, 1, 3, 1, 2), (2, 1, 2, 3, 3), (3, 3, 2, 2, 1), (3, 1, 2, 1, 2), (3, 2, 1, 3, 3), (3, 1, 2, 2, 1), (2, 3, 1, 3, 3), (1, 1, 3, 2, 3), (3, 3, 3, 1, 2), (1, 2, 3, 1, 1), (1, 1, 3, 3, 2), (3, 1, 3, 1, 2), (2, 3, 2, 3, 1), (1, 3, 2, 1, 1), (2, 1, 3, 3, 1), (3, 2, 2, 3, 1), (3, 1, 2, 2, 3), (1, 3, 2, 2, 2), (1, 2, 3, 1, 3), (1, 3, 2, 3, 1), (3, 2, 2, 1, 3), (2, 2, 3, 2, 1), (3, 1, 1, 2, 2), (1, 1, 2, 2, 3), (2, 1, 3, 2, 2), (1, 3, 3, 2, 2), (3, 3, 1, 3, 2), (2, 1, 1, 3, 1), (1, 3, 2, 1, 3), (2, 1, 3, 3, 3), (3, 1, 3, 2, 2), (2, 2, 3, 1, 2), (1, 1, 2, 3, 1), (3, 2, 1, 2, 2), (1, 2, 2, 3, 2), (3, 3, 1, 2, 3), (1, 3, 2, 3, 3), (1, 2, 1, 2, 3), (3, 2, 3, 1, 1), (1, 3, 1, 2, 2), (1, 2, 2, 2, 3), (2, 1, 1, 3, 3), (3, 1, 1, 3, 2), (1, 1, 2, 3, 3), (1, 3, 3, 3, 2), (2, 3, 2, 1, 1), (2, 2, 1, 2, 3), (2, 2, 1, 3, 2), (1, 2, 3, 3, 1), (3, 2, 3, 1, 3), (2, 3, 1, 2, 1), (2, 1, 3, 1, 1), (3, 3, 2, 1, 2), (1, 2, 3, 2, 2), (1, 3, 1, 3, 2), (3, 1, 2, 3, 1), (2, 2, 2, 3, 1), (2, 1, 2, 2, 3), (1, 2, 3, 3, 3), (2, 3, 1, 2, 3), (2, 1, 3, 1, 3), (3, 2, 2, 2, 1), (1, 2, 1, 3, 2), (2, 3, 3, 1, 1), (3, 1, 2, 3, 3), (3, 2, 2, 1, 2), (3, 1, 1, 2, 1), (1, 3, 3, 2, 1), (2, 3, 3, 3, 1), (2, 1, 1, 1, 3), (1, 3, 2, 1, 2), (2, 1, 3, 3, 2), (1, 1, 1, 2, 3), (3, 1, 3, 2, 1), (1, 1, 1, 3, 2), (2, 2, 3, 1, 1), (3, 1, 1, 1, 2), (1, 1, 2, 1, 3), (1, 3, 3, 1, 2), (3, 2, 1, 2, 1), (2, 3, 3, 1, 3), (3, 3, 1, 2, 2), (2, 2, 3, 3, 1), (1, 3, 1, 2, 1), (1, 3, 3, 2, 3), (3, 2, 1, 1, 2), (2, 1, 1, 3, 2), (2, 3, 1, 1, 2), (3, 1, 3, 2, 3), (2, 2, 3, 1, 3), (1, 3, 1, 1, 2), (1, 1, 2, 3, 2), (2, 1, 2, 3, 2), (3, 2, 1, 2, 3), (3, 1, 2, 1, 1), (3, 2, 1, 3, 2), (2, 1, 1, 2, 3), (2, 3, 1, 3, 2), (1, 1, 3, 2, 2), (2, 3, 2, 1, 3), (3, 3, 2, 3, 1), (3, 3, 2, 1, 1), (1, 2, 3, 2, 1), (3, 1, 2, 1, 3), (2, 2, 2, 1, 3), (3, 1, 2, 2, 2), (1, 3, 2, 2, 1), (1, 2, 3, 1, 2), (1, 2, 3, 3, 2)}
CodePudding user response:
Apart from the itertools.combinations
itself, you could use some recursive logic:
def combinations(choices, n = 5):
if n == 1:
return [[x,] for x in choices]
else:
return [v [x,] for v in combinations(choices, n = n -1) for x in choices]
To select only the combinations which contain at least one 1, 2 and 3:
[x for x in combinations(choices, n = 5) if all(c in x for c in choices)]