From 54f4a9bf9e1b3b957ce4a107f3b7dcc1c6dcab81 Mon Sep 17 00:00:00 2001 From: Amaras Date: Wed, 6 May 2020 20:42:15 +0200 Subject: [PATCH 1/5] Slightly changed Galey-Shapely Py implementation --- CONTRIBUTORS.md | 1 + .../code/python/stable_marriage.py | 86 ++++++++++--------- 2 files changed, 48 insertions(+), 39 deletions(-) diff --git a/CONTRIBUTORS.md b/CONTRIBUTORS.md index a9d53d836..805139a1c 100644 --- a/CONTRIBUTORS.md +++ b/CONTRIBUTORS.md @@ -48,3 +48,4 @@ This file lists everyone, who contributed to this repo and wanted to show up her - dovisutu - Antetokounpo - Akash Dhiman +- Amaras diff --git a/contents/stable_marriage_problem/code/python/stable_marriage.py b/contents/stable_marriage_problem/code/python/stable_marriage.py index 8271e9b4e..e75c9e191 100644 --- a/contents/stable_marriage_problem/code/python/stable_marriage.py +++ b/contents/stable_marriage_problem/code/python/stable_marriage.py @@ -1,25 +1,30 @@ # Submitted by Marius Becker +# Updated by Amaras import sys from random import shuffle from copy import copy -from string import ascii_uppercase +from string import ascii_uppercase, ascii_lowercase + def main(): # Set this to however many men and women you want - if len(sys.argv) > 1: + try: num_pairs = int(sys.argv[1]) - else: + except (IndexError, ValueError): + # If you either did not set how many pairs you wanted or you + # did not set it as a number, use default value of 5 num_pairs = 5 - # There are only 26 possible names - if num_pairs > 13: - print('You can\' have more than 13 pairs.') + + # There are only 26 possible names for each sex + if num_pairs > 26: + print("You can't have more than 26 pairs.") return # Create all Person objects - men = [ Person(name) for name in ascii_uppercase[0:num_pairs] ] - women = [ Person(name) for name in ascii_uppercase[num_pairs:num_pairs*2] ] + men = [Person(name) for name in ascii_uppercase[:num_pairs]] + women = [Person(name) for name in ascii_lowercase[:num_pairs]] # Set everyone's preferences for man in men: @@ -35,23 +40,25 @@ def main(): # Print preferences and the result for man in men: - print('{}: {}'.format(man.name, ', '.join([ p.name for p in man.preference ]))) + print(f"{man.name}: {', '.join((p.name for p in man.preference))}") + + print('') for woman in women: - print('{}: {}'.format(woman.name, ', '.join([ p.name for p in woman.preference ]))) + print(f"{woman.name}: {', '.join((p.name for p in woman.preference))}") - print('') + print('\n') for man in men: - print('{} + {}'.format(man.name, man.partner.name)) + print(f'{man.name} + {man.partner.name}') def resolve(men, women): """Finds pairs with stable marriages""" - cont = True - while cont: + + while True: # Let every man without a partner propose to a woman for man in men: - if not man.has_partner(): + if not man.has_partner: man.propose_to_next() # Let the women pick their favorites @@ -59,25 +66,20 @@ def resolve(men, women): woman.pick_preferred() # Continue only when someone is still left without a partner - cont = False - for man in men: - if not man.has_partner(): - cont = True - break + if all((man.has_partner for man in men)): + return + class Person: - name = None - preference = None - pref_index = 0 - candidates = None - partner = None def __init__(self, name): self.name = name self.preference = [] self.candidates = [] + self.pref_index = 0 + self._partner = None - def get_next_choice(self): + def next_choice(self): """Return the next person in the own preference list""" if self.pref_index >= len(self.preference): return None @@ -86,7 +88,7 @@ def get_next_choice(self): def propose_to_next(self): """Propose to the next person in the own preference list""" - person = self.get_next_choice() + person = self.next_choice() person.candidates.append(self) self.pref_index += 1 @@ -99,33 +101,39 @@ def pick_preferred(self): if person == self.partner: break elif person in self.candidates: - self.set_partner(person) + self.partner = person break # Rejected candidates don't get a second chance. :( self.candidates.clear() - def get_partner(self): - """Return the current partner""" - return self.partner + @property + def partner(self): + return self._partner + + # This allows one to change both self.partner and person.partner + # with the simple call: "self.partner = person" + @partner.setter + def partner(self, person): + """Set a person as the new partner and sets the partner of that + person as well""" - def set_partner(self, person): - """Set a person as the new partner and run set_partner() on that person - as well""" # Do nothing if nothing would change if person != self.partner: # Remove self from current partner if self.partner is not None: - self.partner.partner = None + self._partner._partner = None # Set own and the other person's partner - self.partner = person + self._partner = person if self.partner is not None: - self.partner.partner = self + self._partner._partner = self + # This allows use of self.has_parnter instead of self.has_partner() + @property def has_partner(self): - """Determine whether this person currently has a partner or not""" - return self.partner != None + """Determine whether this person currently has a partner or not.""" + return self.partner is not None if __name__ == '__main__': main() From 833161ce103bf8603e2a16c8024e524f8ff26dc7 Mon Sep 17 00:00:00 2001 From: Amaras Date: Thu, 7 May 2020 07:31:45 +0200 Subject: [PATCH 2/5] 'next_partner' should be aproperty --- .../stable_marriage_problem/code/python/stable_marriage.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/contents/stable_marriage_problem/code/python/stable_marriage.py b/contents/stable_marriage_problem/code/python/stable_marriage.py index e75c9e191..68af2becb 100644 --- a/contents/stable_marriage_problem/code/python/stable_marriage.py +++ b/contents/stable_marriage_problem/code/python/stable_marriage.py @@ -79,6 +79,7 @@ def __init__(self, name): self.pref_index = 0 self._partner = None + @property def next_choice(self): """Return the next person in the own preference list""" if self.pref_index >= len(self.preference): @@ -88,7 +89,7 @@ def next_choice(self): def propose_to_next(self): """Propose to the next person in the own preference list""" - person = self.next_choice() + person = self.next_choice person.candidates.append(self) self.pref_index += 1 From 8fc111e2d2e9a6653c724009772af2874eb32512 Mon Sep 17 00:00:00 2001 From: Amaras Date: Sun, 31 May 2020 10:11:15 +0200 Subject: [PATCH 3/5] Addressed review and fixed typos --- .../code/python/stable_marriage.py | 24 +++++++++++-------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/contents/stable_marriage_problem/code/python/stable_marriage.py b/contents/stable_marriage_problem/code/python/stable_marriage.py index 68af2becb..12df743de 100644 --- a/contents/stable_marriage_problem/code/python/stable_marriage.py +++ b/contents/stable_marriage_problem/code/python/stable_marriage.py @@ -16,7 +16,6 @@ def main(): # did not set it as a number, use default value of 5 num_pairs = 5 - # There are only 26 possible names for each sex if num_pairs > 26: print("You can't have more than 26 pairs.") @@ -36,23 +35,27 @@ def main(): shuffle(woman.preference) # Run the algorithm - resolve(men, women) + stable_marriage(men, women) # Print preferences and the result + print('Preferences of the men:') for man in men: - print(f"{man.name}: {', '.join((p.name for p in man.preference))}") + print(f'{man.name}: {", ".join((p.name for p in man.preference))}') - print('') + print() + print('Preferences of the women:') for woman in women: - print(f"{woman.name}: {', '.join((p.name for p in woman.preference))}") + print(f'{woman.name}: {", ".join((p.name for p in woman.preference))}') print('\n') + print('The algorithm gave this solution:') for man in men: print(f'{man.name} + {man.partner.name}') -def resolve(men, women): + +def stable_marriage(men, women): """Finds pairs with stable marriages""" while True: @@ -120,21 +123,22 @@ def partner(self, person): person as well""" # Do nothing if nothing would change - if person != self.partner: + if person != self._partner: # Remove self from current partner - if self.partner is not None: + if self._partner is not None: self._partner._partner = None # Set own and the other person's partner self._partner = person - if self.partner is not None: + if self._partner is not None: self._partner._partner = self - # This allows use of self.has_parnter instead of self.has_partner() + # This allows use of self.has_partner instead of self.has_partner() @property def has_partner(self): """Determine whether this person currently has a partner or not.""" return self.partner is not None + if __name__ == '__main__': main() From 3c32049874306d5e8404666ee992d83c53dd150e Mon Sep 17 00:00:00 2001 From: Amaras Date: Wed, 1 Jul 2020 22:46:50 +0200 Subject: [PATCH 4/5] Removed input requirement, and explained the partner.setter effect a bit more --- .../code/python/stable_marriage.py | 23 ++++++------------- 1 file changed, 7 insertions(+), 16 deletions(-) diff --git a/contents/stable_marriage_problem/code/python/stable_marriage.py b/contents/stable_marriage_problem/code/python/stable_marriage.py index 12df743de..14d097f71 100644 --- a/contents/stable_marriage_problem/code/python/stable_marriage.py +++ b/contents/stable_marriage_problem/code/python/stable_marriage.py @@ -1,25 +1,15 @@ # Submitted by Marius Becker # Updated by Amaras -import sys + from random import shuffle from copy import copy from string import ascii_uppercase, ascii_lowercase def main(): - # Set this to however many men and women you want - try: - num_pairs = int(sys.argv[1]) - except (IndexError, ValueError): - # If you either did not set how many pairs you wanted or you - # did not set it as a number, use default value of 5 - num_pairs = 5 - - # There are only 26 possible names for each sex - if num_pairs > 26: - print("You can't have more than 26 pairs.") - return + # Set this to however many men and women you want, up to 26 + num_pairs = 5 # Create all Person objects men = [Person(name) for name in ascii_uppercase[:num_pairs]] @@ -108,15 +98,16 @@ def pick_preferred(self): self.partner = person break - # Rejected candidates don't get a second chance. :( + # Rejected candidates don't get a second chance self.candidates.clear() @property def partner(self): return self._partner - # This allows one to change both self.partner and person.partner - # with the simple call: "self.partner = person" + # The call self.partner = person sets self._partner as person + # However, since engagement is symmetrical, self._partner._partner + # (which is then person._partner) also needs to be set to self @partner.setter def partner(self, person): """Set a person as the new partner and sets the partner of that From 15d740ec1c251e5b8b4fa66af97a3393913d3517 Mon Sep 17 00:00:00 2001 From: Amaras Date: Wed, 1 Jul 2020 23:07:59 +0200 Subject: [PATCH 5/5] Cleaned the code a bit more, added a __str__ dunder --- .../code/python/stable_marriage.py | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/contents/stable_marriage_problem/code/python/stable_marriage.py b/contents/stable_marriage_problem/code/python/stable_marriage.py index 14d097f71..9302f93e2 100644 --- a/contents/stable_marriage_problem/code/python/stable_marriage.py +++ b/contents/stable_marriage_problem/code/python/stable_marriage.py @@ -30,13 +30,13 @@ def main(): # Print preferences and the result print('Preferences of the men:') for man in men: - print(f'{man.name}: {", ".join((p.name for p in man.preference))}') + print(man) print() print('Preferences of the women:') for woman in women: - print(f'{woman.name}: {", ".join((p.name for p in woman.preference))}') + print(woman) print('\n') @@ -75,11 +75,11 @@ def __init__(self, name): @property def next_choice(self): """Return the next person in the own preference list""" - if self.pref_index >= len(self.preference): + try: + return self.preference[self.pref_index] + except IndexError: return None - return self.preference[self.pref_index] - def propose_to_next(self): """Propose to the next person in the own preference list""" person = self.next_choice @@ -130,6 +130,10 @@ def has_partner(self): """Determine whether this person currently has a partner or not.""" return self.partner is not None + # This allows the preferences to be printed more elegantly + def __str__(self): + return f'{self.name}: {", ".join(p.name for p in self.preference)}' + if __name__ == '__main__': main()