Function Repository Resource:

FindStableMatching

Source Notebook

Find a stable matching between two sets of elements with expressed preferences for each other

Contributed by: Daniel McDonald

ResourceFunction["FindStableMatching"][<|name1,1list1,1,name1,2list1,2,|>,<|name2,1list2,1,name2,2list2,2,|>]

finds a stable matching between the elements name1,i and name2,j based on the preferences expressed in lists listk,l.

ResourceFunction["FindStableMatching"][<|name1,1{list1,1,n1,1},name1,2{list1,2,n1,2},|>,<|name2,1{list2,1,n2,1},name2,2{list2,2,n2,2},|>]

gives each element namei,j capacity ni,j.

ResourceFunction["FindStableMatching"][{list1,1,list1,2,},{list2,1,list2,2,}]

automatically uses indices as names.

Details

Let A and B be sets such that each element of A has ranked all elements of B in order of preference, and each element of B has ranked all elements of A in order of preference. A stable matching (also known as a stable marriage) between A and B is a set of pairs such that for each pair {a,b}, we have aA, bB, and if a prefers b'B to b then b' does not prefer a to a', where a'A is matched with b'.
With a capacity ni,j, namei,j can match up to ni,j times.
ResourceFunction["FindStableMatching"] employs the Gale–Shapley algorithm to find a stable matching of maximum size. If sets A and B have the same size (i.e. same total capacity), then a stable matching pairing all elements (at their full capacity) is guaranteed to exist.

Examples

Basic Examples (2) 

Find a stable matching between two sets of preferences:

In[1]:=
ResourceFunction[
 "FindStableMatching"][<|"a" -> {1, 2, 3}, "b" -> {3, 2, 1}, "c" -> {2, 1, 3}|>, <|1 -> {"a", "b", "c"}, 2 -> {"b", "a", "c"}, 3 -> {"c", "b", "a"}|>]
Out[1]=

Find a stable matching between more complicated sets of preferences:

In[2]:=
prefs1 = <|1 -> {5, 7, 4, 3, 6, 8, 2, 1}, 2 -> {8, 5, 3, 4, 1, 6, 7, 2}, 3 -> {7, 8, 1, 3, 6, 5, 2, 4}, 4 -> {3, 5, 2, 4, 7, 8, 1, 6}, 5 -> {2, 6, 8, 7, 3, 1, 4, 5}, 6 -> {3, 7, 2, 8, 4, 5, 6, 1}, 7 -> {6, 7, 5, 3, 8, 1, 2, 4}, 8 -> {2, 7, 4, 6, 3, 5, 1, 8}|>;
prefs2 = <|1 -> {1, 3, 5, 2, 8, 4, 6, 7}, 2 -> {7, 3, 4, 1, 2, 6, 5, 8}, 3 -> {3, 5, 2, 6, 7, 1, 4, 8}, 4 -> {5, 7, 4, 3, 2, 8, 6, 1}, 5 -> {6, 2, 4, 7, 5, 3, 8, 1}, 6 -> {8, 3, 6, 1, 2, 5, 4, 7}, 7 -> {2, 4, 6, 5, 3, 8, 7, 1}, 8 -> {7, 5, 2, 8, 1, 3, 4, 6}|>;
ResourceFunction["FindStableMatching"][prefs1, prefs2]
Out[3]=

Scope (2) 

Assign capacities to certain elements:

In[4]:=
prefs1 = <|1 -> {6, 1, 4, 8, 3, 5, 7, 2}, 2 -> {7, 3, 6, 5, 8, 1, 4, 2}, 3 -> {{2, 8, 5, 6, 1, 3, 7, 4}, 2}, 4 -> {{6, 3, 5, 1, 8, 7, 2, 4}, 3}, 5 -> {2, 7, 5, 4, 3, 8, 6, 1}, 6 -> {{3, 5, 1, 8, 2, 6, 4, 7}, 3}, 7 -> {{2, 7, 1, 8, 6, 5, 4, 3}, 3}, 8 -> {{2, 1, 7, 4, 3, 6, 5, 8}, 3}, 9 -> {{4, 8, 3, 6, 5, 7, 2, 1}, 2}, 10 -> {{3, 2, 8, 7, 6, 1, 5, 4}, 3}|>;
prefs2 = <|1 -> {{2, 8, 10, 4, 7, 6, 3, 5, 1, 9}, 2}, 2 -> {2, 4, 6, 10, 5, 7, 8, 3, 1, 9}, 3 -> {{3, 7, 4, 10, 8, 5, 9, 1, 6, 2}, 2}, 4 -> {{6, 10, 7, 4, 1, 8, 2, 9, 5, 3}, 3}, 5 -> {4, 6, 7, 8, 10, 3, 9, 1, 5, 2}, 6 -> {{7, 6, 2, 8, 5, 10, 9, 4, 3, 1}, 3}, 7 -> {{6, 3, 4, 10, 7, 2, 5, 9, 8, 1}, 3}, 8 -> {{3, 10, 9, 6, 2, 1, 4, 7, 5, 8}, 3}|>;
ResourceFunction["FindStableMatching"][prefs1, prefs2]
Out[5]=

