Skip to content

Commit accd220

Browse files
Merge branch 'TheAlgorithms:master' into cstdint
2 parents 39cd383 + 901f231 commit accd220

File tree

1 file changed

+129
-0
lines changed

1 file changed

+129
-0
lines changed

greedy_algorithms/gale_shapley.cpp

Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
/**
2+
* @file
3+
* @brief [Gale Shapley Algorithm](https://en.wikipedia.org/wiki/Gale%E2%80%93Shapley_algorithm)
4+
* @details
5+
* This implementation utilizes the Gale-Shapley algorithm to find stable matches.
6+
*
7+
* **Gale Shapley Algorithm** aims to find a stable matching between two equally sized
8+
* sets of elements given an ordinal preference for each element. The algorithm was
9+
* introduced by David Gale and Lloyd Shapley in 1962.
10+
*
11+
* Reference:
12+
* [Wikipedia](https://en.wikipedia.org/wiki/Gale%E2%80%93Shapley_algorithm)
13+
* [Wikipedia](https://en.wikipedia.org/wiki/Stable_matching_problem)
14+
*
15+
* @author [B Karthik](https://github.com/BKarthik7)
16+
*/
17+
18+
#include <iostream> /// for std::u32int_t
19+
#include <vector> /// for std::vector
20+
#include <algorithm> /// for std::find
21+
#include <cassert> /// for assert
22+
23+
/**
24+
* @namespace
25+
* @brief Greedy Algorithms
26+
*/
27+
namespace greedy_algorithms {
28+
/**
29+
* @namespace
30+
* @brief Functions for the Gale-Shapley Algorithm
31+
*/
32+
namespace stable_matching {
33+
/**
34+
* @brief The main function that finds the stable matching between two sets of elements
35+
* using the Gale-Shapley Algorithm.
36+
* @note This doesn't work on negative preferences. the preferences should be continuous integers starting from
37+
* 0 to number of preferences - 1.
38+
* @param primary_preferences the preferences of the primary set should be a 2D vector
39+
* @param secondary_preferences the preferences of the secondary set should be a 2D vector
40+
* @returns matches the stable matching between the two sets
41+
*/
42+
std::vector<std::uint32_t> gale_shapley(const std::vector<std::vector<std::uint32_t>>& secondary_preferences, const std::vector<std::vector<std::uint32_t>>& primary_preferences) {
43+
std::uint32_t num_elements = secondary_preferences.size();
44+
std::vector<std::uint32_t> matches(num_elements, -1);
45+
std::vector<bool> is_free_primary(num_elements, true);
46+
std::vector<std::uint32_t> proposal_index(num_elements, 0); // Tracks the next secondary to propose for each primary
47+
48+
while (true) {
49+
int free_primary_index = -1;
50+
51+
// Find the next free primary
52+
for (std::uint32_t i = 0; i < num_elements; i++) {
53+
if (is_free_primary[i]) {
54+
free_primary_index = i;
55+
break;
56+
}
57+
}
58+
59+
// If no free primary is found, break the loop
60+
if (free_primary_index == -1) break;
61+
62+
// Get the next secondary to propose
63+
std::uint32_t secondary_to_propose = primary_preferences[free_primary_index][proposal_index[free_primary_index]];
64+
proposal_index[free_primary_index]++;
65+
66+
// Get the current match of the secondary
67+
std::uint32_t current_match = matches[secondary_to_propose];
68+
69+
// If the secondary is free, match them
70+
if (current_match == -1) {
71+
matches[secondary_to_propose] = free_primary_index;
72+
is_free_primary[free_primary_index] = false;
73+
} else {
74+
// Determine if the current match should be replaced
75+
auto new_proposer_rank = std::find(secondary_preferences[secondary_to_propose].begin(),
76+
secondary_preferences[secondary_to_propose].end(),
77+
free_primary_index);
78+
auto current_match_rank = std::find(secondary_preferences[secondary_to_propose].begin(),
79+
secondary_preferences[secondary_to_propose].end(),
80+
current_match);
81+
82+
// If the new proposer is preferred over the current match
83+
if (new_proposer_rank < current_match_rank) {
84+
matches[secondary_to_propose] = free_primary_index;
85+
is_free_primary[free_primary_index] = false;
86+
is_free_primary[current_match] = true; // Current match is now free
87+
}
88+
}
89+
}
90+
91+
return matches;
92+
}
93+
} // namespace stable_matching
94+
} // namespace greedy_algorithms
95+
96+
/**
97+
* @brief Self-test implementations
98+
* @returns void
99+
*/
100+
static void tests() {
101+
// Test Case 1
102+
std::vector<std::vector<std::uint32_t>> primary_preferences = {{0, 1, 2, 3}, {2, 1, 3, 0}, {1, 2, 0, 3}, {3, 0, 1, 2}};
103+
std::vector<std::vector<std::uint32_t>> secondary_preferences = {{1, 0, 2, 3}, {3, 0, 1, 2}, {0, 2, 1, 3}, {1, 2, 0, 3}};
104+
assert(greedy_algorithms::stable_matching::gale_shapley(secondary_preferences, primary_preferences) == std::vector<std::uint32_t>({0, 2, 1, 3}));
105+
106+
// Test Case 2
107+
primary_preferences = {{0, 2, 1, 3}, {2, 3, 0, 1}, {3, 1, 2, 0}, {2, 1, 0, 3}};
108+
secondary_preferences = {{1, 0, 2, 3}, {3, 0, 1, 2}, {0, 2, 1, 3}, {1, 2, 0, 3}};
109+
assert(greedy_algorithms::stable_matching::gale_shapley(secondary_preferences, primary_preferences) == std::vector<std::uint32_t>({0, 3, 1, 2}));
110+
111+
// Test Case 3
112+
primary_preferences = {{0, 1, 2}, {2, 1, 0}, {1, 2, 0}};
113+
secondary_preferences = {{1, 0, 2}, {2, 0, 1}, {0, 2, 1}};
114+
assert(greedy_algorithms::stable_matching::gale_shapley(secondary_preferences, primary_preferences) == std::vector<std::uint32_t>({0, 2, 1}));
115+
116+
// Test Case 4
117+
primary_preferences = {};
118+
secondary_preferences = {};
119+
assert(greedy_algorithms::stable_matching::gale_shapley(secondary_preferences, primary_preferences) == std::vector<std::uint32_t>({}));
120+
}
121+
122+
/**
123+
* @brief Main function
124+
* @returns 0 on exit
125+
*/
126+
int main() {
127+
tests(); // Run self-test implementations
128+
return 0;
129+
}

0 commit comments

Comments
 (0)