|
| 1 | +class GaleShapley: |
| 2 | + """Implementation of the Gale-Shapley algorithem |
| 3 | +
|
| 4 | + takes it 2 preference list as a 2D array of ints. First one is the |
| 5 | + proposing side. |
| 6 | + """ |
| 7 | + |
| 8 | + def find_matches( |
| 9 | + self, |
| 10 | + proposers_preferences: dict[int, list[int]], |
| 11 | + receivers_preferences: dict[int, list[int]], |
| 12 | + ) -> dict[int, int]: |
| 13 | + """ |
| 14 | + >>> gs = GaleShapley() |
| 15 | + >>> gs.find_matches({1: [1, 2, 3], 2: [2, 1, 3], 3: [2, 3, 1]}, {1: [1, 2, 3], 2: [2, 1, 3], 3: [2, 3, 1]}) |
| 16 | + {1: 1, 2: 2, 3: 3} |
| 17 | + >>> gs.find_matches({}, {}) |
| 18 | + {} |
| 19 | + >>> gs.find_matches({1: [1,]}, {1: [1,]}) |
| 20 | + {1: 1} |
| 21 | + """ |
| 22 | + |
| 23 | + matches = {key: -1 for key in proposers_preferences.keys()} |
| 24 | + |
| 25 | + # [NOTE] I would've used sets, but want replicability for easy debugging. |
| 26 | + free_proposers = list(proposers_preferences.keys()) |
| 27 | + tested_matches = {key: 0 for key in proposers_preferences.keys()} |
| 28 | + |
| 29 | + while free_proposers: |
| 30 | + proposer = free_proposers[0] |
| 31 | + |
| 32 | + if tested_matches[proposer] == len(proposers_preferences[proposer]): |
| 33 | + free_proposers.remove(proposer) |
| 34 | + continue |
| 35 | + |
| 36 | + receiver = proposers_preferences[proposer][tested_matches[proposer]] |
| 37 | + tested_matches[proposer] += 1 |
| 38 | + |
| 39 | + if receiver not in matches.values(): |
| 40 | + matches[proposer] = receiver |
| 41 | + free_proposers.remove(proposer) |
| 42 | + continue |
| 43 | + cur_proposer = next( |
| 44 | + prop for prop, rec in matches.items() if rec == receiver |
| 45 | + ) |
| 46 | + if receivers_preferences[receiver].index(proposer) < receivers_preferences[ |
| 47 | + receiver |
| 48 | + ].index(cur_proposer): |
| 49 | + matches[cur_proposer] = -1 |
| 50 | + matches[proposer] = receiver |
| 51 | + free_proposers.remove(proposer) |
| 52 | + free_proposers.append(cur_proposer) |
| 53 | + |
| 54 | + return matches |
| 55 | + |
| 56 | + |
| 57 | +if __name__ == "__main__": |
| 58 | + import doctest |
| 59 | + |
| 60 | + doctest.testmod() |
0 commit comments