Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to remove mirrors reflections values of itertools.product function?

I create a cartesian product using the itertools.product function:

from itertools import product

a = list(map(list, itertools.product(list(range(2)), repeat=3)))

Output:

[[0, 0, 0], [0, 0, 1], [0, 1, 0], [0, 1, 1], [1, 0, 0], [1, 0, 1], [1, 1, 0], [1, 1, 1]]

Then I get rid of mirrors reflections in the following way:

b = [] 
for k, v in enumerate(a):
    if v[::-1] not in a[:k]:
       b.append(v[::-1])

Output:

[[0, 0, 0], [1, 0, 0], [0, 1, 0], [1, 1, 0], [1, 0, 1], [1, 1, 1]]

But can I get the same effect step by step without saving all the results of itertools.product in the list? For example, with the usual approach on the for loop:

for i in list(map(list, itertools.product(list(range(2)), repeat=3))):
    # blah blah blah

Because ultimately I will use large cartesian products, at least repeat = 18. And that is why I have to give up the approach on the lists. Unless there is another way to do it? I will be grateful for any tips.

like image 576
Tomasz Przemski Avatar asked Jan 10 '18 11:01

Tomasz Przemski


2 Answers

import itertools

l = (list(i) for i in itertools.product(tuple(range(2)), repeat=3) if tuple(reversed(i)) >= tuple(i))
print list(l)

Output:

[[0, 0, 0], [0, 0, 1], [0, 1, 0], [0, 1, 1], [1, 0, 1], [1, 1, 1]]
like image 51
Piotr Dobrogost Avatar answered Nov 16 '22 14:11

Piotr Dobrogost


Here is an idea for a recursive algorithm to generate only the necessary combinations (as opposed to generate the whole Cartesian product and discarding the unnecessary ones):

def noReflections(n, k, current=None, idx=0, symmetric=True):
    # n: number of distinct elements
    # k: sequences length
    # current: sequence being generated
    # idx: current generated index
    # symmetric: true if the chosen elements up to now are symmetric
    assert n >= 0 and k >= 0
    if n == 0 or k == 0:
        return
    if idx == 0:
        current = k * [0]
    if idx < k // 2:
        # Choose the value for current position (idx) and symmetric (idx2)
        idx2 = k - idx - 1
        for i in range(n):
            # Value for current position
            current[idx] = i
            # If all previously selected values were symmetric,
            # the symmetric position must have a value equal or greater 
            # than the current; otherwise it can take any value.
            first = i if symmetric else 0
            for j in range(first, n):
                # Value for symmetric position
                current[idx2] = j
                # Recursive call
                # Only keep symmetric flag if previously selected values
                # and the ones selected now are symmetric.
                yield from noReflections(n, k, current, idx + 1, symmetric and (i == j))
    elif idx == k // 2 and (k % 2 == 1):
        # In middle position of odd-length sequence
        # Make one sequence with each possible value
        for i in range(n):
            current[idx] = i
            yield tuple(current)
    else:
        # Even-length sequence completed
        yield tuple(current)

print(list(noReflections(2, 3)))
>>> [(0, 0, 0), (0, 1, 0), (0, 0, 1), (0, 1, 1), (1, 0, 1), (1, 1, 1)]

I'm not sure this should perform better than the other answer though, because of the recursion and so on (in a couple of quick tests bot performed similarly in my machine).

like image 34
jdehesa Avatar answered Nov 16 '22 14:11

jdehesa