158 lines
5.1 KiB
Text
158 lines
5.1 KiB
Text
let men = [
|
|
"abe", ["abi";"eve";"cath";"ivy";"jan";"dee";"fay";"bea";"hope";"gay"];
|
|
"bob", ["cath";"hope";"abi";"dee";"eve";"fay";"bea";"jan";"ivy";"gay"];
|
|
"col", ["hope";"eve";"abi";"dee";"bea";"fay";"ivy";"gay";"cath";"jan"];
|
|
"dan", ["ivy";"fay";"dee";"gay";"hope";"eve";"jan";"bea";"cath";"abi"];
|
|
"ed", ["jan";"dee";"bea";"cath";"fay";"eve";"abi";"ivy";"hope";"gay"];
|
|
"fred", ["bea";"abi";"dee";"gay";"eve";"ivy";"cath";"jan";"hope";"fay"];
|
|
"gav", ["gay";"eve";"ivy";"bea";"cath";"abi";"dee";"hope";"jan";"fay"];
|
|
"hal", ["abi";"eve";"hope";"fay";"ivy";"cath";"jan";"bea";"gay";"dee"];
|
|
"ian", ["hope";"cath";"dee";"gay";"bea";"abi";"fay";"ivy";"jan";"eve"];
|
|
"jon", ["abi";"fay";"jan";"gay";"eve";"bea";"dee";"cath";"ivy";"hope"];
|
|
]
|
|
|
|
let women = [
|
|
"abi", ["bob";"fred";"jon";"gav";"ian";"abe";"dan";"ed";"col";"hal"];
|
|
"bea", ["bob";"abe";"col";"fred";"gav";"dan";"ian";"ed";"jon";"hal"];
|
|
"cath", ["fred";"bob";"ed";"gav";"hal";"col";"ian";"abe";"dan";"jon"];
|
|
"dee", ["fred";"jon";"col";"abe";"ian";"hal";"gav";"dan";"bob";"ed"];
|
|
"eve", ["jon";"hal";"fred";"dan";"abe";"gav";"col";"ed";"ian";"bob"];
|
|
"fay", ["bob";"abe";"ed";"ian";"jon";"dan";"fred";"gav";"col";"hal"];
|
|
"gay", ["jon";"gav";"hal";"fred";"bob";"abe";"col";"ed";"dan";"ian"];
|
|
"hope", ["gav";"jon";"bob";"abe";"ian";"dan";"hal";"ed";"col";"fred"];
|
|
"ivy", ["ian";"col";"hal";"gav";"fred";"bob";"abe";"ed";"jon";"dan"];
|
|
"jan", ["ed";"hal";"gav";"abe";"bob";"jon";"col";"ian";"fred";"dan"];
|
|
]
|
|
|
|
type woman_name = string
|
|
type man_name = string
|
|
|
|
type man =
|
|
{ m_name: man_name;
|
|
mutable free: bool;
|
|
women_rank: woman_name list;
|
|
has_proposed: (woman_name, unit) Hashtbl.t (* a set *)
|
|
}
|
|
|
|
type woman =
|
|
{ w_name: woman_name;
|
|
men_rank: man_name list;
|
|
mutable engaged: man_name option
|
|
}
|
|
|
|
|
|
let prefers w m1 m2 =
|
|
(* returns true if w has a lower (better) rank for m1 than m2 *)
|
|
let rec aux = function
|
|
| [] -> invalid_arg "rank_cmp"
|
|
| x::_ when x = m1 -> true
|
|
| x::_ when x = m2 -> false
|
|
| _::xs -> aux xs
|
|
in
|
|
aux w.men_rank
|
|
|
|
let take_while f lst =
|
|
let rec aux acc = function
|
|
| x::xs when f x -> aux (x::acc) xs
|
|
| _ -> List.rev acc
|
|
in
|
|
aux [] lst
|
|
|
|
let more_ranked_than name =
|
|
take_while ((<>) name)
|
|
|
|
let build_structs ~men ~women =
|
|
List.map (fun (name, rank) ->
|
|
{ m_name = name;
|
|
women_rank = rank;
|
|
free = true;
|
|
has_proposed = Hashtbl.create 42 }
|
|
) men,
|
|
List.map (fun (name, rank) ->
|
|
{ w_name = name;
|
|
men_rank = rank;
|
|
engaged = None }
|
|
) women
|
|
|
|
|
|
let _stable_matching ms ws =
|
|
let men_by_name = Hashtbl.create 42 in
|
|
List.iter (fun m -> Hashtbl.add men_by_name m.m_name m) ms;
|
|
let women_by_name = Hashtbl.create 42 in
|
|
List.iter (fun w -> Hashtbl.add women_by_name w.w_name w) ws;
|
|
try
|
|
while true do
|
|
(*TODO free men who still has some w to propose to *)
|
|
let m = List.find (fun m -> m.free) ms in
|
|
(* highest ranked woman who the man has not proposed to yet *)
|
|
let w_name =
|
|
List.find (fun w -> not (Hashtbl.mem m.has_proposed w)) m.women_rank in
|
|
Hashtbl.add m.has_proposed w_name ();
|
|
let w = Hashtbl.find women_by_name w_name in
|
|
match w.engaged with
|
|
| None -> (* w is free *)
|
|
(* (m, w) become engaged *)
|
|
w.engaged <- Some m.m_name;
|
|
m.free <- false
|
|
| Some m'_name -> (* some pair (m', w) already exists *)
|
|
if prefers w m.m_name m'_name
|
|
then begin (* w prefers m to m' *)
|
|
w.engaged <- Some m.m_name;
|
|
let m' = Hashtbl.find men_by_name m'_name in
|
|
m'.free <- true;
|
|
m.free <- false
|
|
end
|
|
done;
|
|
assert false
|
|
with Not_found -> ()
|
|
|
|
|
|
let stable_matching ~men ~women =
|
|
let ms, ws = build_structs ~men ~women in
|
|
_stable_matching ms ws;
|
|
let some = function Some v -> v | None -> "" in
|
|
List.map (fun w -> w.w_name, some w.engaged) ws
|
|
|
|
|
|
let is_stable ~men ~women eng =
|
|
let ms, ws = build_structs ~men ~women in
|
|
not (List.exists (fun (wn, mn) ->
|
|
let m = List.find (fun m -> m.m_name = mn) ms in
|
|
let prefered_women = more_ranked_than wn m.women_rank in
|
|
List.exists (fun pref_w ->
|
|
let w = List.find (fun w -> w.w_name = pref_w) ws in
|
|
let eng_m = List.assoc pref_w eng in
|
|
let prefered_men = more_ranked_than eng_m w.men_rank in
|
|
List.mem m.m_name prefered_men (* exists unstable engagement *)
|
|
) prefered_women
|
|
) eng)
|
|
|
|
|
|
let perturb_engagements eng =
|
|
Random.self_init();
|
|
let eng = Array.of_list eng in
|
|
let len = Array.length eng in
|
|
for n = 1 to 3 do
|
|
let i = Random.int len
|
|
and j = Random.int len in
|
|
let w1, m1 = eng.(i)
|
|
and w2, m2 = eng.(j) in
|
|
eng.(i) <- (w1, m2);
|
|
eng.(j) <- (w2, m1);
|
|
done;
|
|
Array.to_list eng
|
|
|
|
|
|
let print engs =
|
|
List.iter (fun (w,m) ->
|
|
Printf.printf " %4s is engaged with %s\n" w m) engs;
|
|
Printf.printf "# Engagements %s stable\n"
|
|
(if is_stable ~men ~women engs then "are" else "are not")
|
|
|
|
let () =
|
|
let engagements = stable_matching ~men ~women in
|
|
print engagements;
|
|
print_endline "========================";
|
|
let engagements = perturb_engagements engagements in
|
|
print engagements;
|
|
;;
|