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?
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)}
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)]
Suppose I have a list of bytes (x00 to xFF). How can I use itertools to return only permutations that have length of X.
So for example I want all permutations with length 3, then I'll get
[x00,x00,x00], [x00,x00,x01], ..., [xFF,xFF,xFF]
That way there is no waste of computing resources.
EDIT: Doesn't have to be itertools if there is a better way.
import itertools
for tup in itertools.product(range(0x100), repeat=3):
...
It seems #gnibbler's solution is more correct?
In [162]: >>> l = [1, 2, 3]
In [163]: list(itertools.combinations_with_replacement(l, 3))
Out[163]:
[(1, 1, 1),
(1, 1, 2),
(1, 1, 3),
(1, 2, 2),
(1, 2, 3),
(1, 3, 3),
(2, 2, 2),
(2, 2, 3),
(2, 3, 3),
(3, 3, 3)]
In [164]: list(itertools.product(l, repeat=3))
Out[164]:
[(1, 1, 1),
(1, 1, 2),
(1, 1, 3),
(1, 2, 1),
(1, 2, 2),
(1, 2, 3),
(1, 3, 1),
(1, 3, 2),
(1, 3, 3),
(2, 1, 1),
(2, 1, 2),
(2, 1, 3),
(2, 2, 1),
(2, 2, 2),
(2, 2, 3),
(2, 3, 1),
(2, 3, 2),
(2, 3, 3),
(3, 1, 1),
(3, 1, 2),
(3, 1, 3),
(3, 2, 1),
(3, 2, 2),
(3, 2, 3),
(3, 3, 1),
(3, 3, 2),
(3, 3, 3)]
itertools.combinations_with_replacement:
>>> my_list = [1, 2, 3, 4]
>>> import itertools
>>>
>>> list(itertools.combinations_with_replacement(my_list, 3))
[(1, 1, 1), (1, 1, 2), (1, 1, 3), (1, 1, 4),
(1, 2, 2), (1, 2, 3), (1, 2, 4),
(1, 3, 3), (1, 3, 4),
(1, 4, 4),
(2, 2, 2), (2, 2, 3), (2, 2, 4),
(2, 3, 3), (2, 3, 4),
(2, 4, 4),
(3, 3, 3), (3, 3, 4),
(3, 4, 4),
(4, 4, 4)]
Seems like you want all the permutations, with replacement. In that case, you need: itertools.product as in #gnibbler's answer.
If in 2D, p(x,y), I'd like to have a adjacent matrix of 3*3:
(x-1,y-1), (x,y-1), (x+1,y-1),
...
(x-1,y+1), (x,y+1), (x+1,y+1),
What if in 3D(3*3*3), 4D(3*3*3*3),...?
Is there better function?
You might also be able to use itertools.product, depending on exactly what output format you prefer. It'll be slower than a numpy approach but I find it easier to understand:
from itertools import product
def adjacent_grid(centre):
steps = product([-1, 0, 1], repeat=len(centre))
return (tuple(c+d for c,d in zip(centre, delta)) for delta in steps)
which gives
>>> list(adjacent_grid((3,)))
[(2,), (3,), (4,)]
>>> list(adjacent_grid((3,3)))
[(2, 2), (2, 3), (2, 4), (3, 2), (3, 3), (3, 4), (4, 2), (4, 3), (4, 4)]
>>> list(adjacent_grid((3,3,3)))
[(2, 2, 2), (2, 2, 3), (2, 2, 4), (2, 3, 2), (2, 3, 3), (2, 3, 4), (2, 4, 2), (2, 4, 3), (2, 4, 4), (3, 2, 2), (3, 2, 3), (3, 2, 4), (3, 3, 2), (3, 3, 3), (3, 3, 4), (3, 4, 2), (3, 4, 3), (3, 4, 4), (4, 2, 2), (4, 2, 3), (4, 2, 4), (4, 3, 2), (4, 3, 3), (4, 3, 4), (4, 4, 2), (4, 4, 3), (4, 4, 4)]
You can get the result by using broadcasting in numpy:
import numpy as np
def p(*args):
args = np.array(args)
idx = np.array([-1, 0, 1])
a = np.broadcast_arrays(*np.ix_(*(args[:,None] + idx)))
return np.concatenate([x[..., None] for x in a], axis=-1)
The result shape is (3,3,2) in 2D, (3,3,3,3) in 3D, (3,3,3,3,4) in 4D:
>>> p(3, 8)
array([[[2, 7],
[2, 8],
[2, 9]],
[[3, 7],
[3, 8],
[3, 9]],
[[4, 7],
[4, 8],
[4, 9]]])