-
-
Notifications
You must be signed in to change notification settings - Fork 359
Improved stable marriage problem Python implementation #687
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
54f4a9b
833161c
5095802
8fc111e
3c32049
15d740e
ee63058
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,25 +1,19 @@ | ||
# 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: | ||
num_pairs = int(sys.argv[1]) | ||
else: | ||
num_pairs = 5 | ||
|
||
# There are only 26 possible names | ||
if num_pairs > 13: | ||
print('You can\' have more than 13 pairs.') | ||
return | ||
def main(): | ||
# 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[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: | ||
|
@@ -31,62 +25,64 @@ 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: | ||
Amaras marked this conversation as resolved.
Show resolved
Hide resolved
|
||
print('{}: {}'.format(man.name, ', '.join([ p.name for p in man.preference ]))) | ||
print(man) | ||
|
||
print() | ||
|
||
print('Preferences of the women:') | ||
for woman in women: | ||
print('{}: {}'.format(woman.name, ', '.join([ p.name for p in woman.preference ]))) | ||
print(woman) | ||
|
||
print('') | ||
print('\n') | ||
|
||
print('The algorithm gave this solution:') | ||
for man in men: | ||
print('{} + {}'.format(man.name, man.partner.name)) | ||
print(f'{man.name} + {man.partner.name}') | ||
|
||
def resolve(men, women): | ||
|
||
def stable_marriage(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 | ||
for woman in 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): | ||
@property | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think we were talking about these There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There are points both in favour and against properties. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'll be honest, I am not sure what an There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. A property is an intermediate-level concept in Python. It basically allows the access of some methods as instance attributes. They usually replace the more usual idiom A noun should be accessed as Properties are something that advanced Python programmers use whenever they need a getter, getter/setter, or deleter access on an instance attribute, but need some kind of obfuscation, recalculation on access, validation, or custom deletion. They are very useful tools, but should not be used everywhere. Hope it helps you, but I get why they are confusing for beginners with prior knowledge of other languages, as Python is kind of weird compared to C and descendants. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @Property is pretty well known, but in my opinion a bit of a hack (from the language side) and especially new programmers might not know about. I don't deny there are occasions in which they're very useful, but I think the purpose of the AAA is to be as clear as possible, even to new programers. And that, this is not. Especially because I think (though Berquist doesn't agree with me I think) that it's easy to remove them here. The There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I can agree for I can remove the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I cannot find an easy way to remove the partner setter either, so I guess that will be necessary. I guess this is one of those instances where as I said in the previous comment
So I guess we will all just have to live with it :P I am not quite sure if we then want to remove the other properties, they don't add too much but if we commit to using properties anyway they aren't as bad either. |
||
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.get_next_choice() | ||
person = self.next_choice | ||
person.candidates.append(self) | ||
self.pref_index += 1 | ||
|
||
|
@@ -99,33 +95,45 @@ 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. :( | ||
# 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 | ||
|
||
# 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 | ||
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: | ||
if person != self._partner: | ||
# Remove self from current partner | ||
if self.partner is not None: | ||
self.partner.partner = 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: | ||
self.partner.partner = self | ||
self._partner = person | ||
if self._partner is not None: | ||
self._partner._partner = self | ||
|
||
# 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 != None | ||
"""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() |
Uh oh!
There was an error while loading. Please reload this page.