Skip to content

Commit c9fb060

Browse files
feat: add gale shapley algorithm
1 parent e5dad3f commit c9fb060

File tree

1 file changed

+201
-0
lines changed

1 file changed

+201
-0
lines changed
Lines changed: 201 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,201 @@
1+
/**
2+
* @file
3+
* @brief [Gale-Shapley
4+
* Algorithm](https://en.wikipedia.org/wiki/Gale%E2%80%93Shapley_algorithm):
5+
* Stable matching between two sets.
6+
* @details
7+
* This implementation uses the Gale-Shapley algorithm to find stable matches
8+
* between two equally sized sets, given their preferences.
9+
*
10+
* **Gale Shapley Algorithm** aims to find a stable matching between two equally
11+
* sized sets of elements given an ordinal preference for each element. The
12+
* algorithm was introduced by David Gale and Lloyd Shapley in 1962.
13+
*
14+
* Reference:
15+
* - [Wikipedia](https://en.wikipedia.org/wiki/Gale%E2%80%93Shapley_algorithm)
16+
* - [Stable matching
17+
* problem](https://en.wikipedia.org/wiki/Stable_matching_problem)
18+
* - [Original
19+
* Paper](https://sites.math.washington.edu/~billey/classes/562.winter.2018/articles/Gale.Shapley.pdf)
20+
*
21+
* Math: Stable matching means no pair prefers each other over their current
22+
* match.
23+
*
24+
* @author [Mastermind-sap](https://github.com/Mastermind-sap)
25+
*/
26+
27+
#include <assert.h> /// for assert
28+
#include <stdio.h> /// for IO operations
29+
30+
#define N 4 // number of proposers and acceptors
31+
32+
/**
33+
* @brief Find the index of value in an array
34+
* @param arr array to search
35+
* @param size length of array
36+
* @param val value to find
37+
* @return index of value if found, -1 otherwise
38+
*/
39+
int index_of(int arr[], int size, int val)
40+
{
41+
for (int i = 0; i < size; i++)
42+
{
43+
if (arr[i] == val)
44+
return i;
45+
}
46+
return -1;
47+
}
48+
49+
/**
50+
* @brief Initialize matches and free status
51+
* @param matches array to store final matches
52+
* @param is_free_proposer array to check which proposer free
53+
* @param proposal_index array to keep track of next index to propose for each
54+
* proposer
55+
* @returns void
56+
*/
57+
void initialize(int matches[], int is_free_proposer[], int proposal_index[])
58+
{
59+
for (int i = 0; i < N; i++)
60+
{
61+
matches[i] = -1; // all acceptors unmatched
62+
is_free_proposer[i] = 1; // all proposers are free
63+
proposal_index[i] = 0; // start from first preference
64+
}
65+
}
66+
67+
/**
68+
* @brief Find the next free proposer
69+
* @param is_free_proposer array to check which proposer is free
70+
* @returns index of free proposer or -1 if none free
71+
*/
72+
int get_free_proposer(int is_free_proposer[])
73+
{
74+
for (int i = 0; i < N; i++)
75+
{
76+
if (is_free_proposer[i])
77+
return i;
78+
}
79+
return -1;
80+
}
81+
82+
/**
83+
* @brief Gale–Shapley stable matching algorithm
84+
* @param proposer_pref preferences of proposers
85+
* @param acceptor_pref preferences of acceptors
86+
* @param matches output: matches[j] = i means acceptor j is matched with
87+
* proposer i
88+
* @returns void
89+
*/
90+
void gale_shapley(int proposer_pref[N][N], int acceptor_pref[N][N],
91+
int matches[])
92+
{
93+
int is_free_proposer[N];
94+
int proposal_index[N];
95+
initialize(matches, is_free_proposer, proposal_index);
96+
97+
while (1)
98+
{
99+
int p = get_free_proposer(is_free_proposer); // free proposer
100+
if (p == -1)
101+
break; // no free proposer left
102+
103+
int s = proposer_pref[p][proposal_index[p]]; // acceptor to propose
104+
proposal_index[p]++;
105+
106+
int current_match = matches[s];
107+
108+
if (current_match == -1)
109+
{
110+
// acceptor is free
111+
matches[s] = p;
112+
is_free_proposer[p] = 0;
113+
}
114+
else
115+
{
116+
// acceptor already matched, check preference
117+
int rank_new = index_of(acceptor_pref[s], N, p);
118+
int rank_current = index_of(acceptor_pref[s], N, current_match);
119+
120+
if (rank_new < rank_current)
121+
{
122+
// acceptor prefers new proposer
123+
matches[s] = p;
124+
is_free_proposer[p] = 0;
125+
is_free_proposer[current_match] = 1; // old match becomes free
126+
}
127+
}
128+
}
129+
}
130+
131+
/**
132+
* @brief Print final matches
133+
* @param matches output: matches[j] = i means acceptor j is matched with
134+
* proposer i
135+
*/
136+
void print_matches(int matches[])
137+
{
138+
printf("Stable Matching Results:\n");
139+
for (int s = 0; s < N; s++)
140+
{
141+
printf("acceptor %d is matched with proposer %d\n", s, matches[s]);
142+
}
143+
}
144+
145+
/**
146+
* @brief Self-test for Gale-Shapley implementation
147+
* @returns void
148+
*/
149+
static void test()
150+
{
151+
// Test case 1:
152+
int proposer_pref1[N][N] = {
153+
{1, 0, 2, 3}, {2, 1, 3, 0}, {2, 1, 0, 3}, {3, 0, 1, 2}};
154+
int acceptor_pref1[N][N] = {
155+
{1, 0, 2, 3}, {3, 0, 1, 2}, {0, 2, 1, 3}, {1, 2, 0, 3}};
156+
int matches1[N];
157+
gale_shapley(proposer_pref1, acceptor_pref1, matches1);
158+
int expected1[N] = {3, 0, 2, 1};
159+
for (int i = 0; i < N; i++)
160+
{
161+
assert(matches1[i] == expected1[i]);
162+
}
163+
164+
// Test case 2:
165+
int proposer_pref2[N][N] = {
166+
{0, 1, 2, 3}, {0, 1, 2, 3}, {0, 1, 2, 3}, {0, 1, 2, 3}};
167+
int acceptor_pref2[N][N] = {
168+
{0, 1, 2, 3}, {1, 0, 2, 3}, {2, 1, 0, 3}, {3, 2, 1, 0}};
169+
int matches2[N];
170+
gale_shapley(proposer_pref2, acceptor_pref2, matches2);
171+
int expected2[N] = {0, 1, 2, 3};
172+
for (int i = 0; i < N; i++)
173+
{
174+
assert(matches2[i] == expected2[i]);
175+
}
176+
177+
// Test case 3:
178+
int proposer_pref3[N][N] = {
179+
{3, 2, 1, 0}, {3, 2, 1, 0}, {3, 2, 1, 0}, {3, 2, 1, 0}};
180+
int acceptor_pref3[N][N] = {
181+
{3, 2, 1, 0}, {2, 3, 1, 0}, {1, 2, 3, 0}, {0, 1, 2, 3}};
182+
int matches3[N];
183+
gale_shapley(proposer_pref3, acceptor_pref3, matches3);
184+
int expected3[N] = {3, 2, 1, 0};
185+
for (int i = 0; i < N; i++)
186+
{
187+
assert(matches3[i] == expected3[i]);
188+
}
189+
190+
printf("All Gale-Shapley tests have successfully passed!\n");
191+
}
192+
193+
/**
194+
* @brief Main function with test case
195+
* @returns 0 on exit
196+
*/
197+
int main()
198+
{
199+
test(); // Run Gale-Shapley self-test
200+
return 0;
201+
}

0 commit comments

Comments
 (0)