Skip to content

Commit bf1ffe7

Browse files
committed
Added Gale-Shapley algorithm with test cases (#1963)
1 parent cad4754 commit bf1ffe7

File tree

2 files changed

+91
-0
lines changed

2 files changed

+91
-0
lines changed

algorithms/greedy/__init__.py

+1
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
11
from .max_contiguous_subsequence_sum import *
2+
from .gale_shapley import *

algorithms/greedy/gale_shapley.py

+90
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
"""
2+
The Gale-Shapley algorithm is a method to solve the
3+
stable matching/marriage problem. Given N men and women
4+
with ranked preferences of the opposite sex, the men and
5+
women will be matched so that each pair of man and woman
6+
would not prefer someone over their current match. With
7+
no conflicting preferences, the matches are stable. The
8+
algorithm can be extended to issues that involve ranked
9+
matching.
10+
11+
Example for function:
12+
M denotes man, W denotes woman, and number corresponds to
13+
respective man/woman. Preference lists go from highest to lowest.
14+
15+
men_preferences = {
16+
"M1": ["W1", "W2", "W3"],
17+
"M2": ["W1", "W3", "W2"],
18+
"M3": ["W3", "W1", "W2"],
19+
}
20+
women_preferences = {
21+
"W1": ["M2", "M1", "M3"],
22+
"W2": ["M1", "M2", "M3"],
23+
"W3": ["M3", "M1", "M2"],
24+
}
25+
26+
input: print(gale_shapley(men_preferences, women_preferences))
27+
output: {'M2': 'W1', 'M3': 'W3', 'M1': 'W2'}
28+
Explanation:
29+
Both Man 1 and Man 2 have a top preference of Woman 1,
30+
and since Woman 1 has a top preference of Man 2, Man 2
31+
and Woman 1 are matched, and Man 1 is added back to available
32+
men. Man 3 and Woman 3 have their top preference as each other,
33+
so the two are matched. Man 1 then proposes to Woman 2, and
34+
Man 1 is the top preference of Woman 2, so the two are matched.
35+
There is no match of Man AND Woman where both would want to
36+
leave, so the current matches are stable.
37+
38+
"""
39+
40+
# size denotes the number of men/women
41+
# Function takes in dictionary for men and women preferences in style outlined above
42+
def gale_shapley(men, women):
43+
size = len(men)
44+
# Initialize all men to be available
45+
men_available = list(men.keys())
46+
# Initialize married to empty
47+
married = {}
48+
# Intialize proposal count for each man to 0
49+
proposal_counts = {man: 0 for man in men}
50+
while men_available:
51+
# Pop first available man
52+
man = men_available.pop(0)
53+
# Of the popped man, set woman equal to corresponding proposal index
54+
woman = men[man][proposal_counts[man]]
55+
#increment proposal count
56+
proposal_counts[man] += 1
57+
if woman not in married:
58+
# Set marriage if woman not married
59+
married[woman] = man
60+
else:
61+
# If woman married, currently_married corresponds to currently matched man
62+
currently_married = married[woman]
63+
if women[woman].index(man) < women[woman].index(currently_married):
64+
"""
65+
If the available man is of greater preference to the woman than her
66+
currently married partner, change the marriage to the new available
67+
man and append the previously married man back to men_available
68+
"""
69+
married[woman] = man
70+
men_available.append(currently_married)
71+
else:
72+
# Add man back to men_available and try woman at next index
73+
men_available.append(man)
74+
# Returns pairs of matched men and women in form of Man:Woman
75+
return {man: woman for woman, man in married.items()}
76+
77+
# Example case
78+
men_preferences = {
79+
"M1": ["W1", "W2", "W3"],
80+
"M2": ["W1", "W3", "W2"],
81+
"M3": ["W3", "W1", "W2"],
82+
}
83+
women_preferences = {
84+
"W1": ["M2", "M1", "M3"],
85+
"W2": ["M1", "M2", "M3"],
86+
"W3": ["M3", "M1", "M2"],
87+
}
88+
89+
res = gale_shapley(men_preferences, women_preferences)
90+
print(res)

0 commit comments

Comments
 (0)