-
-
Notifications
You must be signed in to change notification settings - Fork 5.7k
feat: Add GaleShapley in a new folder Greedy #1714
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
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 | ||||
---|---|---|---|---|---|---|
@@ -0,0 +1,78 @@ | ||||||
/** | ||||||
* The Gale-Shapley algorithm is used to find a stable matching between two sets | ||||||
* of equal size (e.g., donors and recipients) where no two elements prefer each | ||||||
* other over their current partners. It ensures a stable matching by proposing | ||||||
* from one side to the other until all are matched. | ||||||
* | ||||||
* Complexity: | ||||||
* Worst-case performance O(n^2) | ||||||
* Best-case performance O(n^2) | ||||||
* | ||||||
* Reference: | ||||||
* https://en.wikipedia.org/wiki/Stable_marriage_problem | ||||||
* https://www.youtube.com/watch?v=Qcv1IqHWAzg (Numberphile) | ||||||
* | ||||||
*/ | ||||||
|
||||||
/** | ||||||
* | ||||||
* @param {number[][]} donorPref Preferences of donors, where each donor has an ordered list of recipients. | ||||||
* @param {number[][]} recipientPref Preferences of recipients, where each recipient has an ordered list of donors. | ||||||
* @returns {number[]} Array where the index is the donor, and the value at the index is the matched recipient. | ||||||
* | ||||||
* @example | ||||||
* const donorPref = [[0, 1, 3, 2], [0, 2, 3, 1], [1, 0, 2, 3], [0, 3, 1, 2]]; | ||||||
* const recipientPref = [[3, 1, 2, 0], [3, 1, 0, 2], [0, 3, 1, 2], [1, 0, 3, 2]]; | ||||||
* stableMatching(donorPref, recipientPref); // Output: [1, 2, 3, 0] | ||||||
*/ | ||||||
function stableMatching(donorPref, recipientPref) { | ||||||
// Initialize the number of donors and create a list of unmatched donors | ||||||
let n = donorPref.length | ||||||
let unmatchedDonors = Array.from({ length: n }, (_, i) => i) | ||||||
|
||||||
// Records of which recipient each donor is paired with and vice versa | ||||||
let donorRecord = Array(n).fill(-1) // Donor to recipient | ||||||
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'd compute |
||||||
let recRecord = Array(n).fill(-1) // Recipient to donor | ||||||
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 also think it'd be idiomatic to use |
||||||
|
||||||
// Array to track how many recipients each donor has proposed to | ||||||
let numDonations = Array(n).fill(0) | ||||||
|
||||||
// While there are unmatched donors | ||||||
while (unmatchedDonors.length > 0) { | ||||||
// Take the first unmatched donor | ||||||
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. Don't. Take the last one instead; use the array as a stack (you could also use a queue, but why do that when a stack suffices). If you take the first one, you have to do a costly |
||||||
let donor = unmatchedDonors[0] | ||||||
let donorPreference = donorPref[donor] | ||||||
|
||||||
// Find the next recipient this donor prefers | ||||||
let recipient = donorPreference[numDonations[donor]] | ||||||
numDonations[donor] += 1 | ||||||
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.
Suggested change
|
||||||
|
||||||
// Get recipient's preference list and check the current match | ||||||
let recPreference = recipientPref[recipient] | ||||||
let prevDonor = recRecord[recipient] | ||||||
|
||||||
// If recipient is already matched, check if they prefer the new donor | ||||||
if (prevDonor !== -1) { | ||||||
if (recPreference.indexOf(prevDonor) > recPreference.indexOf(donor)) { | ||||||
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. This also wrecks time complexity by adding a linear factor. Instead you should precompute the inverse mappings initially as lookup tables (this is what's responsible for the |
||||||
// If the new donor is preferred, match them and unmatch the previous donor | ||||||
recRecord[recipient] = donor | ||||||
donorRecord[donor] = recipient | ||||||
unmatchedDonors.push(prevDonor) | ||||||
unmatchedDonors.splice(unmatchedDonors.indexOf(donor), 1) // Remove the current donor from unmatched | ||||||
} | ||||||
} else { | ||||||
// If the recipient is not matched, pair them with the current donor | ||||||
recRecord[recipient] = donor | ||||||
donorRecord[donor] = recipient | ||||||
unmatchedDonors.splice(unmatchedDonors.indexOf(donor), 1) // Remove the current donor from unmatched | ||||||
} | ||||||
} | ||||||
|
||||||
return donorRecord | ||||||
} | ||||||
|
||||||
// // Example usage: | ||||||
// const donorPref = [[0, 1, 3, 2], [0, 2, 3, 1], [1, 0, 2, 3], [0, 3, 1, 2]]; | ||||||
// const recipientPref = [[3, 1, 2, 0], [3, 1, 0, 2], [0, 3, 1, 2], [1, 0, 3, 2]]; | ||||||
// console.log(stableMatching(donorPref, recipientPref)); // Output: [1, 2, 3, 0] | ||||||
export { stableMatching } |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
import { expect, test } from 'vitest' | ||
import { stableMatching } from '../GaleShapley' | ||
|
||
test('Test Case 1', () => { | ||
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 test case structure like this isn't helpful. Please see https://github.com/TheAlgorithms/TypeScript/blob/master/CONTRIBUTING.md#writing-good-tests (it's for the TS repo, but translates to JS just as well). TL;DR: Use a |
||
const donorPref = [ | ||
[0, 1, 3, 2], | ||
[0, 2, 3, 1], | ||
[1, 0, 2, 3], | ||
[0, 3, 1, 2] | ||
] | ||
const recipientPref = [ | ||
[3, 1, 2, 0], | ||
[3, 1, 0, 2], | ||
[0, 3, 1, 2], | ||
[1, 0, 3, 2] | ||
] | ||
expect(stableMatching(donorPref, recipientPref)).toEqual([1, 2, 3, 0]) | ||
}) | ||
test('Test Case 2', () => { | ||
const donorPref = [ | ||
[0, 1, 2], | ||
[0, 1, 2], | ||
[0, 1, 2] | ||
] | ||
const recipientPref = [ | ||
[0, 1, 2], | ||
[0, 1, 2], | ||
[0, 1, 2] | ||
] | ||
expect(stableMatching(donorPref, recipientPref)).toEqual([0, 1, 2]) | ||
}) | ||
test('Test Case 3', () => { | ||
const donorPref = [] | ||
const recipientPref = [] | ||
expect(stableMatching(donorPref, recipientPref)).toEqual([]) | ||
}) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd use this array as a stack because that's efficient.