itertools.product eliminating repeated reversed tuples

折月煮酒 提交于 2021-01-28 19:09:13

问题


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,


回答1:


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]



回答2:


I’m doing it a bit differently, using sets to get rid of the order where the order is not necessary. Actually using frozensets, so we can easily nest them.

First of all, we convert the lis into a list of lists of (frozen)sets, because the order of the numbers of the tuples should be ignored.

>>> lis = [[(1,2), (3,4)], [(5,2), (1,2)], [(2,1), (1,2)]]
>>> lis_ = [[frozenset(x) for x in y] for y in lis]

Next, we create the product, and then put the result in a set, so we get rid of duplicates:

>>> result = set(x for x in itertools.product(*lis_))
>>> result
{(frozenset({3, 4}), frozenset({1, 2}), frozenset({1, 2})), (frozenset({1, 2}), frozenset({1, 2}), frozenset({1, 2})), (frozenset({3, 4}), frozenset({2, 5}), frozenset({1, 2})), (frozenset({1, 2}), frozenset({2, 5}), frozenset({1, 2}))}

And we are already done. If you print those now, and make it a bit prettier (getting rid of the frozenset() part in the output, you have your result:

>>> for r in result:
        print([tuple(x) for x in r])

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

Different solution which just filters the results from itertools.product:

>>> lis = [[(1,2), (3,4)], [(5,2), (1,2)], [(2,1), (1,2)]]
>>> seenProducts = set()
>>> for p in itertools.product(*lis):
        product = tuple(frozenset(x) for x in p)
        if product not in seenProducts:
            seenProducts.add(product)
            print(p) # print original product

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


来源:https://stackoverflow.com/questions/19754499/itertools-product-eliminating-repeated-reversed-tuples

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!