Enter preferences as lists:

In[6]:=
prefs1 = {{7, 3, 8, 9, 6, 4, 2, 1, 5}, {5, 4, 8, 3, 1, 2, 6, 7, 9}, {4, 8, 3, 9, 7, 5, 6, 1, 2}, {9, 7, 4, 2, 5, 8, 3, 1, 6}, {2, 6, 4, 9, 8, 7, 5, 1, 3}, {2, 7, 8, 6, 5, 3, 4, 1, 9}, {1, 6, 2, 3,
     8, 5, 4, 9, 7}, {5, 6, 9, 1, 2, 8, 4, 3, 7}, {6, 1, 4, 7, 5, 8, 3, 9, 2}};
prefs2 = {{3, 1, 5, 2, 8, 7, 6, 9, 4}, {9, 4, 8, 1, 7, 6, 3, 2, 5}, {3, 1, 8, 9, 5, 4, 2, 6, 7}, {8, 7, 5, 3, 2, 6, 4, 9, 1}, {6, 9, 2, 5, 1, 4, 7, 3, 8}, {2, 4, 5, 1, 6, 8, 3, 9, 7}, {9, 3, 8, 2,
     7, 5, 4, 6, 1}, {6, 3, 2, 1, 8, 4, 5, 9, 7}, {8, 2, 6, 4, 9, 1, 3, 7, 5}};
ResourceFunction["FindStableMatching"][prefs1, prefs2]
Out[7]=

Applications (2) 

Given an equal number of men and women looking for mates of the opposite gender, it is always possible to pair them into stable marriages:

In[8]:=
malePrefs = <|"Abe" -> {"Dee", "Cate", "Hope", "Allie", "Bea", "Eve", "Fay", "Ivy", "Jan", "Gina"}, "Bob" -> {"Gina", "Bea", "Hope", "Eve", "Fay", "Dee", "Cate", "Ivy", "Jan", "Allie"}, "Cal" -> {"Fay", "Hope", "Allie", "Jan", "Ivy", "Bea", "Gina", "Cate", "Eve", "Dee"}, "Dan" -> {"Gina", "Hope", "Eve", "Cate", "Jan", "Fay", "Bea", "Allie", "Dee", "Ivy"}, "Ed" -> {"Allie", "Bea", "Jan", "Dee", "Gina", "Ivy", "Hope", "Cate", "Eve", "Fay"}, "Fred" -> {"Cate", "Jan", "Allie", "Bea", "Ivy", "Gina", "Dee", "Eve", "Fay", "Hope"}, "Gary" -> {"Hope", "Jan", "Dee", "Fay", "Allie", "Gina", "Ivy", "Bea", "Cate", "Eve"}, "Hal" -> {"Dee", "Ivy", "Bea", "Hope", "Eve", "Gina", "Fay", "Jan",
      "Cate", "Allie"}, "Ian" -> {"Gina", "Hope", "Cate", "Bea", "Eve", "Ivy", "Jan", "Dee", "Fay", "Allie"}, "Jon" -> {"Hope", "Bea", "Eve", "Gina", "Dee", "Ivy", "Jan", "Allie", "Cate", "Fay"}|>;
