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