Skip to content
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

feat: Add GaleShapley in a new folder Greedy #1714

Open
wants to merge 2 commits into
base: master
Choose a base branch
from

Conversation

BKarthik7
Copy link

Open in Gitpod know more

Describe your change:

  • Add an algorithm?
  • Documentation change?

Checklist:

  • I have read CONTRIBUTING.md.
  • This pull request is all my own work -- I have not plagiarized.
  • I know that pull requests will not be merged if they fail the automated tests.
  • This PR only changes one algorithm file. To ease review, please open separate PRs for separate algorithms.
  • All filenames should use the UpperCamelCase (PascalCase) style. There should be no spaces in filenames.
    Example:UserProfile.js is allowed but userprofile.js,Userprofile.js,user-Profile.js,userProfile.js are not
  • All new algorithms have a URL in their comments that points to Wikipedia or another similar explanation.

@codecov-commenter
Copy link

Codecov Report

All modified and coverable lines are covered by tests ✅

Project coverage is 84.71%. Comparing base (5b17ea1) to head (43b1f01).

Additional details and impacted files
@@            Coverage Diff             @@
##           master    #1714      +/-   ##
==========================================
+ Coverage   84.65%   84.71%   +0.06%     
==========================================
  Files         378      379       +1     
  Lines       19744    19822      +78     
  Branches     2952     2957       +5     
==========================================
+ Hits        16715    16793      +78     
  Misses       3029     3029              

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

@BKarthik7
Copy link
Author

@raklaptudirm can you please review

import { expect, test } from 'vitest'
import { stableMatching } from '../GaleShapley'

test('Test Case 1', () => {
Copy link
Collaborator

Choose a reason for hiding this comment

The 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 describe block for Gale-Shapley and it.each for individual test cases if you have no descriptive labels for them.

* stableMatching(donorPref, recipientPref); // Output: [1, 2, 3, 0]
*/
function stableMatching(donorPref, recipientPref) {
// Initialize the number of donors and create a list of unmatched donors
Copy link
Collaborator

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.

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
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd compute donorRecord at the end (or not at all even and instead just return recRecord and document that because the two are equivalent). That way you save yourself having to remember to update it during the algorithm, and the constant factor of the $O(n^2)$ many operations is better.


// While there are unmatched donors
while (unmatchedDonors.length > 0) {
// Take the first unmatched donor
Copy link
Collaborator

Choose a reason for hiding this comment

The 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 $O(n)$ unshift / splice later on to remove it (arguably you could also swap with the last one and pop, but that's just unnecessary complexity when you can just pop here), which wrecks your time complexity: You get $O(n^3)$ instead of $O(n^2)$.


// Find the next recipient this donor prefers
let recipient = donorPreference[numDonations[donor]]
numDonations[donor] += 1
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
numDonations[donor] += 1
numDonations[donor]++


// Records of which recipient each donor is paired with and vice versa
let donorRecord = Array(n).fill(-1) // Donor to recipient
let recRecord = Array(n).fill(-1) // Recipient to donor
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I also think it'd be idiomatic to use null rather than -1 to signal the absence of a value.


// If recipient is already matched, check if they prefer the new donor
if (prevDonor !== -1) {
if (recPreference.indexOf(prevDonor) > recPreference.indexOf(donor)) {
Copy link
Collaborator

Choose a reason for hiding this comment

The 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 $O(n^2)$ best case): For each receiver, compute the mapping from donor -> preference ahead of time.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants