2016 Update

This commit is contained in:
Tina Müller 2016-12-05 22:15:40 +01:00
parent 948b86eafa
commit dcf5d15da3
7965 changed files with 139854 additions and 31002 deletions

View file

@ -1,12 +1,14 @@
Solve the [[wp:Stable marriage problem|Stable marriage problem]] using the Gale/Shapley algorithm.
'''Problem description'''<br>
Given an equal number of men and women to be paired for marriage, each man ranks all the women in order of his preference and each women ranks all the men in order of her preference.
A stable set of engagements for marriage is one where no man prefers a women over the one he is engaged to, where that other woman ''also'' prefers that man over the one she is engaged to. I.e. with consulting marriages, there would be no reason for the engagements between the people to change.
'''Problem description'''<br>
Given an equal number of men and women to be paired for marriage, each man ranks all the women in order of his preference and each woman ranks all the men in order of her preference.
A stable set of engagements for marriage is one where no man prefers a woman over the one he is engaged to, where that other woman ''also'' prefers that man over the one she is engaged to. I.e. with consulting marriages, there would be no reason for the engagements between the people to change.
Gale and Shapley proved that there is a stable set of engagements for any set of preferences and the first link above gives their algorithm for finding a set of stable engagements.
'''Task Specifics'''<br>
Given ten males:
abe, bob, col, dan, ed, fred, gav, hal, ian, jon
@ -39,6 +41,7 @@ And a complete list of ranked preferences, where the most liked is to the left:
# Use the Gale Shapley algorithm to find a stable set of engagements
# Perturb this set of engagements to form an unstable set of engagements then check this new set for stability.
'''References'''
# [http://www.cs.columbia.edu/~evs/intro/stable/writeup.html The Stable Marriage Problem]. (Eloquent description and background information).
# [http://sephlietz.com/gale-shapley/ Gale-Shapley Algorithm Demonstration].
@ -46,3 +49,4 @@ And a complete list of ranked preferences, where the most liked is to the left:
# [https://www.youtube.com/watch?v=Qcv1IqHWAzg Stable Marriage Problem - Numberphile] (Video).
# [https://www.youtube.com/watch?v=LtTV6rIxhdo Stable Marriage Problem (the math bit)] (Video).
# [http://www.ams.org/samplings/feature-column/fc-2015-03 The Stable Marriage Problem and School Choice]. (Excellent exposition)
<br><br>

View file

@ -0,0 +1,12 @@
{-# LANGUAGE TemplateHaskell #-}
import Lens.Micro
import Lens.Micro.TH
import Data.List (union, delete)
type Preferences a = (a, [a])
type Couple a = (a,a)
data State a = State { _freeGuys :: [a]
, _guys :: [Preferences a]
, _girls :: [Preferences a]}
makeLenses ''State

View file

@ -0,0 +1,7 @@
name n = lens get set
where get = head . dropWhile ((/= n).fst)
set assoc (_,v) = let (prev, _:post) = break ((== n).fst) assoc
in prev ++ (n, v):post
fianceesOf n = guys.name n._2
fiancesOf n = girls.name n._2

View file

@ -0,0 +1,32 @@
stableMatching :: Eq a => State a -> [Couple a]
stableMatching = getPairs . iterateUntil (null._freeGuys) step
where
iterateUntil p f = head . dropWhile (not . p) . iterate f
getPairs s = map (_2 %~ head) $ s^.guys
step :: Eq a => State a -> State a
step s = foldl propose s (s^.freeGuys)
where
propose s guy =
let girl = s^.fianceesOf guy & head
bestGuy : otherGuys = s^.fiancesOf girl
modify
| guy == bestGuy = freeGuys %~ delete guy
| guy `elem` otherGuys = (fiancesOf girl %~ dropWhile (/= guy)) .
(freeGuys %~ guy `replaceBy` bestGuy)
| otherwise = fianceesOf guy %~ tail
in modify s
replaceBy x y [] = []
replaceBy x y (h:t) | h == x = y:t
| otherwise = h:replaceBy x y t
unstablePairs :: Eq a => State a -> [Couple a] -> [(Couple a, Couple a)]
unstablePairs s pairs =
[ ((m1, w1), (m2,w2)) | (m1, w1) <- pairs
, (m2,w2) <- pairs
, m1 /= m2
, let fm = s^.fianceesOf m1
, elemIndex w2 fm < elemIndex w1 fm
, let fw = s^.fiancesOf w2
, elemIndex m2 fw < elemIndex m1 fw ]

View file

@ -0,0 +1,23 @@
guys0 =
[("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"])]
girls0 =
[("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"])]

View file

@ -0,0 +1 @@
s0 = State (fst <$> guys0) guys0 ((_2 %~ reverse) <$> girls0)

View file

