Looking for a more efficient way to achieve a desired result. Combinations is first used to create a list of items taken two at a time:
combos = itertools.combinations(range(1,6),2) b=list(combos)
[(1, 2), (1, 3), (1, 4), (1, 5), (2, 3), (2, 4), (2, 5), (3, 4), (3, 5), (4, 5)]
Starting with the first firsts tuple (1,2), I need to identify which other 2-tuple can be used to create a 4 tuple where the 2nd tuple has no elements in common with the first. (Akin to sampling two players for one team, and two for another).
The desired result is a maximum set of 4 tuples from the list (b) above, where all there are no duplicates. Note For this exercise: (3,4,1,2) is a duplicate of (1,2,3,4). Continuing with the team analogy, it is because the opponents are the same: (1,2) vs. (3,4). Effectively cuts the list in half.
The code below creates the desired output:
x = [(b[i][0], b[i][1], b[j][0], b[j][1])
for i in range(0,len(b))
for j in range(i 1,len(b))
if b[i][0] != b[j][0]
and b[i][0] != b[j][1]
and b[i][1] != b[j][0]
and b[i][1] != b[j][1]]
[(1, 2, 3, 4), (1, 2, 3, 5), (1, 2, 4, 5), (1, 3, 2, 4), (1, 3, 2, 5), (1, 3, 4, 5), (1, 4, 2, 3), (1, 4, 2, 5), (1, 4, 3, 5), (1, 5, 2, 3), (1, 5, 2, 4), (1, 5, 3, 4), (2, 3, 4, 5), (2, 4, 3, 5), (2, 5, 3, 4)]
Got the right result Choose(5,2) X Choose (3,2) cut in half for 10 * 3 /2 = 15 4-tuples.
I wanted to get the communities input if there is possibly a better way to generate the list. Clunky, but it works. Cheers and Thanks!
CodePudding user response:
You can generate all unique 4-tuples and add permutations:
idxs = [[0, 1, 2, 3], [0, 2, 1, 3], [0, 3, 1, 2]]
result = list()
for comb in itertools.combinations(range(1, 6), 4):
for idx in idxs:
result.append(tuple([comb[i] for i in idx]))
CodePudding user response:
You can generate these lazily (so minimal memory is needed) and without needing to filter duplicates by iterating over combinations of length 4
instead of pairs of combinations of length 2
.
Suppose you have an ordered list of 4 numbers: a < b < c < d
. These 4 numbers will be part of exactly 3 results in the answer: a
will be first, and we have 3 choices for the second number, after which the last two numbers must go in order.
Then just use itertools.chain() for lazy evaluation:
ans = itertools.chain.from_iterable(
(((a, b, c, d),
(a, c, b, d),
(a, d, b, c))
for a, b, c, d in itertools.combinations(range(1, 6), 4)))
producing:
(1, 2, 3, 4)
(1, 3, 2, 4)
(1, 4, 2, 3)
(1, 2, 3, 5)
(1, 3, 2, 5)
(1, 5, 2, 3)
(1, 2, 4, 5)
(1, 4, 2, 5)
(1, 5, 2, 4)
(1, 3, 4, 5)
(1, 4, 3, 5)
(1, 5, 3, 4)
(2, 3, 4, 5)
(2, 4, 3, 5)
(2, 5, 3, 4)
CodePudding user response:
If your tuples are considered equal regardless of their member's order, you want to use sets instead.
from itertools import combinations, product
sets = set(map(frozenset, combinations(range(1,6),2)))
result = {frozenset((a, b)) for a, b in product(sets, sets) if a.isdisjoint(b)}
print(result)
And if you want to go back to a list of tuples:
result = [(*a, *b) for a, b in result]
print(result, len(result))