From c6095bf6f1e7227eac21809a7d96e7911a53cb31 Mon Sep 17 00:00:00 2001 From: Akash Shroff <63399889+akashvshroff@users.noreply.github.com> Date: Fri, 12 Jun 2020 16:56:01 +0530 Subject: [PATCH 01/10] Gale Shapley Algorithm Implementation of a Nobel prize-winning algorithm that determines a stable matching in a bipartite graph. --- graphs/gale_shapley_bigraph.py | 38 ++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) create mode 100644 graphs/gale_shapley_bigraph.py diff --git a/graphs/gale_shapley_bigraph.py b/graphs/gale_shapley_bigraph.py new file mode 100644 index 000000000000..94b9b2bed8e1 --- /dev/null +++ b/graphs/gale_shapley_bigraph.py @@ -0,0 +1,38 @@ +def stable_matching(n: int, men_preferences: list, women_preferences: list) -> list: + ''' + Finds the stable match in any bipartite graph, i.e a pairing where no 2 objects prefer each other over their partner. + Here marriage is used to make the variable names easier and so the algorithm can be intuitively understood. + The function accepts the preferences of the men and women (where both are named from 0 to n-1) and returns a list where index + position corresponds to the man and value at the index is the woman he is marrying. + E.g: + n = 4 + men_preferences = [[0, 1, 3, 2], [0, 2, 3, 1], [1, 0, 2, 3], [0, 3, 1, 2]] + women_preferences = [[3, 1, 2, 0], [3, 1, 0, 2], [0, 3, 1, 2], [1, 0, 3, 2]] + >>>print(stable_matching(n,men_preferences,women_preferences)) + [1,2,3,0] + P.S: Marriages are heterosexual since it is a bipartite graph where there must be 2 distinct sets of objects to be matched - i.e + patients and organ donors. + To better understand the algorithm, see also: + https://github.com/akashvshroff/Gale_Shapley_Stable_Matching (Detailed README). + https://www.youtube.com/watch?v=Qcv1IqHWAzg&t=13s (Numberphile YouTube Video). + ''' + unmarried_men = [i for i in range(n)] + man_spouse = [None for i in range(n)] + woman_spouse = [None for i in range(n)] + num_proposals = [0 for i in range(n)] + while unmarried_men: + man = unmarried_men[0] + his_preferences = men_preferences[man] + woman = his_preferences[num_proposals[man]] + num_proposals[man] += 1 + her_preferences = women_preferences[woman] + husb = woman_spouse[woman] + if husb != None: + if her_preferences.index(husb) > her_preferences.index(man): + woman_spouse[woman], man_spouse[man] = man, woman + unmarried_men.append(husb) + unmarried_men.remove(man) + else: + woman_spouse[woman], man_spouse[man] = man, woman + unmarried_men.remove(man) + return man_spouse From b05ee58abfe8bc096d88a428dcff15436193e628 Mon Sep 17 00:00:00 2001 From: Akash Shroff <63399889+akashvshroff@users.noreply.github.com> Date: Wed, 24 Jun 2020 21:23:37 +0530 Subject: [PATCH 02/10] Update graphs/gale_shapley_bigraph.py Co-authored-by: Christian Clauss --- graphs/gale_shapley_bigraph.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/graphs/gale_shapley_bigraph.py b/graphs/gale_shapley_bigraph.py index 94b9b2bed8e1..e813b6432f59 100644 --- a/graphs/gale_shapley_bigraph.py +++ b/graphs/gale_shapley_bigraph.py @@ -8,7 +8,7 @@ def stable_matching(n: int, men_preferences: list, women_preferences: list) -> l n = 4 men_preferences = [[0, 1, 3, 2], [0, 2, 3, 1], [1, 0, 2, 3], [0, 3, 1, 2]] women_preferences = [[3, 1, 2, 0], [3, 1, 0, 2], [0, 3, 1, 2], [1, 0, 3, 2]] - >>>print(stable_matching(n,men_preferences,women_preferences)) + >>> print(stable_matching(n,men_preferences,women_preferences)) [1,2,3,0] P.S: Marriages are heterosexual since it is a bipartite graph where there must be 2 distinct sets of objects to be matched - i.e patients and organ donors. From fe8a7a29c963d61e256da65e2067f01173b4d465 Mon Sep 17 00:00:00 2001 From: Akash Shroff <63399889+akashvshroff@users.noreply.github.com> Date: Wed, 24 Jun 2020 21:48:34 +0530 Subject: [PATCH 03/10] Fixed some flake8 issues. --- graphs/gale_shapley_bigraph.py | 33 +++++++++++++++++++-------------- 1 file changed, 19 insertions(+), 14 deletions(-) diff --git a/graphs/gale_shapley_bigraph.py b/graphs/gale_shapley_bigraph.py index e813b6432f59..88d6a34dac59 100644 --- a/graphs/gale_shapley_bigraph.py +++ b/graphs/gale_shapley_bigraph.py @@ -1,20 +1,25 @@ -def stable_matching(n: int, men_preferences: list, women_preferences: list) -> list: +def stable_matching(n: int, men_pref: list, women_pref: list) -> list: ''' - Finds the stable match in any bipartite graph, i.e a pairing where no 2 objects prefer each other over their partner. - Here marriage is used to make the variable names easier and so the algorithm can be intuitively understood. - The function accepts the preferences of the men and women (where both are named from 0 to n-1) and returns a list where index - position corresponds to the man and value at the index is the woman he is marrying. + Finds the stable match in any bipartite graph, i.e a pairing where no 2 + objects prefer each other over their partner. + Here marriage is used to make the variable names easier and so the algorithm + can be intuitively understood. + The function accepts the preferences of the men and women (where both are + named from 0 to n-1) and returns a list where index position corresponds + to the man and value at the index is the woman he is marrying. E.g: n = 4 - men_preferences = [[0, 1, 3, 2], [0, 2, 3, 1], [1, 0, 2, 3], [0, 3, 1, 2]] - women_preferences = [[3, 1, 2, 0], [3, 1, 0, 2], [0, 3, 1, 2], [1, 0, 3, 2]] - >>> print(stable_matching(n,men_preferences,women_preferences)) + men_pref = [[0, 1, 3, 2], [0, 2, 3, 1], [1, 0, 2, 3], [0, 3, 1, 2]] + women_pref = [[3, 1, 2, 0], [3, 1, 0, 2], [0, 3, 1, 2], [1, 0, 3, 2]] + >>> print(stable_matching(n,men_pref,women_pref)) [1,2,3,0] - P.S: Marriages are heterosexual since it is a bipartite graph where there must be 2 distinct sets of objects to be matched - i.e + P.S: Marriages are heterosexual since it is a bipartite graph where there + must + be 2 distinct sets of objects to be matched - i.e patients and organ donors. To better understand the algorithm, see also: - https://github.com/akashvshroff/Gale_Shapley_Stable_Matching (Detailed README). - https://www.youtube.com/watch?v=Qcv1IqHWAzg&t=13s (Numberphile YouTube Video). + https://github.com/akashvshroff/Gale_Shapley_Stable_Matching (README). + https://www.youtube.com/watch?v=Qcv1IqHWAzg&t=13s (Numberphile YouTube). ''' unmarried_men = [i for i in range(n)] man_spouse = [None for i in range(n)] @@ -22,12 +27,12 @@ def stable_matching(n: int, men_preferences: list, women_preferences: list) -> l num_proposals = [0 for i in range(n)] while unmarried_men: man = unmarried_men[0] - his_preferences = men_preferences[man] + his_preferences = men_pref[man] woman = his_preferences[num_proposals[man]] num_proposals[man] += 1 - her_preferences = women_preferences[woman] + her_preferences = women_pref[woman] husb = woman_spouse[woman] - if husb != None: + if husb is not None: if her_preferences.index(husb) > her_preferences.index(man): woman_spouse[woman], man_spouse[man] = man, woman unmarried_men.append(husb) From e196934723d18f53a10bb4e7de93d86cabaa6d5b Mon Sep 17 00:00:00 2001 From: Akash Shroff <63399889+akashvshroff@users.noreply.github.com> Date: Sat, 4 Jul 2020 16:12:05 +0530 Subject: [PATCH 04/10] Updated it to donors and recipients --- graphs/gale_shapley_bigraph.py | 60 +++++++++++++++------------------- 1 file changed, 27 insertions(+), 33 deletions(-) diff --git a/graphs/gale_shapley_bigraph.py b/graphs/gale_shapley_bigraph.py index 88d6a34dac59..b563b8dee75d 100644 --- a/graphs/gale_shapley_bigraph.py +++ b/graphs/gale_shapley_bigraph.py @@ -1,43 +1,37 @@ -def stable_matching(n: int, men_pref: list, women_pref: list) -> list: +def stable_matching(n: int, donor_pref: list, recipient_pref: list) -> list: ''' - Finds the stable match in any bipartite graph, i.e a pairing where no 2 + Finds the stable match in any bipartite graph, i.e a pairing where no 2 objects prefer each other over their partner. - Here marriage is used to make the variable names easier and so the algorithm - can be intuitively understood. - The function accepts the preferences of the men and women (where both are - named from 0 to n-1) and returns a list where index position corresponds - to the man and value at the index is the woman he is marrying. + The function accepts the preferences of the donors and recipients (where + both arecnamed from 0 to n-1) and returns a list where the index position + corresponds to the donor and value at the index is the recipient (of the organ). E.g: n = 4 - men_pref = [[0, 1, 3, 2], [0, 2, 3, 1], [1, 0, 2, 3], [0, 3, 1, 2]] - women_pref = [[3, 1, 2, 0], [3, 1, 0, 2], [0, 3, 1, 2], [1, 0, 3, 2]] - >>> print(stable_matching(n,men_pref,women_pref)) + donor_pref = [[0, 1, 3, 2], [0, 2, 3, 1], [1, 0, 2, 3], [0, 3, 1, 2]] + recipient_pref = [[3, 1, 2, 0], [3, 1, 0, 2], [0, 3, 1, 2], [1, 0, 3, 2]] + >>> print(stable_matching(n,donor_pref,recipient_pref)) [1,2,3,0] - P.S: Marriages are heterosexual since it is a bipartite graph where there - must - be 2 distinct sets of objects to be matched - i.e - patients and organ donors. To better understand the algorithm, see also: https://github.com/akashvshroff/Gale_Shapley_Stable_Matching (README). https://www.youtube.com/watch?v=Qcv1IqHWAzg&t=13s (Numberphile YouTube). ''' - unmarried_men = [i for i in range(n)] - man_spouse = [None for i in range(n)] - woman_spouse = [None for i in range(n)] - num_proposals = [0 for i in range(n)] - while unmarried_men: - man = unmarried_men[0] - his_preferences = men_pref[man] - woman = his_preferences[num_proposals[man]] - num_proposals[man] += 1 - her_preferences = women_pref[woman] - husb = woman_spouse[woman] - if husb is not None: - if her_preferences.index(husb) > her_preferences.index(man): - woman_spouse[woman], man_spouse[man] = man, woman - unmarried_men.append(husb) - unmarried_men.remove(man) + undonated_donors = [i for i in range(n)] + donor_record = [None for i in range(n)] # who the donor has donated to + recipient_record = [None for i in range(n)] # donor received from + num_donations = [0 for i in range(n)] + while undonated_donors: + donor = undonated_donors[0] + donor_preference = donor_pref[donor] + recipient = donor_preference[num_donations[donor]] + num_donations[donor] += 1 + recipient_preference = recipient_pref[recipient] + prev_donor = recipient_record[recipient] + if prev_donor is not None: + if recipient_preference.index(prev_donor) > recipient_preference.index(donor): + recipient_record[recipient], donor_record[donor] = donor, recipient + undonated_donors.append(prev_donor) + undonated_donors.remove(donor) else: - woman_spouse[woman], man_spouse[man] = man, woman - unmarried_men.remove(man) - return man_spouse + recipient_record[recipient], donor_record[donor] = donor, recipient + undonated_donors.remove(donor) + return donor_record From c18c55ecbdfc4616541fb5a1a1878c0f81444aa5 Mon Sep 17 00:00:00 2001 From: Akash Shroff <63399889+akashvshroff@users.noreply.github.com> Date: Sun, 5 Jul 2020 10:40:36 +0530 Subject: [PATCH 05/10] description changes Co-authored-by: Christian Clauss --- graphs/gale_shapley_bigraph.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/graphs/gale_shapley_bigraph.py b/graphs/gale_shapley_bigraph.py index b563b8dee75d..761e2ab13e02 100644 --- a/graphs/gale_shapley_bigraph.py +++ b/graphs/gale_shapley_bigraph.py @@ -3,7 +3,7 @@ def stable_matching(n: int, donor_pref: list, recipient_pref: list) -> list: Finds the stable match in any bipartite graph, i.e a pairing where no 2 objects prefer each other over their partner. The function accepts the preferences of the donors and recipients (where - both arecnamed from 0 to n-1) and returns a list where the index position + both are assigned numbers from 0 to n-1) and returns a list where the index position corresponds to the donor and value at the index is the recipient (of the organ). E.g: n = 4 From 9443cc4d87ad5ea982ae0a71c938969dc6380a0a Mon Sep 17 00:00:00 2001 From: Akash Shroff <63399889+akashvshroff@users.noreply.github.com> Date: Sun, 5 Jul 2020 10:40:50 +0530 Subject: [PATCH 06/10] description changes Co-authored-by: Christian Clauss --- graphs/gale_shapley_bigraph.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/graphs/gale_shapley_bigraph.py b/graphs/gale_shapley_bigraph.py index 761e2ab13e02..bb1a8fb1ec47 100644 --- a/graphs/gale_shapley_bigraph.py +++ b/graphs/gale_shapley_bigraph.py @@ -4,7 +4,7 @@ def stable_matching(n: int, donor_pref: list, recipient_pref: list) -> list: objects prefer each other over their partner. The function accepts the preferences of the donors and recipients (where both are assigned numbers from 0 to n-1) and returns a list where the index position - corresponds to the donor and value at the index is the recipient (of the organ). + corresponds to the donor and value at the index is the organ recipient. E.g: n = 4 donor_pref = [[0, 1, 3, 2], [0, 2, 3, 1], [1, 0, 2, 3], [0, 3, 1, 2]] From 5567fc5eb5dcd1f95329a8014ae93696e7c09971 Mon Sep 17 00:00:00 2001 From: Akash Shroff <63399889+akashvshroff@users.noreply.github.com> Date: Sun, 5 Jul 2020 10:41:02 +0530 Subject: [PATCH 07/10] description changes Co-authored-by: Christian Clauss --- graphs/gale_shapley_bigraph.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/graphs/gale_shapley_bigraph.py b/graphs/gale_shapley_bigraph.py index bb1a8fb1ec47..949cf8b68bea 100644 --- a/graphs/gale_shapley_bigraph.py +++ b/graphs/gale_shapley_bigraph.py @@ -15,7 +15,7 @@ def stable_matching(n: int, donor_pref: list, recipient_pref: list) -> list: https://github.com/akashvshroff/Gale_Shapley_Stable_Matching (README). https://www.youtube.com/watch?v=Qcv1IqHWAzg&t=13s (Numberphile YouTube). ''' - undonated_donors = [i for i in range(n)] + unmatched_donors = [i for i in range(n)] donor_record = [None for i in range(n)] # who the donor has donated to recipient_record = [None for i in range(n)] # donor received from num_donations = [0 for i in range(n)] From 054dff2712f3252d56838e2e480b46620d70e6dc Mon Sep 17 00:00:00 2001 From: Akash Shroff <63399889+akashvshroff@users.noreply.github.com> Date: Sun, 5 Jul 2020 10:48:02 +0530 Subject: [PATCH 08/10] Edited the line lengths --- graphs/gale_shapley_bigraph.py | 27 ++++++++++++++------------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/graphs/gale_shapley_bigraph.py b/graphs/gale_shapley_bigraph.py index 949cf8b68bea..334f43b200eb 100644 --- a/graphs/gale_shapley_bigraph.py +++ b/graphs/gale_shapley_bigraph.py @@ -3,8 +3,9 @@ def stable_matching(n: int, donor_pref: list, recipient_pref: list) -> list: Finds the stable match in any bipartite graph, i.e a pairing where no 2 objects prefer each other over their partner. The function accepts the preferences of the donors and recipients (where - both are assigned numbers from 0 to n-1) and returns a list where the index position - corresponds to the donor and value at the index is the organ recipient. + both are assigned numbers from 0 to n-1) and returns a list where the index + position corresponds to the donor and value at the index is the organ + recipient. E.g: n = 4 donor_pref = [[0, 1, 3, 2], [0, 2, 3, 1], [1, 0, 2, 3], [0, 3, 1, 2]] @@ -17,21 +18,21 @@ def stable_matching(n: int, donor_pref: list, recipient_pref: list) -> list: ''' unmatched_donors = [i for i in range(n)] donor_record = [None for i in range(n)] # who the donor has donated to - recipient_record = [None for i in range(n)] # donor received from + rec_record = [None for i in range(n)] # donor received from num_donations = [0 for i in range(n)] - while undonated_donors: - donor = undonated_donors[0] + while unmatched_donors: + donor = unmatched_donors[0] donor_preference = donor_pref[donor] recipient = donor_preference[num_donations[donor]] num_donations[donor] += 1 - recipient_preference = recipient_pref[recipient] - prev_donor = recipient_record[recipient] + rec_preference = recipient_pref[recipient] + prev_donor = rec_record[recipient] if prev_donor is not None: - if recipient_preference.index(prev_donor) > recipient_preference.index(donor): - recipient_record[recipient], donor_record[donor] = donor, recipient - undonated_donors.append(prev_donor) - undonated_donors.remove(donor) + if rec_preference.index(prev_donor) > rec_preference.index(donor): + rec_record[recipient], donor_record[donor] = donor, recipient + unmatched_donors.append(prev_donor) + unmatched_donors.remove(donor) else: - recipient_record[recipient], donor_record[donor] = donor, recipient - undonated_donors.remove(donor) + rec_record[recipient], donor_record[donor] = donor, recipient + unmatched_donors.remove(donor) return donor_record From 312b87d3b9ae318cc358eebcd0b618fefd469ae2 Mon Sep 17 00:00:00 2001 From: Christian Clauss Date: Sun, 5 Jul 2020 10:56:11 +0200 Subject: [PATCH 09/10] Update gale_shapley_bigraph.py --- graphs/gale_shapley_bigraph.py | 50 +++++++++++++++++++--------------- 1 file changed, 28 insertions(+), 22 deletions(-) diff --git a/graphs/gale_shapley_bigraph.py b/graphs/gale_shapley_bigraph.py index 334f43b200eb..d0d141258c4c 100644 --- a/graphs/gale_shapley_bigraph.py +++ b/graphs/gale_shapley_bigraph.py @@ -1,25 +1,29 @@ -def stable_matching(n: int, donor_pref: list, recipient_pref: list) -> list: - ''' - Finds the stable match in any bipartite graph, i.e a pairing where no 2 - objects prefer each other over their partner. - The function accepts the preferences of the donors and recipients (where - both are assigned numbers from 0 to n-1) and returns a list where the index - position corresponds to the donor and value at the index is the organ - recipient. - E.g: - n = 4 - donor_pref = [[0, 1, 3, 2], [0, 2, 3, 1], [1, 0, 2, 3], [0, 3, 1, 2]] - recipient_pref = [[3, 1, 2, 0], [3, 1, 0, 2], [0, 3, 1, 2], [1, 0, 3, 2]] - >>> print(stable_matching(n,donor_pref,recipient_pref)) - [1,2,3,0] +from typing import List + + +def stable_matching(donor_pref: List[int], recipient_pref: List[int]) -> List[int]: + """ + Finds the stable match in any bipartite graph, i.e a pairing where no 2 objects + prefer each other over their partner. The function accepts the preferences of + oegan donors and recipients (where both are assigned numbers from 0 to n-1) and + returns a list where the index position corresponds to the donor and value at the + index is the organ recipient. + To better understand the algorithm, see also: https://github.com/akashvshroff/Gale_Shapley_Stable_Matching (README). https://www.youtube.com/watch?v=Qcv1IqHWAzg&t=13s (Numberphile YouTube). - ''' - unmatched_donors = [i for i in range(n)] - donor_record = [None for i in range(n)] # who the donor has donated to - rec_record = [None for i in range(n)] # donor received from - num_donations = [0 for i in range(n)] + + >>> donor_pref = [[0, 1, 3, 2], [0, 2, 3, 1], [1, 0, 2, 3], [0, 3, 1, 2]] + >>> recipient_pref = [[3, 1, 2, 0], [3, 1, 0, 2], [0, 3, 1, 2], [1, 0, 3, 2]] + >>> print(stable_matching(donor_pref, recipient_pref)) + [1, 2, 3, 0] + """ + assert len(donor_pref) == len(recipient_pref) + n = len(donor_pref) + unmatched_donors = list(range(n)) + donor_record = [-1] * n # who the donor has donated to + rec_record = [-1] * n # who the recipient has received from + num_donations = [0] * n while unmatched_donors: donor = unmatched_donors[0] donor_preference = donor_pref[donor] @@ -27,12 +31,14 @@ def stable_matching(n: int, donor_pref: list, recipient_pref: list) -> list: num_donations[donor] += 1 rec_preference = recipient_pref[recipient] prev_donor = rec_record[recipient] - if prev_donor is not None: + if prev_donor is not -1: if rec_preference.index(prev_donor) > rec_preference.index(donor): - rec_record[recipient], donor_record[donor] = donor, recipient + rec_record[recipient] = donor + donor_record[donor] = recipient unmatched_donors.append(prev_donor) unmatched_donors.remove(donor) else: - rec_record[recipient], donor_record[donor] = donor, recipient + rec_record[recipient] = donor + donor_record[donor] = recipient unmatched_donors.remove(donor) return donor_record From 6883cd5b366765fb9f52d86ec2d03e240083521d Mon Sep 17 00:00:00 2001 From: Christian Clauss Date: Sun, 5 Jul 2020 11:07:33 +0200 Subject: [PATCH 10/10] Update gale_shapley_bigraph.py --- graphs/gale_shapley_bigraph.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/graphs/gale_shapley_bigraph.py b/graphs/gale_shapley_bigraph.py index d0d141258c4c..07a3922f86ec 100644 --- a/graphs/gale_shapley_bigraph.py +++ b/graphs/gale_shapley_bigraph.py @@ -31,7 +31,7 @@ def stable_matching(donor_pref: List[int], recipient_pref: List[int]) -> List[in num_donations[donor] += 1 rec_preference = recipient_pref[recipient] prev_donor = rec_record[recipient] - if prev_donor is not -1: + if prev_donor != -1: if rec_preference.index(prev_donor) > rec_preference.index(donor): rec_record[recipient] = donor donor_record[donor] = recipient