@ -1,99 +0,0 @@
import Data.List
import Control.Monad
import Control.Arrow
import Data.Maybe
mp = map ((head &&& tail). splitNames)
["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"]
fp = map ((head &&& tail). splitNames)
["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"]
splitNames = map (takeWhile(`notElem`",:")). words
pref x y xs = fromJust (elemIndex x xs) < fromJust (elemIndex y xs)
task ms fs = do
let
jos = fst $ unzip ms
runGS es js ms = do
let (m:js') = js
(v:vm') = case lookup m ms of
Just xs -> xs
_ -> []
vv = fromJust $ lookup v fs
m2 = case lookup v es of
Just e -> e
_ -> ""
ms1 = insert (m,vm') $ delete (m,v:vm') ms
if null js then do
putStrLn ""
putStrLn "=== Couples ==="
return es
else if null m2 then
do putStrLn $ v ++ " with " ++ m
runGS ( insert (v,m) es ) js' ms1
else if pref m m2 vv then
do putStrLn $ v ++ " dumped " ++ m2 ++ " for " ++ m
runGS ( insert (v,m) $ delete (v,m2) es ) (if not $ null vm' then js'++[m2] else js') ms1
else runGS es (if not $ null js' then js'++[m] else js') ms1
cs <- runGS [] jos ms
mapM_ (\(f,m) -> putStrLn $ f ++ " with " ++ m ) cs
putStrLn ""
checkStab cs
putStrLn ""
putStrLn "Introducing error: "
let [r1@(a,b), r2@(p,q)] = take 2 cs
r3 = (a,q)
r4 = (p,b)
errcs = insert r4. insert r3. delete r2 $ delete r1 cs
putStrLn $ "\tSwapping partners of " ++ a ++ " and " ++ p
putStrLn $ (\((a,b),(p,q)) -> "\t" ++ a ++ " is now with " ++ b ++ " and " ++ p ++ " with " ++ q) (r3,r4)
putStrLn ""
checkStab errcs
checkStab es = do
let
fmt (a,b,c,d) = a ++ " and " ++ b ++ " like each other better than their current partners " ++ c ++ " and " ++ d
ies = uncurry(flip zip) $ unzip es -- es = [(fem,m)] & ies = [(m,fem)]
slb = map (\(f,m)-> (f,m, map (id &&& fromJust. flip lookup ies). fst.break(==m). fromJust $ lookup f fp) ) es
hlb = map (\(f,m)-> (m,f, map (id &&& fromJust. flip lookup es ). fst.break(==f). fromJust $ lookup m mp) ) es
tslb = concatMap (filter snd. (\(f,m,ls) ->
map (\(m2,f2) ->
((f,m2,f2,m), pref f f2 $ fromJust $ lookup m2 mp)) ls)) slb
thlb = concatMap (filter snd. (\(m,f,ls) ->
map (\(f2,m2) ->
((m,f2,m2,f), pref m m2 $ fromJust $ lookup f2 fp)) ls)) hlb
res = tslb ++ thlb
if not $ null res then do
putStrLn "Marriages are unstable, e.g.:"
putStrLn.fmt.fst $ head res
else putStrLn "Marriages are stable"

View file

@ -1,19 +1 @@
0 105 A."_1 matchMake '' NB. swap abi and bea
┌───┬────┬───┬───┬───┬────┬───┬───┬────┬───┐
│abe│bob │col│dan│ed │fred│gav│hal│ian │jon│
├───┼────┼───┼───┼───┼────┼───┼───┼────┼───┤
│ivy│cath│dee│fay│jan│abi │gay│eve│hope│bea│
└───┴────┴───┴───┴───┴────┴───┴───┴────┴───┘
checkStable 0 105 A."_1 matchMake ''
Engagements preferred by both members to their current ones:
┌────┬───┐
│fred│bea│
├────┼───┤
│jon │fay│
├────┼───┤
│jon │gay│
├────┼───┤
│jon │eve│
└────┴───┘
|assertion failure: assert
| assert-.bad
checkStable matchMake''

View file

@ -0,0 +1,19 @@
0 105 A."_1 matchMake '' NB. swap abi and bea
┌───┬────┬───┬───┬───┬────┬───┬───┬────┬───┐
│abe│bob │col│dan│ed │fred│gav│hal│ian │jon│
├───┼────┼───┼───┼───┼────┼───┼───┼────┼───┤
│ivy│cath│dee│fay│jan│abi │gay│eve│hope│bea│
└───┴────┴───┴───┴───┴────┴───┴───┴────┴───┘
checkStable 0 105 A."_1 matchMake ''
Engagements preferred by both members to their current ones:
┌────┬───┐
│fred│bea│
├────┼───┤
│jon │fay│
├────┼───┤
│jon │gay│
├────┼───┤
│jon │eve│
└────┴───┘
|assertion failure: assert
| assert-.bad

View file

@ -0,0 +1,122 @@
import java.util.*
class People(val map: Map<String, Array<String>>) {
operator fun get(name: String) = map[name]
val names: List<String> by lazy { map.keys.toList() }
fun preferences(k: String, v: String): List<String> {
val prefers = get(k)!!
return ArrayList<String>(prefers.slice(0..prefers.indexOf(v)))
}
}
class EngagementRegistry() : TreeMap<String, String>() {
constructor(guys: People, girls: People) : this() {
val freeGuys = guys.names.toMutableList()
while (freeGuys.any()) {
val guy = freeGuys.removeAt(0) // get a load of THIS guy
val guy_p = guys[guy]!!
for (girl in guy_p)
if (this[girl] == null) {
this[girl] = guy // girl is free
break
} else {
val other = this[girl]!!
val girl_p = girls[girl]!!
if (girl_p.indexOf(guy) < girl_p.indexOf(other)) {
this[girl] = guy // this girl prefers this guy to the guy she's engaged to
freeGuys += other
break
} // else no change... keep looking for this guy
}
}
}
override fun toString(): String {
val s = StringBuilder()
for ((k, v) in this) s.append("$k is engaged to $v\n")
return s.toString()
}
fun analyse(guys: People, girls: People) {
if (check(guys, girls))
println("Marriages are stable")
else
println("Marriages are unstable")
}
fun swap(girls: People, i: Int, j: Int) {
val n1 = girls.names[i]
val n2 = girls.names[j]
val g0 = this[n1]!!
val g1 = this[n2]!!
this[n1] = g1
this[n2] = g0
println("$n1 and $n2 have switched partners")
}
private fun check(guys: People, girls: People): Boolean {
val guy_names = guys.names
val girl_names = girls.names
if (!keys.containsAll(girl_names) or !values.containsAll(guy_names))
return false
val invertedMatches = TreeMap<String, String>()
for ((k, v) in this) invertedMatches[v] = k
for ((k, v) in this) {
val sheLikesBetter = girls.preferences(k, v)
val heLikesBetter = guys.preferences(v, k)
for (guy in sheLikesBetter) {
val fiance = invertedMatches[guy]
val guy_p = guys[guy]!!
if (guy_p.indexOf(fiance) > guy_p.indexOf(k)) {
println("$k likes $guy better than $v and $guy likes $k better than their current partner")
return false
}
}
for (girl in heLikesBetter) {
val fiance = get(girl)
val girl_p = girls[girl]!!
if (girl_p.indexOf(fiance) > girl_p.indexOf(v)) {
println("$v likes $girl better than $k and $girl likes $v better than their current partner")
return false
}
}
}
return true
}
}
fun main(args: Array<String>) {
val guys = People(mapOf("abe" to arrayOf("abi", "eve", "cath", "ivy", "jan", "dee", "fay", "bea", "hope", "gay"),
"bob" to arrayOf("cath", "hope", "abi", "dee", "eve", "fay", "bea", "jan", "ivy", "gay"),
"col" to arrayOf("hope", "eve", "abi", "dee", "bea", "fay", "ivy", "gay", "cath", "jan"),
"dan" to arrayOf("ivy", "fay", "dee", "gay", "hope", "eve", "jan", "bea", "cath", "abi"),
"ed" to arrayOf("jan", "dee", "bea", "cath", "fay", "eve", "abi", "ivy", "hope", "gay"),
"fred" to arrayOf("bea", "abi", "dee", "gay", "eve", "ivy", "cath", "jan", "hope", "fay"),
"gav" to arrayOf("gay", "eve", "ivy", "bea", "cath", "abi", "dee", "hope", "jan", "fay"),
"hal" to arrayOf("abi", "eve", "hope", "fay", "ivy", "cath", "jan", "bea", "gay", "dee"),
"ian" to arrayOf("hope", "cath", "dee", "gay", "bea", "abi", "fay", "ivy", "jan", "eve"),
"jon" to arrayOf("abi", "fay", "jan", "gay", "eve", "bea", "dee", "cath", "ivy", "hope")))
val girls = People(mapOf("abi" to arrayOf("bob", "fred", "jon", "gav", "ian", "abe", "dan", "ed", "col", "hal"),
"bea" to arrayOf("bob", "abe", "col", "fred", "gav", "dan", "ian", "ed", "jon", "hal"),
"cath" to arrayOf("fred", "bob", "ed", "gav", "hal", "col", "ian", "abe", "dan", "jon"),
"dee" to arrayOf("fred", "jon", "col", "abe", "ian", "hal", "gav", "dan", "bob", "ed"),
"eve" to arrayOf("jon", "hal", "fred", "dan", "abe", "gav", "col", "ed", "ian", "bob"),
"fay" to arrayOf("bob", "abe", "ed", "ian", "jon", "dan", "fred", "gav", "col", "hal"),
"gay" to arrayOf("jon", "gav", "hal", "fred", "bob", "abe", "col", "ed", "dan", "ian"),
"hope" to arrayOf("gav", "jon", "bob", "abe", "ian", "dan", "hal", "ed", "col", "fred"),
"ivy" to arrayOf("ian", "col", "hal", "gav", "fred", "bob", "abe", "ed", "jon", "dan"),
"jan" to arrayOf("ed", "hal", "gav", "abe", "bob", "jon", "col", "ian", "fred", "dan")))
with(EngagementRegistry(guys, girls)) {
print(this)
analyse(guys, girls)
swap(girls, 0, 1)
analyse(guys, girls)
}
}

View file

@ -24,9 +24,6 @@ my %she-likes =
jan => < ed hal gav abe bob jon col ian fred dan >,
;
my \guys = %he-likes.keys;
my \gals = %she-likes.keys;
my %fiancé;
my %fiancée;
my %proposed;
@ -59,7 +56,7 @@ sub match'em { #'
}
sub check-stability {
my @instabilities = gather for guys X gals -> $m, $w {
my @instabilities = gather for flat %he-likes.keys X %she-likes.keys -> $m, $w {
if he-prefers($m, $w) and she-prefers($w, $m) {
take "\t$w prefers $m to %fiancé{$w} and $m prefers $w to %fiancée{$m}";
}
@ -74,7 +71,7 @@ sub check-stability {
}
}
sub unmatched-guy { guys.first: { not %fiancée{$_} } }
sub unmatched-guy { %he-likes.keys.first: { not %fiancée{$_} } }
sub preferred-choice($guy) { %he-likes{$guy}.first: { not %proposed{"$guy $_" } } }

View file

@ -0,0 +1,205 @@
/*- REXX --------------------------------------------------------------
* pref.b Preferences of boy b
* pref.g Preferences of girl g
* boys List of boys
* girls List of girls
* plist List of proposals
* mlist List of (current) matches
* glist List of girls to be matched
* glist.b List of girls that proposed to boy b
* blen maximum length of boys' names
* glen maximum length of girls' names
---------------------------------------------------------------------*/
pref.Charlotte=translate('Bingley Darcy Collins Wickham ')
pref.Elisabeth=translate('Wickham Darcy Bingley Collins ')
pref.Jane =translate('Bingley Wickham Darcy Collins ')
pref.Lydia =translate('Bingley Wickham Darcy Collins ')
pref.Bingley =translate('Jane Elisabeth Lydia Charlotte')
pref.Collins =translate('Jane Elisabeth Lydia Charlotte')
pref.Darcy =translate('Elisabeth Jane Charlotte Lydia')
pref.Wickham =translate('Lydia Jane Elisabeth Charlotte')
pref.ABE='ABI EVE CATH IVY JAN DEE FAY BEA HOPE GAY'
pref.BOB='CATH HOPE ABI DEE EVE FAY BEA JAN IVY GAY'
pref.COL='HOPE EVE ABI DEE BEA FAY IVY GAY CATH JAN'
pref.DAN='IVY FAY DEE GAY HOPE EVE JAN BEA CATH ABI'
pref.ED='JAN DEE BEA CATH FAY EVE ABI IVY HOPE GAY'
pref.FRED='BEA ABI DEE GAY EVE IVY CATH JAN HOPE FAY'
pref.GAV='GAY EVE IVY BEA CATH ABI DEE HOPE JAN FAY'
pref.HAL='ABI EVE HOPE FAY IVY CATH JAN BEA GAY DEE'
pref.IAN='HOPE CATH DEE GAY BEA ABI FAY IVY JAN EVE'
pref.JON='ABI FAY JAN GAY EVE BEA DEE CATH IVY HOPE'
pref.ABI='BOB FRED JON GAV IAN ABE DAN ED COL HAL'
pref.BEA='BOB ABE COL FRED GAV DAN IAN ED JON HAL'
pref.CATH='FRED BOB ED GAV HAL COL IAN ABE DAN JON'
pref.DEE='FRED JON COL ABE IAN HAL GAV DAN BOB ED'
pref.EVE='JON HAL FRED DAN ABE GAV COL ED IAN BOB'
pref.FAY='BOB ABE ED IAN JON DAN FRED GAV COL HAL'
pref.GAY='JON GAV HAL FRED BOB ABE COL ED DAN IAN'
pref.HOPE='GAV JON BOB ABE IAN DAN HAL ED COL FRED'
pref.IVY='IAN COL HAL GAV FRED BOB ABE ED JON DAN'
pref.JAN='ED HAL GAV ABE BOB JON COL IAN FRED DAN'
If arg(1)>'' Then Do
Say 'Input from task description'
boys='ABE BOB COL DAN ED FRED GAV HAL IAN JON'
girls='ABI BEA CATH DEE EVE FAY GAY HOPE IVY JAN'
End
Else Do
Say 'Input from link'
girls=translate('Charlotte Elisabeth Jane Lydia')
boys =translate('Bingley Collins Darcy Wickham')
End
debug=0
blen=0
Do i=1 To words(boys)
blen=max(blen,length(word(boys,i)))
End
glen=0
Do i=1 To words(girls)
glen=max(glen,length(word(girls,i)))
End
glist=girls
mlist=''
Do ri=1 By 1 Until glist='' /* as long as there are girls */
Call dbg 'Round' ri
plist='' /* no proposals in this round */
glist.=''
Do gi=1 To words(glist) /* loop over free girls */
gg=word(glist,gi) /* an unmathed girl */
b=word(pref.gg,1) /* her preferred boy */
plist=plist gg'-'||b /* remember this proposal */
glist.b=glist.b gg /* add girl to the boy's list */
Call dbg left(gg,glen) 'proposes to' b /* tell the user */
End
Do bi=1 To words(boys) /* loop over all boys */
b=word(boys,bi) /* one of them */
If glist.b>'' Then /* if he's got proposals */
Call dbg b 'has these proposals' glist.b /* show them */
End
Do bi=1 To words(boys) /* loop over all boys */
b=word(boys,bi) /* one of them */
bm=pos(b'*',mlist) /* has he been matched yet? */
Select
When words(glist.b)=1 Then Do /* one girl proposed for him */
gg=word(glist.b,1) /* the proposing girl */
If bm=0 Then Do /* no, he hasn't */
Call dbg b 'accepts' gg /* is accepted */
Call set_mlist 'A',mlist b||'*'||gg /* add match to mlist */
Call set_glist 'A',remove(gg,glist) /* remove gg from glist*/
pref.gg=remove(b,pref.gg) /* remove b from gg's preflist*/
End
Else Do /* boy has been matched */
Parse Var mlist =(bm) '*' go ' ' /* to girl go */
If wordpos(gg,pref.b)<wordpos(go,pref.b) Then Do
/* the proposing girl is preferred to the current one */
/* so we replace go by gg */
Call set_mlist 'B',repl(mlist,b||'*'||gg,b||'*'||go)
Call dbg b 'releases' go
Call dbg b 'accepts ' gg
Call set_glist 'B',glist go /* add go to list of girls */
Call set_glist 'C',remove(gg,glist) /* and remove gg */
End
pref.gg=remove(b,pref.gg) /* remove b from gg's preflist*/
End
End
When words(glist.b)>1 Then
Call pick_1
Otherwise Nop
End
End
Call dbg 'Matches :' mlist
Call dbg 'free girls:' glist
Call check 'L'
End
Say 'Success at round' (ri-1)
Do While mlist>''
Parse Var mlist boy '*' girl mlist
Say left(boy,blen) 'matches' girl
End
Exit
pick_1:
If bm>0 Then Do /* boy has been matched */
Parse Var mlist =(bm) '*' go ' ' /* to girl go */
pmin=wordpos(go,pref.b)
End
Else Do
go=''
pmin=99
End
Do gi=1 To words(glist.b)
gt=word(glist.b,gi)
gp=wordpos(gt,pref.b)
If gp<pmin Then Do
pmin=gp
gg=gt
End
End
If bm=0 Then Do
Call dbg b 'accepts' gg /* is accepted */
Call set_mlist 'A',mlist b||'*'||gg /* add match to mlist */
Call set_glist 'A',remove(gg,glist) /* remove gg from glist*/
pref.gg=remove(b,pref.gg) /* remove b from gg's preflist*/
End
Else Do
If gg<>go Then Do
Call set_mlist 'B',repl(mlist,b||'*'||gg,b||'*'||go)
Call dbg b 'releases' go
Call dbg b 'accepts ' gg
Call set_glist 'B',glist go /* add go to list of girls */
Call set_glist 'C',remove(gg,glist) /* and remove gg */
pref.gg=remove(b,pref.gg) /* remove b from gg's preflist*/
End
End
Return
remove:
Parse Arg needle,haystack
pp=pos(needle,haystack)
If pp>0 Then
res=left(haystack,pp-1) substr(haystack,pp+length(needle))
Else
res=haystack
Return space(res)
set_mlist:
Parse Arg where,new_mlist
Call dbg 'set_mlist' where':' mlist
mlist=space(new_mlist)
Call dbg 'set_mlist ->' mlist
Call dbg ''
Return
set_glist:
Parse Arg where,new_glist
Call dbg 'set_glist' where':' glist
glist=new_glist
Call dbg 'set_glist ->' glist
Call dbg ''
Return
check:
If words(mlist)+words(glist)<>words(boys) Then Do
Call dbg 'FEHLER bei' arg(1) (words(mlist)+words(glist))'<>10'
say 'match='mlist'<'
say ' glist='glist'<'
End
Return
dbg:
If debug Then
Call dbg arg(1)
Return
repl: Procedure
Parse Arg s,new,old
Do i=1 To 100 Until p=0
p=pos(old,s)
If p>0 Then
s=left(s,p-1)||new||substr(s,p+length(old))
End
Return s

View file

@ -0,0 +1,121 @@
import java.util._
import scala.collection.JavaConversions._
object SMP extends App {
def run() {
Seq("abe" -> Array("abi", "eve", "cath", "ivy", "jan", "dee", "fay", "bea", "hope", "gay"),
"bob" -> Array("cath", "hope", "abi", "dee", "eve", "fay", "bea", "jan", "ivy", "gay"),
"col" -> Array("hope", "eve", "abi", "dee", "bea", "fay", "ivy", "gay", "cath", "jan"),
"dan" -> Array("ivy", "fay", "dee", "gay", "hope", "eve", "jan", "bea", "cath", "abi"),
"ed" -> Array("jan", "dee", "bea", "cath", "fay", "eve", "abi", "ivy", "hope", "gay"),
"fred" -> Array("bea", "abi", "dee", "gay", "eve", "ivy", "cath", "jan", "hope", "fay"),
"gav" -> Array("gay", "eve", "ivy", "bea", "cath", "abi", "dee", "hope", "jan", "fay"),
"hal" -> Array("abi", "eve", "hope", "fay", "ivy", "cath", "jan", "bea", "gay", "dee"),
"ian" -> Array("hope", "cath", "dee", "gay", "bea", "abi", "fay", "ivy", "jan", "eve"),
"jon" -> Array("abi", "fay", "jan", "gay", "eve", "bea", "dee", "cath", "ivy", "hope"))
.foreach { e => guyPrefers.put(e._1, e._2.toList) }
Seq("abi" -> Array("bob", "fred", "jon", "gav", "ian", "abe", "dan", "ed", "col", "hal"),
"bea" -> Array("bob", "abe", "col", "fred", "gav", "dan", "ian", "ed", "jon", "hal"),
"cath" -> Array("fred", "bob", "ed", "gav", "hal", "col", "ian", "abe", "dan", "jon"),
"dee" -> Array("fred", "jon", "col", "abe", "ian", "hal", "gav", "dan", "bob", "ed"),
"eve" -> Array("jon", "hal", "fred", "dan", "abe", "gav", "col", "ed", "ian", "bob"),
"fay" -> Array("bob", "abe", "ed", "ian", "jon", "dan", "fred", "gav", "col", "hal"),
"gay" -> Array("jon", "gav", "hal", "fred", "bob", "abe", "col", "ed", "dan", "ian"),
"hope" -> Array("gav", "jon", "bob", "abe", "ian", "dan", "hal", "ed", "col", "fred"),
"ivy" -> Array("ian", "col", "hal", "gav", "fred", "bob", "abe", "ed", "jon", "dan"),
"jan" -> Array("ed", "hal", "gav", "abe", "bob", "jon", "col", "ian", "fred", "dan"))
.foreach { e => girlPrefers.put(e._1, e._2.toList) }
val matches = matching(guys, guyPrefers, girlPrefers)
matches.foreach { e => println(s"${e._1} is engaged to ${e._2}") }
if (checkMatches(guys, girls, matches, guyPrefers, girlPrefers))
println("Marriages are stable")
else
println("Marriages are unstable")
val tmp = matches(girls(0))
matches += girls(0) -> matches(girls(1))
matches += girls(1) -> tmp
println(girls(0) + " and " + girls(1) + " have switched partners")
if (checkMatches(guys, girls, matches, guyPrefers, girlPrefers))
println("Marriages are stable")
else
println("Marriages are unstable")
}
private def matching(guys: Iterable[String],
guyPrefers: Map[String, List[String]],
girlPrefers: Map[String, List[String]]): Map[String, String] = {
val engagements = new TreeMap[String, String]
val freeGuys = new LinkedList[String](guys)
while (!freeGuys.isEmpty) {
val guy = freeGuys.remove(0)
val guy_p = guyPrefers(guy)
var break = false
for (girl <- guy_p)
if (!break)
if (!engagements.containsKey(girl)) {
engagements += girl -> guy
break = true
}
else {
val other_guy = engagements(girl)
val girl_p = girlPrefers(girl)
if (girl_p.indexOf(guy) < girl_p.indexOf(other_guy)) {
engagements += girl -> guy
freeGuys += other_guy
break = true
}
}
}
engagements
}
private def checkMatches(guys: Iterable[String], girls: Iterable[String],
matches: Map[String, String],
guyPrefers: Map[String, List[String]],
girlPrefers: Map[String, List[String]]): Boolean = {
if (!matches.keySet.containsAll(girls) || !matches.values.containsAll(guys))
return false
val invertedMatches = new TreeMap[String, String]
matches.foreach { invertedMatches += _.swap }
for ((k, v) <- matches) {
val shePrefers = girlPrefers(k)
val sheLikesBetter = new LinkedList[String]
sheLikesBetter.addAll(shePrefers.subList(0, shePrefers.indexOf(v)))
val hePrefers = guyPrefers(v)
val heLikesBetter = new LinkedList[String]
heLikesBetter.addAll(hePrefers.subList(0, hePrefers.indexOf(k)))
for (guy <- sheLikesBetter) {
val fiance = invertedMatches(guy)
val guy_p = guyPrefers(guy)
if (guy_p.indexOf(fiance) > guy_p.indexOf(k)) {
println(s"$k likes $guy better than $v and $guy likes $k better than their current partner")
return false
}
}
for (girl <- heLikesBetter) {
val fiance = matches(girl)
val girl_p = girlPrefers(girl)
if (girl_p.indexOf(fiance) > girl_p.indexOf(v)) {
println(s"$v likes $girl better than $k and $girl likes $v better than their current partner")
return false
}
}
}
true
}
private val guys = "abe" :: "bob" :: "col" :: "dan" :: "ed" :: "fred" :: "gav" :: "hal" :: "ian" :: "jon" :: Nil
private val girls = "abi" :: "bea" :: "cath" :: "dee" :: "eve" :: "fay" :: "gay" :: "hope" :: "ivy" :: "jan" :: Nil
private val guyPrefers = new HashMap[String, List[String]]
private val girlPrefers = new HashMap[String, List[String]]
run()
}

View file

@ -0,0 +1,167 @@
#!/usr/bin/env bash
main() {
# Our ten males:
local males=(abe bob col dan ed fred gav hal ian jon)
# And ten females:
local females=(abi bea cath dee eve fay gay hope ivy jan)
# Everyone's preferences, ranked most to least desirable:
local abe=( abi eve cath ivy jan dee fay bea hope gay )
local abi=( bob fred jon gav ian abe dan ed col hal )
local bea=( bob abe col fred gav dan ian ed jon hal )
local bob=(cath hope abi dee eve fay bea jan ivy gay )
local cath=(fred bob ed gav hal col ian abe dan jon )
local col=(hope eve abi dee bea fay ivy gay cath jan )
local dan=( ivy fay dee gay hope eve jan bea cath abi )
local dee=(fred jon col abe ian hal gav dan bob ed )
local ed=( jan dee bea cath fay eve abi ivy hope gay )
local eve=( jon hal fred dan abe gav col ed ian bob )
local fay=( bob abe ed ian jon dan fred gav col hal )
local fred=( bea abi dee gay eve ivy cath jan hope fay )
local gav=( gay eve ivy bea cath abi dee hope jan fay )
local gay=( jon gav hal fred bob abe col ed dan ian )
local hal=( abi eve hope fay ivy cath jan bea gay dee )
local hope=( gav jon bob abe ian dan hal ed col fred)
local ian=(hope cath dee gay bea abi fay ivy jan eve )
local ivy=( ian col hal gav fred bob abe ed jon dan )
local jan=( ed hal gav abe bob jon col ian fred dan )
local jon=( abi fay jan gay eve bea dee cath ivy hope)
# A place to store the engagements:
local -A engagements=()
# Our list of free males, initially comprised of all of them:
local freemales=( "${males[@]}" )
# Now we use the Gale-Shapley algorithm to find a stable set of engagements
# Loop over the free males. Note that we can't use for..in because the body
# of the loop may modify the array we're looping over
local -i m=0
while (( m < ${#freemales[@]} )); do
local male=${freemales[m]}
let m+=1
# This guy's preferences
eval 'local his=("${'"$male"'[@]}")'
# Starting with his favorite
local -i f=0
local female=${his[f]}
# Find her preferences
eval 'local hers=("${'"$female"'[@]}")'
# And her current fiancé, if any
local fiance=${engagements[$female]}
# If she has a fiancé and prefers him to this guy, look for this guy's next
# best choice
while [[ -n $fiance ]] &&
(( $(index "$male" "${hers[@]}") > $(index "$fiance" "${hers[@]}") )); do
let f+=1
female=${his[f]}
eval 'hers=("${'"$female"'[@]}")'
fiance=${engagements[$female]}
done
# If we're still on someone who's engaged, it means she prefers this guy
# to her current fiancé. Dump him and put him at the end of the free list.
if [[ -n $fiance ]]; then
freemales+=("$fiance")
printf '%-4s rejected %-4s\n' "$female" "$fiance"
fi
# We found a match! Record it
engagements[$female]=$male
printf '%-4s accepted %-4s\n' "$female" "$male"
done
# Display the final result, which should be stable
print_couples engagements
# Verify its stability
print_stable engagements "${females[@]}"
# Try a swap
printf '\nWhat if cath and ivy swap partners?\n'
local temp=${engagements[cath]}
engagements[cath]=${engagements[ivy]}
engagements[ivy]=$temp
# Display the new result, which should be unstable
print_couples engagements
# Verify its instability
print_stable engagements "${females[@]}"
}
# utility function - get index of an item in an array
index() {
local needle=$1
shift
local haystack=("$@")
local -i i
for i in "${!haystack[@]}"; do
if [[ ${haystack[i]} == $needle ]]; then
printf '%d\n' "$i"
return 0
fi
done
return 1
}
# print the couples from the engagement array; takes name of array as argument
print_couples() {
printf '\nCouples:\n'
local keys
mapfile -t keys < <(eval 'printf '\''%s\n'\'' "${!'"$1"'[@]}"' | sort)
local female
for female in "${keys[@]}"; do
eval 'local male=${'"$1"'["'"$female"'"]}'
printf '%-4s is engaged to %-4s\n' "$female" "$male"
done
printf '\n'
}
# print whether a set of engagements is stable; takes name of engagement array
# followed by the list of females
print_stable() {
if stable "$@"; then
printf 'These couples are stable.\n'
else
printf 'These couples are not stable.\n'
fi
}
# determine if a set of engagements is stable; takes name of engagement array
# followed by the list of females
stable() {
local dict=$1
shift
eval 'local shes=("${!'"$dict"'[@]}")'
eval 'local hes=("${'"$dict"'[@]}")'
local -i i
local -i result=0
for (( i=0; i<${#shes[@]}; ++i )); do
local she=${shes[i]} he=${hes[i]}
eval 'local his=("${'"$he"'[@]}")'
local alt
for alt in "$@"; do
eval 'local fiance=${'"$dict"'["'"$alt"'"]}'
eval 'local hers=("${'"$alt"'[@]}")'
if (( $(index "$she" "${his[@]}") > $(index "$alt" "${his[@]}")
&& $(index "$fiance" "${hers[@]}") > $(index "$he" "${hers[@]}") ))
then
printf '%-4s is engaged to %-4s but prefers %4s, ' "$he" "$she" "$alt"
printf 'while %-4s is engaged to %-4s but prefers %4s.\n' "$alt" "$fiance" "$he"
result=1
fi
done
done
if (( result )); then printf '\n'; fi
return $result
}
main "$@"

View file

@ -0,0 +1,44 @@
Sub M_snb()
c00 = "_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 " & _
"_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 "
sn = Filter(Filter(Split(c00), "_"), "-", 0)
Do
c01 = Mid(c00, InStr(c00, sn(0) & " "))
st = Split(Left(c01, InStr(Mid(c01, 2), "_")))
For j = 1 To UBound(st) - 1
If InStr(c00, "_" & st(j) & " ") > 0 Then
c00 = Replace(Replace(c00, sn(0), sn(0) & "-" & st(j)), "_" & st(j), "_" & st(j) & "." & Mid(sn(0), 2))
Exit For
Else
c02 = Filter(Split(c00, "_"), st(j) & ".")(0)
c03 = Split(Split(c02)(0), ".")(1)
If InStr(c02, " " & Mid(sn(0), 2) & " ") < InStr(c02, " " & c03 & " ") Then
c00 = Replace(Replace(Replace(c00, c03 & "-" & st(j), c03), sn(0), sn(0) & "-" & st(j)), "_" & st(j), "_" & st(j) & "." & Mid(sn(0), 2))
Exit For
End If
End If
Next
sn = Filter(Filter(Filter(Split(c00), "_"), "-", 0), ".", 0)
Loop Until UBound(sn) = -1
MsgBox Replace(Join(Filter(Split(c00), "-"), vbLf), "_", "")
End Sub

View file

@ -0,0 +1,56 @@
Sub M_snb()
Set d_00 = CreateObject("scripting.dictionary")
Set d_01 = CreateObject("scripting.dictionary")
Set d_02 = CreateObject("scripting.dictionary")
sn = Split("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 ", "_")
sp = Split("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 ", "_")
For j = 0 To UBound(sn)
d_00(Split(sn(j))(0)) = ""
d_01(Split(sp(j))(0)) = ""
d_02(Split(sn(j))(0)) = sn(j)
d_02(Split(sp(j))(0)) = sp(j)
Next
Do
For Each it In d_00.keys
If d_00.Item(it) = "" Then
st = Split(d_02.Item(it))
For jj = 1 To UBound(st)
If d_01(st(jj)) = "" Then
d_00(st(0)) = st(0) & vbTab & st(jj)
d_01(st(jj)) = st(0)
Exit For
ElseIf InStr(d_02.Item(st(jj)), " " & st(0) & " ") < InStr(d_02.Item(st(jj)), " " & d_01(st(jj)) & " ") Then
d_00(d_01(st(jj))) = ""
d_00(st(0)) = st(0) & vbTab & st(jj)
d_01(st(jj)) = st(0)
Exit For
End If
Next
End If
Next
Loop Until UBound(Filter(d_00.items, vbTab)) = d_00.Count - 1
MsgBox Join(d_00.items, vbLf)
End Sub