11# Submitted by Marius Becker
2+ # Updated by Amaras
3+
24
3- import sys
45from random import shuffle
56from copy import copy
6- from string import ascii_uppercase
7+ from string import ascii_uppercase , ascii_lowercase
78
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
149
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
1913
2014 # 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 ] ]
2317
2418 # Set everyone's preferences
2519 for man in men :
@@ -31,62 +25,64 @@ def main():
3125 shuffle (woman .preference )
3226
3327 # Run the algorithm
34- resolve (men , women )
28+ stable_marriage (men , women )
3529
3630 # Print preferences and the result
31+ print ('Preferences of the men:' )
3732 for man in men :
38- print ('{}: {}' .format (man .name , ', ' .join ([ p .name for p in man .preference ])))
33+ print (man )
34+
35+ print ()
3936
37+ print ('Preferences of the women:' )
4038 for woman in women :
41- print ('{}: {}' . format ( woman . name , ', ' . join ([ p . name for p in woman . preference ])) )
39+ print (woman )
4240
43- print ('' )
41+ print ('\n ' )
4442
43+ print ('The algorithm gave this solution:' )
4544 for man in men :
46- print ('{ } + {}' . format ( man .name , man . partner .name ) )
45+ print (f' { man . name } + { man .partner .name } ' )
4746
48- def resolve (men , women ):
47+
48+ def stable_marriage (men , women ):
4949 """Finds pairs with stable marriages"""
50- cont = True
51- while cont :
50+
51+ while True :
5252 # Let every man without a partner propose to a woman
5353 for man in men :
54- if not man .has_partner () :
54+ if not man .has_partner :
5555 man .propose_to_next ()
5656
5757 # Let the women pick their favorites
5858 for woman in women :
5959 woman .pick_preferred ()
6060
6161 # 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+
6765
6866class Person :
69- name = None
70- preference = None
71- pref_index = 0
72- candidates = None
73- partner = None
7467
7568 def __init__ (self , name ):
7669 self .name = name
7770 self .preference = []
7871 self .candidates = []
72+ self .pref_index = 0
73+ self ._partner = None
7974
80- def get_next_choice (self ):
75+ @property
76+ def next_choice (self ):
8177 """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 :
8381 return None
8482
85- return self .preference [self .pref_index ]
86-
8783 def propose_to_next (self ):
8884 """Propose to the next person in the own preference list"""
89- person = self .get_next_choice ()
85+ person = self .next_choice
9086 person .candidates .append (self )
9187 self .pref_index += 1
9288
@@ -99,33 +95,45 @@ def pick_preferred(self):
9995 if person == self .partner :
10096 break
10197 elif person in self .candidates :
102- self .set_partner ( person )
98+ self .partner = person
10399 break
104100
105- # Rejected candidates don't get a second chance. :(
101+ # Rejected candidates don't get a second chance
106102 self .candidates .clear ()
107103
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"""
111115
112- def set_partner (self , person ):
113- """Set a person as the new partner and run set_partner() on that person
114- as well"""
115116 # Do nothing if nothing would change
116- if person != self .partner :
117+ if person != self ._partner :
117118 # 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
120121
121122 # 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
125126
127+ # This allows use of self.has_partner instead of self.has_partner()
128+ @property
126129 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+
129137
130138if __name__ == '__main__' :
131139 main ()
0 commit comments