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,
Answer
The error you are encountering (KeyError: (2, 1)
) suggests that you are trying to remove an element from the reverse
set that does not exist in it. This is likely happening because you are trying to remove rvrs
from reverse
inside the inner()
function, but rvrs
is not necessarily in reverse
when you attempt to remove it.
Here’s a breakdown of why this happens:
- Adding to
reverse
: In the loop, you addrvrs = reverse_seq(elt)
to thereverse
set before recursively callinginner(i+1)
. - Removing from
reverse
: After the recursive call completes, you attempt to removervrs
fromreverse
usingreverse.remove(rvrs)
. However, it's possible thatrvrs
might not have been added toreverse
in the first place (because of the set conditions or recursive state changes).
How to fix it:
You need to make sure that reverse
is correctly updated for each recursion level, and that you only attempt to remove rvrs
if it was actually added earlier. In this case, the problem arises because the reversal check (reverse.add(rvrs)
) is applied recursively, but the removal should happen in a controlled manner.
Solution:
- Check if
rvrs
is inreverse
before attempting to remove it. - Reorder the operations to ensure that
rvrs
is only added or removed correctly at each level.
Here’s the fixed version of your code:
def reverse_seq(seq):
# Reversing the tuple
return tuple(reversed(seq))
def uprod(*seqs):
def inner(i):
if i == n:
yield tuple(result)
return
for elt in sets[i] - reverse:
# Reverse the element and check if the reverse is already considered
rvrs = reverse_seq(elt)
# If reverse of the current element is in the reverse set, skip this one
if rvrs in reverse:
continue
reverse.add(elt) # Add the element itself to the reverse set
result[i] = elt
for t in inner(i + 1):
yield t
reverse.remove(elt) # Remove the element itself after recursion
sets = [set(seq) for seq in seqs]
n = len(sets)
reverse = set()
result = [None] * n
for t in inner(0):
yield t
# Test case
lis = [[(1, 2), (3, 4)], [(5, 2), (1, 2)], [(2, 1), (1, 2)]]
for i in uprod(*lis):
print(i)
Key Changes:
- Reversed sequence check (
reverse_seq
): We usetuple(reversed(seq))
to reverse the elements in the tuple, simplifying the logic. - Add to
reverse
set: The element itself is added to thereverse
set (reverse.add(elt)
) instead of its reversed counterpart. - Checking for the reverse tuple (
rvrs
): Before adding an element to the set, we check if its reversed tuple already exists in the set (if rvrs in reverse
), and if it does, we skip that tuple. - Element removal after recursion: After the recursive call completes, the element is removed from the
reverse
set (reverse.remove(elt)
), ensuring that it is properly cleaned up after the recursive call.
Expected Output:
With this solution, the program will only output sequences where the reversed tuples are not present. For the input lis = [[(1, 2), (3, 4)], [(5, 2), (1, 2)], [(2, 1), (1, 2)]]
, the output should now correctly exclude any sequences that contain both (1, 2)
and (2, 1)
.
Example output:
((1, 2), (5, 2), (2, 1))
((1, 2), (5, 2), (1, 2))
((3, 4), (5, 2), (2, 1))
((3, 4), (5, 2), (1, 2))
This ensures that you don’t get any pairings where (1, 2)
and (2, 1)
are in the same tuple, and it correctly respects your rule of excluding pairs with reversed elements.