femalePrefs = <|"Allie" -> {"Ian", "Ed", "Abe", "Hal", "Fred", "Bob", "Dan", "Cal", "Jon", "Gary"}, "Bea" -> {"Gary", "Ed", "Abe", "Bob", "Fred", "Ian", "Jon", "Hal", "Cal", "Dan"}, "Cate" -> {"Fred", "Abe", "Jon", "Cal", "Bob", "Ed", "Dan", "Hal", "Ian", "Gary"}, "Dee" -> {"Ed", "Hal", "Bob", "Dan", "Fred", "Cal", "Ian", "Gary", "Jon", "Abe"}, "Eve" -> {"Fred", "Bob", "Ian", "Hal", "Abe", "Ed", "Cal", "Dan", "Jon", "Gary"}, "Fay" -> {"Dan", "Abe", "Jon", "Hal", "Fred", "Cal", "Bob", "Ian", "Ed", "Gary"}, "Gina" -> {"Bob", "Jon", "Abe", "Cal", "Hal", "Fred", "Ed", "Ian", "Dan", "Gary"}, "Hope" -> {"Bob", "Gary", "Fred", "Jon", "Ed", "Cal", "Hal", "Abe",
      "Dan", "Ian"}, "Ivy" -> {"Fred", "Abe", "Ian", "Hal", "Cal", "Jon", "Ed", "Dan", "Gary", "Bob"}, "Jan" -> {"Fred", "Gary", "Cal", "Jon", "Dan", "Abe", "Hal", "Bob",
      "Ed", "Ian"}|>;
ResourceFunction["FindStableMatching"][malePrefs, femalePrefs]
Out[9]=

Assign graduating medical students to residency programs, each having a certain number of available slots:

In[10]:=
studentPrefs = <|"Abe" -> {"Crippen Mem. Hosp.", "Fort Carson Med. Cen.", "Las Venturas Hosp.", "All Saints Gen. Hosp.", "Angel Pine Med. Cen."}, "Bob" -> {"Crippen Mem. Hosp.", "All Saints Gen. Hosp.", "Angel Pine Med. Cen.", "Las Venturas Hosp.", "Fort Carson Med. Cen."}, "Cal" -> {"Las Venturas Hosp.", "Fort Carson Med. Cen.", "Crippen Mem. Hosp.", "All Saints Gen. Hosp.", "Angel Pine Med. Cen."}, "Dan" -> {"Las Venturas Hosp.", "Crippen Mem. Hosp.", "Angel Pine Med. Cen.", "All Saints Gen. Hosp.", "Fort Carson Med. Cen."}, "Ed" -> {"Angel Pine Med. Cen.", "Fort Carson Med. Cen.", "Crippen Mem. Hosp.", "All Saints Gen. Hosp.", "Las Venturas Hosp."}, "Fred" -> {"Las Venturas Hosp.", "All Saints Gen. Hosp.", "Crippen Mem. Hosp.", "Angel Pine Med. Cen.", "Fort Carson Med. Cen."}, "Gary" -> {"Crippen Mem. Hosp.", "Angel Pine Med. Cen.", "Las Venturas Hosp.", "Fort Carson Med. Cen.", "All Saints Gen. Hosp."}, "Hal" -> {"All Saints Gen. Hosp.", "Fort Carson Med. Cen.", "Las Venturas Hosp.", "Angel Pine Med. Cen.", "Crippen Mem. Hosp."}, "Ian" -> {"Angel Pine Med. Cen.", "All Saints Gen. Hosp.", "Crippen Mem. Hosp.", "Las Venturas Hosp.", "Fort Carson Med. Cen."}, "Jon" -> {"Crippen Mem. Hosp.", "Las Venturas Hosp.", "Fort Carson Med. Cen.", "Angel Pine Med. Cen.", "All Saints Gen. Hosp."}, "Allie" -> {"Crippen Mem. Hosp.", "Fort Carson Med. Cen.", "Angel Pine Med. Cen.", "Las Venturas Hosp.", "All Saints Gen. Hosp."}, "Bea" -> {"Fort Carson Med. Cen.", "Crippen Mem. Hosp.", "All Saints Gen. Hosp.", "Las Venturas Hosp.", "Angel Pine Med. Cen."}, "Cate" -> {"All Saints Gen. Hosp.", "Las Venturas Hosp.", "Angel Pine Med. Cen.", "Fort Carson Med. Cen.", "Crippen Mem. Hosp."}, "Dee" -> {"Crippen Mem. Hosp.", "Angel Pine Med. Cen.", "All Saints Gen. Hosp.", "Fort Carson Med. Cen.", "Las Venturas Hosp."}, "Eve" -> {"Angel Pine Med. Cen.", "Las Venturas Hosp.", "Fort Carson Med. Cen.", "All Saints Gen. Hosp.", "Crippen Mem. Hosp."}, "Fay" -> {"All Saints Gen. Hosp.", "Fort Carson Med. Cen.", "Las Venturas Hosp.", "Angel Pine Med. Cen.", "Crippen Mem. Hosp."}, "Gina" -> {"Las Venturas Hosp.", "Angel Pine Med. Cen.", "All Saints Gen. Hosp.", "Fort Carson Med. Cen.", "Crippen Mem. Hosp."}, "Hope" -> {"Las Venturas Hosp.", "All Saints Gen. Hosp.", "Crippen Mem. Hosp.", "Fort Carson Med. Cen.", "Angel Pine Med. Cen."}, "Ivy" -> {"Angel Pine Med. Cen.", "All Saints Gen. Hosp.", "Fort Carson Med. Cen.", "Crippen Mem. Hosp.", "Las Venturas Hosp."}, "Jan" -> {"Crippen Mem. Hosp.", "All Saints Gen. Hosp.", "Las Venturas Hosp.", "Fort Carson Med. Cen.", "Angel Pine Med. Cen."}|>;
hospitalPrefs = <|"All Saints Gen. Hosp." -> {{"Cal", "Jon", "Ed", "Eve", "Hope", "Fred", "Hal", "Dee", "Bob", "Abe", "Jan", "Dan",
       "Bea", "Ian", "Cate", "Gary", "Ivy", "Allie", "Fay", "Gina"}, 4}, "Angel Pine Med. Cen." -> {{"Allie", "Cate", "Ed", "Eve", "Gina", "Dee", "Abe", "Jan", "Cal", "Ian", "Bea", "Dan", "Ivy", "Fay", "Jon", "Hope", "Bob", "Gary", "Fred", "Hal"}, 3}, "Crippen Mem. Hosp." -> {{"Bea", "Jon", "Bob", "Fred", "Dee", "Hal", "Allie", "Cate", "Jan", "Ed", "Gina", "Ivy", "Abe", "Eve", "Fay", "Dan", "Hope", "Cal", "Ian", "Gary"}, 3}, "Fort Carson Med. Cen." -> {{"Cal", "Ivy", "Allie", "Cate", "Fay", "Jan", "Fred", "Eve", "Hal", "Ian", "Abe", "Gary", "Dee", "Bob",
       "Bea", "Gina", "Ed", "Dan", "Jon", "Hope"}, 4}, "Las Venturas Hosp." -> {{"Ivy", "Fay", "Cal", "Gina", "Allie", "Fred", "Bob", "Hope", "Abe", "Dee", "Eve", "Gary", "Hal", "Ian", "Jon", "Jan", "Dan", "Cate", "Ed", "Bea"}, 5}|>;
