1
1
# Submitted by Marius Becker
2
+ # Updated by Amaras
3
+
2
4
3
- import sys
4
5
from random import shuffle
5
6
from copy import copy
6
- from string import ascii_uppercase
7
+ from string import ascii_uppercase , ascii_lowercase
7
8
8
- def main ():
9
- # Set this to however many men and women you want
10
- if len (sys .argv ) > 1 :
11
- num_pairs = int (sys .argv [1 ])
12
- else :
13
- num_pairs = 5
14
9
15
- # There are only 26 possible names
16
- if num_pairs > 13 :
17
- print ('You can\' have more than 13 pairs.' )
18
- return
10
+ def main ():
11
+ # Set this to however many men and women you want, up to 26
12
+ num_pairs = 5
19
13
20
14
# Create all Person objects
21
- men = [ Person (name ) for name in ascii_uppercase [0 :num_pairs ] ]
22
- women = [ Person (name ) for name in ascii_uppercase [ num_pairs :num_pairs * 2 ] ]
15
+ men = [Person (name ) for name in ascii_uppercase [:num_pairs ]]
16
+ women = [Person (name ) for name in ascii_lowercase [ :num_pairs ] ]
23
17
24
18
# Set everyone's preferences
25
19
for man in men :
@@ -31,62 +25,64 @@ def main():
31
25
shuffle (woman .preference )
32
26
33
27
# Run the algorithm
34
- resolve (men , women )
28
+ stable_marriage (men , women )
35
29
36
30
# Print preferences and the result
31
+ print ('Preferences of the men:' )
37
32
for man in men :
38
- print ('{}: {}' .format (man .name , ', ' .join ([ p .name for p in man .preference ])))
33
+ print (man )
34
+
35
+ print ()
39
36
37
+ print ('Preferences of the women:' )
40
38
for woman in women :
41
- print ('{}: {}' . format ( woman . name , ', ' . join ([ p . name for p in woman . preference ])) )
39
+ print (woman )
42
40
43
- print ('' )
41
+ print ('\n ' )
44
42
43
+ print ('The algorithm gave this solution:' )
45
44
for man in men :
46
- print ('{ } + {}' . format ( man .name , man . partner .name ) )
45
+ print (f' { man . name } + { man .partner .name } ' )
47
46
48
- def resolve (men , women ):
47
+
48
+ def stable_marriage (men , women ):
49
49
"""Finds pairs with stable marriages"""
50
- cont = True
51
- while cont :
50
+
51
+ while True :
52
52
# Let every man without a partner propose to a woman
53
53
for man in men :
54
- if not man .has_partner () :
54
+ if not man .has_partner :
55
55
man .propose_to_next ()
56
56
57
57
# Let the women pick their favorites
58
58
for woman in women :
59
59
woman .pick_preferred ()
60
60
61
61
# Continue only when someone is still left without a partner
62
- cont = False
63
- for man in men :
64
- if not man .has_partner ():
65
- cont = True
66
- break
62
+ if all ((man .has_partner for man in men )):
63
+ return
64
+
67
65
68
66
class Person :
69
- name = None
70
- preference = None
71
- pref_index = 0
72
- candidates = None
73
- partner = None
74
67
75
68
def __init__ (self , name ):
76
69
self .name = name
77
70
self .preference = []
78
71
self .candidates = []
72
+ self .pref_index = 0
73
+ self ._partner = None
79
74
80
- def get_next_choice (self ):
75
+ @property
76
+ def next_choice (self ):
81
77
"""Return the next person in the own preference list"""
82
- if self .pref_index >= len (self .preference ):
78
+ try :
79
+ return self .preference [self .pref_index ]
80
+ except IndexError :
83
81
return None
84
82
85
- return self .preference [self .pref_index ]
86
-
87
83
def propose_to_next (self ):
88
84
"""Propose to the next person in the own preference list"""
89
- person = self .get_next_choice ()
85
+ person = self .next_choice
90
86
person .candidates .append (self )
91
87
self .pref_index += 1
92
88
@@ -99,33 +95,45 @@ def pick_preferred(self):
99
95
if person == self .partner :
100
96
break
101
97
elif person in self .candidates :
102
- self .set_partner ( person )
98
+ self .partner = person
103
99
break
104
100
105
- # Rejected candidates don't get a second chance. :(
101
+ # Rejected candidates don't get a second chance
106
102
self .candidates .clear ()
107
103
108
- def get_partner (self ):
109
- """Return the current partner"""
110
- return self .partner
104
+ @property
105
+ def partner (self ):
106
+ return self ._partner
107
+
108
+ # The call self.partner = person sets self._partner as person
109
+ # However, since engagement is symmetrical, self._partner._partner
110
+ # (which is then person._partner) also needs to be set to self
111
+ @partner .setter
112
+ def partner (self , person ):
113
+ """Set a person as the new partner and sets the partner of that
114
+ person as well"""
111
115
112
- def set_partner (self , person ):
113
- """Set a person as the new partner and run set_partner() on that person
114
- as well"""
115
116
# Do nothing if nothing would change
116
- if person != self .partner :
117
+ if person != self ._partner :
117
118
# Remove self from current partner
118
- if self .partner is not None :
119
- self .partner . partner = None
119
+ if self ._partner is not None :
120
+ self ._partner . _partner = None
120
121
121
122
# Set own and the other person's partner
122
- self .partner = person
123
- if self .partner is not None :
124
- self .partner . partner = self
123
+ self ._partner = person
124
+ if self ._partner is not None :
125
+ self ._partner . _partner = self
125
126
127
+ # This allows use of self.has_partner instead of self.has_partner()
128
+ @property
126
129
def has_partner (self ):
127
- """Determine whether this person currently has a partner or not"""
128
- return self .partner != None
130
+ """Determine whether this person currently has a partner or not."""
131
+ return self .partner is not None
132
+
133
+ # This allows the preferences to be printed more elegantly
134
+ def __str__ (self ):
135
+ return f'{ self .name } : { ", " .join (p .name for p in self .preference )} '
136
+
129
137
130
138
if __name__ == '__main__' :
131
139
main ()
0 commit comments