I need help with an algorithm that efficiently groups people into pairs, and ensures that previous pairs are not repeated.
For example, say we have 10 candidates;
candidates = [0,1,2,3,4,5,6,7,8,9]
And say we have a dictionary of previous matches such that each key-value pair i.e. candidate:matches
represents a candidate and an array of candidates that they have been paired with so far;
prev_matches = {0: [6, 5, 1, 2], 1: [4, 9, 0, 7], 2: [9, 8, 6, 0], 3: [5, 4, 8, 9], 4: [1, 3, 9, 6], 5: [3, 0, 7, 8], 6: [0, 7, 2, 4], 7: [8, 6, 5, 1], 8: [7, 2, 3, 5], 9: [2, 1, 4, 3]}
So for Candidate 0
, they were first paired with Candidate 6
, and in the subsequent pairing rounds, they were paired with Candidate 5
, Candidate 1
, and Candidate 2
. The same follows for the other key-value pairs in the dictionary.
There have already been four rounds of matches, as indicated by the length of all the matches in prev_matches
. How do I script an algorithm that creates a fifth, sixth...nth(up to numberOfCandidates - 1) round of matches such that candidates do not have duplicate pairs?
So Candidate 0
can no longer be paired with Candidate 6
, Candidate 5
, Candidate 1
, and Candidate 2
. And after a possible fifth round of matches, we could have our prev_matches
as such:
prev_matches: {0: [6, 5, 1, 2, 3], 1: [4, 9, 0, 7, 2], 2: [9, 8, 6, 0, 1], 3: [5, 4, 8, 9, 0], 4: [1, 3, 9, 6, 7], 5: [3, 0, 7, 8, 9], 6: [0, 7, 2, 4, 8], 7: [8, 6, 5, 1, 4], 8: [7, 2, 3, 5, 8], 9: [2, 1, 4, 3, 5]}
.
Here is a naive solution I tried:
def make_match(prev_matches):
paired_candidates = set()
for candidate, matches in prev_matches.items():
i = 0
while i < 10:
if i != candidate and i not in matches and i not in paired_candidates and candidate not in paired_candidates:
prev_matches[candidate].append(i)
prev_matches[i].append(candidate)
paired_candidates.add(candidate)
paired_candidates.add(i)
break
i += 1
return prev_matches
It worked for the fifth round and returned the following:
prev_matches = {0: [6, 5, 1, 2, 3], 1: [4, 9, 0, 7, 2], 2: [9, 8, 6 0, 1], 3: [5, 4, 8, 9, 0], 4: [1, 3, 9, 6, 5], 5: [3, 0, 7, 8, 4], 6: [0, 7, 2, 4, 8], 7: [8, 6, 5, 1, 9], 8: [7, 2, 3, 5, 6], 9: [2, 1, 4, 3, 7]}
For the sixth round however, it failed to work as some candidates (7 and 8) couldn't find valid pairs:
prev_matches = {0: [6, 5, 1, 2, 3, 4], 1: [4, 9, 0, 7, 2, 3], 2: [9, 8, 6, 0, 1, 5], 3: [5, 4, 8, 9, 0, 1], 4: [1, 3, 9, 6, 5, 0], 5: [3, 0, 7, 8, 4, 2], 6: [0, 7, 2, 4, 8, 9], 7: [8, 6, 5, 1, 9], 8: [7, 2, 3, 5, 6], 9: [2, 1, 4, 3, 7, 6]}
As such, it's neither a reliable nor acceptable solution.
I'm considering treating it as a backtracking problem such that I'd explore all possible pairings across the rounds till I reach a wholly acceptable and valid solution after the nth round. But the concern here would be how to make it work efficiently.
I'd appreciate any help I can get.