ResourceFunction["FindStableMatching"][studentPrefs, hospitalPrefs]
Out[11]=

Possible Issues (1) 

Reversing the order of the two preference inputs will still give a stable matching of the same size, but not necessarily the same one:

In[12]:=
prefs1 = {{1, 4, 7, 6, 3, 5, 8, 2}, {8, 1, 6, 3, 5, 7, 4, 2}, {4, 3, 2, 5, 7, 8, 6, 1}, {2, 3, 7, 4, 6, 1, 5, 8}, {8, 6, 7, 1, 2, 5, 4,
     3}, {7, 3, 5, 4, 2, 6, 1, 8}, {2, 6, 1, 5, 7, 3, 8, 4}, {3, 6, 4,
     5, 8, 2, 1, 7}, {8, 1, 6, 2, 4, 5, 3, 7}, {7, 3, 5, 4, 1, 2, 6, 8}};
prefs2 = {{4, 2, 10, 1, 9, 5, 3, 7, 6, 8}, {3, 5, 4, 9, 7, 8, 2, 1, 6,
     10}, {2, 1, 4, 6, 3, 10, 8, 7, 9, 5}, {2, 1, 3, 9, 10, 4, 5, 8, 6, 7}, {8, 1, 5, 2, 10, 9, 6, 4, 7, 3}, {3, 1, 10, 4, 6, 9, 7, 2, 5, 8}, {3, 9, 6, 7, 8, 5, 2, 10, 1, 4}, {6, 2, 7, 4, 9, 8, 1, 10, 3, 5}};
In[13]:=
Sort[ResourceFunction["FindStableMatching"][prefs1, prefs2]]
Out[13]=
In[14]:=
Sort[Reverse /@ ResourceFunction["FindStableMatching"][prefs2, prefs1]]
Out[14]=

Publisher

Daniel McDonald

Version History

  • 1.0.0 – 17 August 2021

Source Metadata

License Information