Search code examples
pythonpython-2.7python-itertools

itertools.product eliminating repeated reversed tuples


I asked a question yesterday and thanks to Tim Peters, it is solved. The question is here;

itertools.product eliminating repeated elements

The new question is further version of this. This time I will generate tuples inside of tuples. Here is an example;

lis = [[(1,2), (3,4)], [(5,2), (1,2)], [(2,1), (1,2)]]

When I use it in itertools.product function this is what I get,

((1, 2), (5, 2), (2, 1))
((1, 2), (5, 2), (1, 2))
((1, 2), (1, 2), (2, 1))
((1, 2), (1, 2), (1, 2))
((3, 4), (5, 2), (2, 1))
((3, 4), (5, 2), (1, 2))
((3, 4), (1, 2), (2, 1))
((3, 4), (1, 2), (1, 2))

I want to change it in a way that if a sequence has (a,b) inside of it, then it can not have (b,a). In this example if you look at this sequence ((3, 4), (1, 2), (2, 1)) it has (1,2) and (2,1) inside of it. So, this sequence ((3, 4), (1, 2), (2, 1)) should not be considered in the results.

As I said, I asked similar question before, in that case it was not considering duplicate elements. I try to adapt it to my problem. Here is modified code. Changed parts in old version are taken in comments.

def reverse_seq(seq):
    s = []
    for i in range(len(seq)):
        s.append(seq[-i-1])         
    return tuple(s)


def uprod(*seqs):  
    def inner(i):
        if i == n:
            yield tuple(result)
            return
        for elt in sets[i] - reverse:
            #seen.add(elt)
            rvrs = reverse_seq(elt)
            reverse.add(rvrs)
            result[i] = elt
            for t in inner(i+1):
                yield t
            #seen.remove(elt)
            reverse.remove(rvrs)

    sets = [set(seq) for seq in seqs]
    n = len(sets)
    #seen = set()
    reverse = set()
    result = [None] * n
    for t in inner(0):
        yield t

In my opinion this code should work but I am getting error for the input lis = [[(1,2), (3,4)], [(5,2), (1,2)], [(2,1), (1,2)]]. I could not understand where I am wrong.

for i in uprod(*lis):
    print i

Output is,

((1, 2), (1, 2), (1, 2))
Traceback (most recent call last):
  File "D:\Users\SUUSER\workspace tree\sequence_covering _array\denemeler_buraya.py", line 39, in <module>
    for i in uprod(*lis):
  File "D:\Users\SUUSER\workspace tree\sequence_covering _array\denemeler_buraya.py", line 32, in uprod
    for t in inner(0):
  File "D:\Users\SUUSER\workspace tree\sequence_covering _array\denemeler_buraya.py", line 22, in inner
    for t in inner(i+1):
  File "D:\Users\SUUSER\workspace tree\sequence_covering _array\denemeler_buraya.py", line 25, in inner
    reverse.remove(rvrs)
KeyError: (2, 1)

Thanks,


Solution

  • The problem is that you unconditionally do reverse.remove(rvrs), even if rvrs was already in reverse before you (redundantly) added it. So insert:

                remove_later = rvrs not in reverse
    

    before:

                reverse.add(rvrs)
    

    and change the removal code to:

                if remove_later:
                    reverse.remove(rvrs)
    

    Then the output is:

    ((1, 2), (1, 2), (1, 2))
    ((1, 2), (5, 2), (1, 2))
    ((3, 4), (1, 2), (1, 2))
    ((3, 4), (5, 2), (1, 2))
    ((3, 4), (5, 2), (2, 1))
    

    Unrelatedly, you can throw away the reverse_seq() function and write this instead:

                rvrs = elt[::-1]