Open
Description
Bug Report
Description
The Python implementation in the stable marriage chapter is worst case ϴ(n³), but should be O(n²).
I have attached an alternative implementation that runs in worst-case quadratic time. I can make a PR if you want to replace the current algorithm.
Steps to Reproduce
Run the code with N = 3000.
Expected behavior
Terminate within seconds.
Screenshots
N | current | new |
---|---|---|
0 | 0:00.11 | 0:00.08 |
100 | 0:00.10 | 0:00.07 |
200 | 0:00.14 | 0:00.09 |
300 | 0:00.18 | 0:00.13 |
400 | 0:00.28 | 0:00.16 |
500 | 0:00.24 | 0:00.20 |
600 | 0:00.90 | 0:00.27 |
700 | 0:00.71 | 0:00.35 |
800 | 0:01.38 | 0:00.43 |
900 | 0:01.95 | 0:00.55 |
1000 | 0:01.37 | 0:00.65 |
1100 | 0:04.69 | 0:00.80 |
1200 | 0:02.42 | 0:00.95 |
1300 | 0:04.20 | 0:01.15 |
1400 | 0:05.49 | 0:01.29 |
1500 | 0:09.23 | 0:01.55 |
1600 | 0:15.27 | 0:01.76 |
1700 | 0:09.13 | 0:01.93 |
1800 | 0:10.11 | 0:02.19 |
1900 | 0:11.64 | 0:02.27 |
2000 | 0:18.57 | 0:02.49 |
2100 | 0:22.63 | 0:02.89 |
2200 | 0:19.52 | 0:03.13 |
2300 | 0:26.70 | 0:03.43 |
2400 | 0:54.32 | 0:03.72 |
2500 | 1:07.84 | 0:04.16 |
Additional context
from random import sample as shuffle
def stable_matching(hospital_preferences, student_preferences):
students = [s for s in student_preferences]
hospitals = [h for h in hospital_preferences]
proposals = {h: 0 for h in hospitals}
unmatched_hospitals = [h for h in hospitals]
student = {h: None for h in hospitals}
hospital = {s: None for s in students}
inrank = {
s: {} for s in students
} # maps s to each hospital's s-ranking
for s in students:
for idx, h in enumerate(student_preferences[s]):
inrank[s][h] = idx
while unmatched_hospitals:
h = unmatched_hospitals.pop()
nxt = proposals[h]
s = hospital_preferences[h][nxt]
proposals[h] += 1
# h proposes to its best student not yet proposed to
if not hospital[s]:
# s is available
hospital[s] = h
student[h] = s
else:
sh = hospital[s]
rank_sh = inrank[s][sh]
rank_h = inrank[s][h]
if rank_h < rank_sh:
# s dumps sh for h
hospital[s] = h
student[sh] = None
student[h] = s
unmatched_hospitals.append(sh)
else:
# s is taken
unmatched_hospitals.append(h)
return student
def _generate_instance(N):
HOSPITALS = [f"h{i}" for i in range(N)]
STUDENTS = [f"s{i}" for i in range(N)]
hospital_preferences = {h: STUDENTS[:N] for h in HOSPITALS[:N]}
student_preferences = {s: HOSPITALS[:N] for s in STUDENTS[:N]}
for h in HOSPITALS[:N]:
hospital_preferences[h] = shuffle(hospital_preferences[h], N)
for s in STUDENTS[:N]:
student_preferences[s] = shuffle(student_preferences[s], N)
return hospital_preferences, student_preferences
if __name__ == "__main__":
import sys
hospital_preferences, student_preferences = _generate_instance(int(sys.argv[1]))
#print(hospital_preferences)
#print(student_preferences)
M = stable_matching(hospital_preferences, student_preferences)
for h in M:
print(f"Hospital {h} + Student {M[h]}")
For Algorithm Archive Developers
- The bug can be reproduced
- The bug can be fixed (if not, please explain why not in a comment below)
- There is a timeline to fix the bug
- The bug has been fixed (Please link the PR)