June 2018 Update

This commit is contained in:
Ingy döt Net 2018-06-22 20:57:24 +00:00
parent ba8067c3b7
commit 22f33d4004
5278 changed files with 84726 additions and 14379 deletions

View file

@ -1,139 +1,148 @@
:: Stable Marriage Problem in Rosetta Code
:: Batch File Implementation
@echo off
setlocal enabledelayedexpansion
%== Initialization ==%
set "male= abe bob col dan ed fred gav hal ian jon" ::First whitespace is necessary
set "female= abi bea cath dee eve fay gay hope ivy jan" ::same here...
:: Initialization (Index Starts in 0)
set "male=abe bob col dan ed fred gav hal ian jon"
set "femm=abi bea cath dee eve fay gay hope ivy jan"
::Initialization of pseudo-arrays [Male]
set "cnt=0" & for %%. in (abi, eve, cath, ivy, jan, dee, fay, bea, hope, gay) do (set abe[!cnt!]=%%.&set /a cnt+=1)
set "cnt=0" & for %%. in (cath, hope, abi, dee, eve, fay, bea, jan, ivy, gay) do (set bob[!cnt!]=%%.&set /a cnt+=1)
set "cnt=0" & for %%. in (hope, eve, abi, dee, bea, fay, ivy, gay, cath, jan) do (set col[!cnt!]=%%.&set /a cnt+=1)
set "cnt=0" & for %%. in (ivy, fay, dee, gay, hope, eve, jan, bea, cath, abi) do (set dan[!cnt!]=%%.&set /a cnt+=1)
set "cnt=0" & for %%. in (jan, dee, bea, cath, fay, eve, abi, ivy, hope, gay) do (set ed[!cnt!]=%%.&set /a cnt+=1)
set "cnt=0" & for %%. in (bea, abi, dee, gay, eve, ivy, cath, jan, hope, fay) do (set fred[!cnt!]=%%.&set /a cnt+=1)
set "cnt=0" & for %%. in (gay, eve, ivy, bea, cath, abi, dee, hope, jan, fay) do (set gav[!cnt!]=%%.&set /a cnt+=1)
set "cnt=0" & for %%. in (abi, eve, hope, fay, ivy, cath, jan, bea, gay, dee) do (set hal[!cnt!]=%%.&set /a cnt+=1)
set "cnt=0" & for %%. in (hope, cath, dee, gay, bea, abi, fay, ivy, jan, eve) do (set ian[!cnt!]=%%.&set /a cnt+=1)
set "cnt=0" & for %%. in (abi, fay, jan, gay, eve, bea, dee, cath, ivy, hope) do (set jon[!cnt!]=%%.&set /a cnt+=1)
set "abe[]=abi, eve, cath, ivy, jan, dee, fay, bea, hope, gay"
set "bob[]=cath, hope, abi, dee, eve, fay, bea, jan, ivy, gay"
set "col[]=hope, eve, abi, dee, bea, fay, ivy, gay, cath, jan"
set "dan[]=ivy, fay, dee, gay, hope, eve, jan, bea, cath, abi"
set "ed[]=jan, dee, bea, cath, fay, eve, abi, ivy, hope, gay"
set "fred[]=bea, abi, dee, gay, eve, ivy, cath, jan, hope, fay"
set "gav[]=gay, eve, ivy, bea, cath, abi, dee, hope, jan, fay"
set "hal[]=abi, eve, hope, fay, ivy, cath, jan, bea, gay, dee"
set "ian[]=hope, cath, dee, gay, bea, abi, fay, ivy, jan, eve"
set "jon[]=abi, fay, jan, gay, eve, bea, dee, cath, ivy, hope"
::Initialization of pseudo-arrays [Female]
set "cnt=0" & for %%. in (bob, fred, jon, gav, ian, abe, dan, ed, col, hal) do (set abi[!cnt!]=%%.&set /a cnt+=1)
set "cnt=0" & for %%. in (bob, abe, col, fred, gav, dan, ian, ed, jon, hal) do (set bea[!cnt!]=%%.&set /a cnt+=1)
set "cnt=0" & for %%. in (fred, bob, ed, gav, hal, col, ian, abe, dan, jon) do (set cath[!cnt!]=%%.&set /a cnt+=1)
set "cnt=0" & for %%. in (fred, jon, col, abe, ian, hal, gav, dan, bob, ed) do (set dee[!cnt!]=%%.&set /a cnt+=1)
set "cnt=0" & for %%. in (jon, hal, fred, dan, abe, gav, col, ed, ian, bob) do (set eve[!cnt!]=%%.&set /a cnt+=1)
set "cnt=0" & for %%. in (bob, abe, ed, ian, jon, dan, fred, gav, col, hal) do (set fay[!cnt!]=%%.&set /a cnt+=1)
set "cnt=0" & for %%. in (jon, gav, hal, fred, bob, abe, col, ed, dan, ian) do (set gay[!cnt!]=%%.&set /a cnt+=1)
set "cnt=0" & for %%. in (gav, jon, bob, abe, ian, dan, hal, ed, col, fred) do (set hope[!cnt!]=%%.&set /a cnt+=1)
set "cnt=0" & for %%. in (ian, col, hal, gav, fred, bob, abe, ed, jon, dan) do (set ivy[!cnt!]=%%.&set /a cnt+=1)
set "cnt=0" & for %%. in (ed, hal, gav, abe, bob, jon, col, ian, fred, dan) do (set jan[!cnt!]=%%.&set /a cnt+=1)
%==/Initialization ==%
set "abi[]=bob, fred, jon, gav, ian, abe, dan, ed, col, hal"
set "bea[]=bob, abe, col, fred, gav, dan, ian, ed, jon, hal"
set "cath[]=fred, bob, ed, gav, hal, col, ian, abe, dan, jon"
set "dee[]=fred, jon, col, abe, ian, hal, gav, dan, bob, ed"
set "eve[]=jon, hal, fred, dan, abe, gav, col, ed, ian, bob"
set "fay[]=bob, abe, ed, ian, jon, dan, fred, gav, col, hal"
set "gay[]=jon, gav, hal, fred, bob, abe, col, ed, dan, ian"
set "hope[]=gav, jon, bob, abe, ian, dan, hal, ed, col, fred"
set "ivy[]=ian, col, hal, gav, fred, bob, abe, ed, jon, dan"
set "jan[]=ed, hal, gav, abe, bob, jon, col, ian, fred, dan"
( %== The main thing ==%
echo.HISTORY:
rem variable notation:
rem <boy>{<index>} = <girl>
rem <boy>[<girl>] = <index>
for %%M in (%male%) do (
set cnt=0
for %%. in (!%%M[]!) do (
set "%%M{!cnt!}=%%."
set "%%M[%%.]=!cnt!"
set /a cnt+=1
)
)
for %%F in (%femm%) do (
set cnt=0
for %%. in (!%%F[]!) do (
set "%%F[%%.]=!cnt!"
set /a cnt+=1
)
)
:: The Main Thing
echo(HISTORY:
call :stableMatching
echo.
echo.NEWLYWEDS:
echo(
echo(NEWLYWEDS:
call :display
echo.
echo(
call :isStable
echo.
echo.What if ed and hal swapped?
echo(
echo(What if ed and hal swapped?
call :swapper ed hal
echo.
echo.NEW-NEWLYWEDS:
echo(
echo(NEW-NEWLYWEDS:
call :display
echo.
echo(
call :isStable
pause>nul
exit /b 0
) %==/The main thing ==%
%== The algorithm ==%
:: The Algorithm
:stableMatching
set "free_men=%male%" ::The free-men variable
set "free_women=%female%" ::The free-women variable
set nextgirl=0
:thematchloop
set m=&for %%F in (!free_men!) do (if not defined m set "m=%%F")
if "!m!"=="" goto :EOF
set "free_men=%male%"
set "free_fem=%femm%"
for %%M in (%male%) do set "%%M_tried=0"
for /f "tokens=1-2 delims==" %%A in ('set !m![!nextgirl!]') do set "w=%%B"
set "propo="
for %%W in (!free_women!) do (
if "%%W"=="!w!" (
set propo=TRUE
set "!w!_=!m!" & set "!m!_=!w!"
set free_women=!free_women: %w%=!
set free_men=!free_men: %m%=!
echo. !w! ACCEPTED !m!.
)
)
if defined propo (set "nextgirl=0" & goto thematchloop)
:match_loop
if "%free_men%"=="" goto :EOF
for /f "tokens=1* delims= " %%m in ("%free_men%") do (
rem get woman not yet proposed to, but if man's tries exceeds the number
rem of women (poor guy), he starts again to his most preferred woman (#0).
for /f %%x in ("!%%m_tried!") do if not defined %%m{%%x} (
set "%%m_tried=0" & set "w=!%%m{0}!"
) else set "w=!%%m{%%x}!"
set "m=%%m"
for /f "tokens=1-2 delims==" %%A in ('set !w!_') do set "mbef=%%B"
set "replace=" & for /f "tokens=1-2 delims==" %%R in ('set !w![') do (
if not defined replace (
if "%%S"=="!m!" (
set replace=TRUE
set "!w!_=!m!" & set "!m!_=!w!"
set "free_men=!free_men! !mbef!"
set "free_men=!free_men: %m%=!"
set nextgirl=0
echo. !w! LEFT !mbef!.
echo. !w! ACCEPTED !m!.
)
if "%%S"=="!mbef!" (
set /a nextgirl+=1
set replace=FALSE
)
)
)
goto thematchloop
%==/The Algorithm ==%
for /f %%x in ("free_fem:!w!=") do (
if not "!free_fem!"=="!%%x!" (
rem accept because !w! (the woman) is free
set "!m!_=!w!" & set "!w!_=!m!"
set "free_men=%%n" & set "free_fem=!%%x!"
echo( !w! ACCEPTED !m!.
) else (
rem here, !w! already has a pair; get his name and rank.
for /f %%. in ("!w!") do set "cur_man=!%%._!"
for /f %%. in ("!w![!cur_man!]") do set "rank_cur=!%%.!"
rem also, get the rank of current proposing man.
for /f %%. in ("!w![!m!]") do set "rank_new=!%%.!"
%== Output the Couples ==%
:display
for %%S in (!male!) do echo. %%S and !%%S_!.
goto :EOF
%==/Output the Couples ==%
%== Stability Checking ==%
:isStable
for %%M in (!female!) do (
set "better="
set "dislike=" & for /f "tokens=1-2 delims==" %%R in ('set %%M[') do (
if not defined dislike (
if "%%S"=="!%%M_!" (set dislike=T) else (set "better=!better! %%S")
)
)
for %%X in (!better!) do (
for /f "tokens=1-2 delims==" %%F in ('set %%X_') do set curr_partner_of_boy=%%G
set "main_check="
for /f "tokens=1-2 delims==" %%B in ('set %%X[') do (
if not defined main_check (
if "%%C"=="%%M" (
echo.STABILITY = FALSE.
echo %%M and %%X would rather be together than their current partners.
goto :EOF
)
if "%%C"=="!curr_partner_of_boy!" set "main_check=CONTINUE"
)
)
)
if !rank_new! lss !rank_cur! (
rem here, !w! will leave her pair, and choose !m!.
set "free_men=%%n !cur_man!"
echo( !w! LEFT !cur_man!.
rem pair them up now!
set "!m!_=!w!" & set "!w!_=!m!"
echo( !w! ACCEPTED !m!.
)
)
)
set /a "!m!_tried+=1"
)
echo.STABILITY = TRUE.
goto :EOF
%==/Stability Chacking ==%
goto :match_loop
%== Swapper ==%
:: Output the Couples
:display
for %%S in (%femm%) do echo. %%S and !%%S_!.
goto :EOF
:: Stability Checking
:isStable
for %%f in (%femm%) do (
for %%g in (%male%) do (
for /f %%. in ("%%f[!%%f_!]") do set "girl_cur=!%%.!"
set "girl_aboy=!%%f[%%g]!"
for /f %%. in ("%%g[!%%g_!]") do set "boy_cur=!%%.!"
set "boy_agirl=!%%g[%%f]!"
if !boy_cur! gtr !boy_agirl! (
if !girl_cur! gtr !girl_aboy! (
echo(STABILITY = FALSE.
echo(%%f and %%g would rather be together than their current partners.
goto :EOF
)
)
)
)
echo(STABILITY = TRUE.
goto :EOF
:: Swapper
:swapper
set %~1.tmp=!%~1_!
set %~2.tmp=!%~2_!
set "%~1_=!%~2.tmp!"
set "%~2_=!%~1.tmp!"
set "!%~1.tmp!_=%~2"
set "!%~2.tmp!_=%~1"
goto :EOF
%==/Swapper==%
set %~1.tmp=!%~1_!
set %~2.tmp=!%~2_!
set "%~1_=!%~2.tmp!"
set "%~2_=!%~1.tmp!"
set "!%~1.tmp!_=%~2"
set "!%~2.tmp!_=%~1"
goto :EOF

View file

@ -0,0 +1,138 @@
abstract class Single(name) of Gal | Guy {
shared String name;
shared late Single[] preferences;
shared variable Single? fiance = null;
shared Boolean free => fiance is Null;
shared variable Integer currentProposalIndex = 0;
"Does this single prefer this other single over their fiance?"
shared Boolean prefers(Single otherSingle) =>
let (p1 = preferences.firstIndexWhere(otherSingle.equals), f = fiance)
if (!exists p1)
then false
else if (!exists f)
then true
else if (exists p2 = preferences.firstIndexWhere(f.equals))
then p1 < p2
else false;
string => name;
}
abstract class Guy(String name) of abe | bob | col | dan | ed | fred | gav | hal | ian | jon extends Single(name) {}
object abe extends Guy("Abe") {}
object bob extends Guy("Bob") {}
object col extends Guy("Col") {}
object dan extends Guy("Dan") {}
object ed extends Guy("Ed") {}
object fred extends Guy("Fred") {}
object gav extends Guy("Gav") {}
object hal extends Guy("Hal") {}
object ian extends Guy("Ian") {}
object jon extends Guy("Jon") {}
abstract class Gal(String name) of abi | bea | cath | dee | eve | fay | gay | hope | ivy | jan extends Single(name) {}
object abi extends Gal("Abi") {}
object bea extends Gal("Bea") {}
object cath extends Gal("Cath") {}
object dee extends Gal("Dee") {}
object eve extends Gal("Eve") {}
object fay extends Gal("Fay") {}
object gay extends Gal("Gay") {}
object hope extends Gal("Hope") {}
object ivy extends Gal("Ivy") {}
object jan extends Gal("Jan") {}
Guy[] guys = `Guy`.caseValues;
Gal[] gals = `Gal`.caseValues;
"The main function. Run this one."
shared void run() {
abe.preferences = [ abi, eve, cath, ivy, jan, dee, fay, bea, hope, gay ];
bob.preferences = [ cath, hope, abi, dee, eve, fay, bea, jan, ivy, gay ];
col.preferences = [ hope, eve, abi, dee, bea, fay, ivy, gay, cath, jan ];
dan.preferences = [ ivy, fay, dee, gay, hope, eve, jan, bea, cath, abi ];
ed.preferences = [ jan, dee, bea, cath, fay, eve, abi, ivy, hope, gay ];
fred.preferences = [ bea, abi, dee, gay, eve, ivy, cath, jan, hope, fay ];
gav.preferences = [ gay, eve, ivy, bea, cath, abi, dee, hope, jan, fay ];
hal.preferences = [ abi, eve, hope, fay, ivy, cath, jan, bea, gay, dee ];
ian.preferences = [ hope, cath, dee, gay, bea, abi, fay, ivy, jan, eve ];
jon.preferences = [ abi, fay, jan, gay, eve, bea, dee, cath, ivy, hope ];
abi.preferences = [ bob, fred, jon, gav, ian, abe, dan, ed, col, hal ];
bea.preferences = [ bob, abe, col, fred, gav, dan, ian, ed, jon, hal ];
cath.preferences = [ fred, bob, ed, gav, hal, col, ian, abe, dan, jon ];
dee.preferences = [ fred, jon, col, abe, ian, hal, gav, dan, bob, ed ];
eve.preferences = [ jon, hal, fred, dan, abe, gav, col, ed, ian, bob ];
fay.preferences = [ bob, abe, ed, ian, jon, dan, fred, gav, col, hal ];
gay.preferences = [ jon, gav, hal, fred, bob, abe, col, ed, dan, ian ];
hope.preferences = [ gav, jon, bob, abe, ian, dan, hal, ed, col, fred ];
ivy.preferences = [ ian, col, hal, gav, fred, bob, abe, ed, jon, dan ];
jan.preferences = [ ed, hal, gav, abe, bob, jon, col, ian, fred, dan ];
print("------ the matchmaking process ------");
matchmake();
print("------ the final engagements ------");
for (guy in guys) {
print("``guy`` is engaged to ``guy.fiance else "no one"``");
}
print("------ is it stable? ------");
checkStability();
value temp = jon.fiance;
jon.fiance = fred.fiance;
fred.fiance = temp;
print("------ is it stable after switching jon and fred's partners? ------");
checkStability();
}
"Match up all the singles with the Gale/Shapley algorithm."
void matchmake() {
while (true) {
value singleGuys = guys.filter(Guy.free);
if (singleGuys.empty) {
return;
}
for (guy in singleGuys) {
if (exists gal = guy.preferences[guy.currentProposalIndex]) {
guy.currentProposalIndex++;
value fiance = gal.fiance;
if (!exists fiance) {
print("``guy`` and ``gal`` just got engaged!");
guy.fiance = gal;
gal.fiance = guy;
}
else {
if (gal.prefers(guy)) {
print("``gal`` dumped ``fiance`` for ``guy``!");
fiance.fiance = null;
gal.fiance = guy;
guy.fiance = gal;
}
else {
print("``gal`` turned down ``guy`` and stayed with ``fiance``!");
}
}
}
}
}
}
void checkStability() {
variable value stabilityFlag = true;
for (gal in gals) {
for (guy in guys) {
if (guy.prefers(gal) && gal.prefers(guy)) {
stabilityFlag = false;
print("``guy`` prefers ``gal`` over ``guy.fiance else "nobody"``
and ``gal`` prefers ``guy`` over ``gal.fiance else "nobody"``!".normalized);
}
}
}
print("``if(!stabilityFlag) then "Not " else ""``Stable!");
}

View file

@ -0,0 +1,125 @@
# This is not optimized, but tries to follow the pseudocode given the Wikipedia entry below.
# Reference: https://en.wikipedia.org/wiki/Stable_marriage_problem#Algorithm
const males = ["abe", "bob", "col", "dan", "ed", "fred", "gav", "hal", "ian", "jon"]
const females = ["abi", "bea", "cath", "dee", "eve", "fay", "gay", "hope", "ivy", "jan"]
const malepreferences = Dict(
"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"]
)
const femalepreferences = Dict(
"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"]
)
function pshuf(d)
ret = Dict()
for (k,v) in d
ret[k] = shuffle(v)
end
ret
end
# helper functions for the verb: p1 "prefers" p2 over p3
pindexin(a, p) = ([i for i in 1:length(a) if a[i] == p])[1]
prefers(d, p1, p2, p3) = (pindexin(d[p1], p2) < pindexin(d[p1], p3))
function isstable(mmatchup, fmatchup, mpref, fpref)
for (mmatch, fmatch) in mmatchup
for f in mpref[mmatch]
if(f != fmatch && prefers(mpref, mmatch, f, fmatch)
&& prefers(fpref, f, mmatch, fmatchup[f]))
println("$mmatch prefers $f and $f prefers $mmatch over their current partners.")
return false
end
end
end
true
end
function galeshapley(men, women, malepref, femalepref)
# Initialize all m ∈ M and w ∈ W to free
mfree = Dict([(p, true) for p in men])
wfree = Dict([(p, true) for p in women])
mpairs = Dict()
wpairs = Dict()
while true # while ∃ free man m who still has a woman w to propose to
bachelors = [p for p in keys(mfree) if mfree[p]]
if(length(bachelors) == 0)
return mpairs, wpairs
end
for m in bachelors
for w in malepref[m] # w = first woman on ms list to whom m has not yet proposed
if(wfree[w]) # if w is free (else some pair (m', w) already exists)
#println("Free match: $m, $w")
mpairs[m] = w # (m, w) become engaged
wpairs[w] = m # double entry bookeeping
mfree[m] = false
wfree[w] = false
break
elseif(prefers(femalepref, w, m, wpairs[w])) # if w prefers m to m'
#println("Unmatch $(wpairs[w]), match: $m, $w")
mfree[wpairs[w]] = true # m' becomes free
mpairs[m] = w # (m, w) become engaged
wpairs[w] = m
mfree[m] = false
break
end # else (m', w) remain engaged, so continue
end
end
end
end
function tableprint(txt, ans, stab)
println(txt)
println(" Man Woman")
println(" ----- -----")
show(STDOUT, "text/plain", ans)
if(stab)
println("\n ----STABLE----\n\n")
else
println("\n ---UNSTABLE---\n\n")
end
end
println("Use the Gale Shapley algorithm to find a stable set of engagements.")
answer = galeshapley(males, females, malepreferences, femalepreferences)
stabl = isstable(answer[1], answer[2], malepreferences, femalepreferences)
tableprint("Original Data Table", answer[1], stabl)
println("To check this is not a one-off solution, run the function on a randomized sample.")
newmpref = pshuf(malepreferences)
newfpref = pshuf(femalepreferences)
answer = galeshapley(males, females, newmpref, newfpref)
stabl = isstable(answer[1], answer[2], newmpref, newfpref)
tableprint("Shuffled Preferences", answer[1], stabl)
# trade abe with bob
println("Perturb this set of engagements to form an unstable set of engagements then check this new set for stability.")
answer = galeshapley(males, females, malepreferences, femalepreferences)
fia1 = (answer[1])["abe"]
fia2 = (answer[1])["bob"]
answer[1]["abe"] = fia2
answer[1]["bob"] = fia1
answer[2][fia1] = "bob"
answer[2][fia2] = "abe"
stabl = isstable(answer[1], answer[2], malepreferences, femalepreferences)
tableprint("Original Data With Bob and Abe Switched", answer[1], stabl)

View file

@ -1,122 +1,106 @@
import java.util.*
data class Person(val name: String) {
val preferences = mutableListOf<Person>()
var matchedTo: Person? = null
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
}
fun trySwap(p: Person) {
if (prefers(p)) {
matchedTo?.matchedTo = null
matchedTo = p
p.matchedTo = this
}
}
override fun toString(): String {
val s = StringBuilder()
for ((k, v) in this) s.append("$k is engaged to $v\n")
return s.toString()
fun prefers(p: Person) = when (matchedTo) {
null -> true
else -> preferences.indexOf(p) < preferences.indexOf(matchedTo!!)
}
fun analyse(guys: People, girls: People) {
if (check(guys, girls))
println("Marriages are stable")
else
println("Marriages are unstable")
}
fun showMatch() = "$name <=> ${matchedTo?.name}"
}
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 match(males: Collection<Person>) {
while (males.find { it.matchedTo == null }?.also { match(it) } != null) {
}
}
fun match(male: Person) {
while (male.matchedTo == null) {
male.preferences.removeAt(0).trySwap(male)
}
}
fun isStableMatch(males: Collection<Person>, females: Collection<Person>): Boolean {
return males.all { isStableMatch(it, females) }
}
fun isStableMatch(male: Person, females: Collection<Person>): Boolean {
val likesBetter = females.filter { !male.preferences.contains(it) }
val stable = !likesBetter.any { it.prefers(male) }
if (!stable) {
println("#### Unstable pair: ${male.showMatch()}")
}
return stable
}
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 inMales = mapOf(
"abe" to mutableListOf("abi", "eve", "cath", "ivy", "jan", "dee", "fay", "bea", "hope", "gay"),
"bob" to mutableListOf("cath", "hope", "abi", "dee", "eve", "fay", "bea", "jan", "ivy", "gay"),
"col" to mutableListOf("hope", "eve", "abi", "dee", "bea", "fay", "ivy", "gay", "cath", "jan"),
"dan" to mutableListOf("ivy", "fay", "dee", "gay", "hope", "eve", "jan", "bea", "cath", "abi"),
"ed" to mutableListOf("jan", "dee", "bea", "cath", "fay", "eve", "abi", "ivy", "hope", "gay"),
"fred" to mutableListOf("bea", "abi", "dee", "gay", "eve", "ivy", "cath", "jan", "hope", "fay"),
"gav" to mutableListOf("gay", "eve", "ivy", "bea", "cath", "abi", "dee", "hope", "jan", "fay"),
"hal" to mutableListOf("abi", "eve", "hope", "fay", "ivy", "cath", "jan", "bea", "gay", "dee"),
"ian" to mutableListOf("hope", "cath", "dee", "gay", "bea", "abi", "fay", "ivy", "jan", "eve"),
"jon" to mutableListOf("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")))
val inFemales = mapOf(
"abi" to listOf("bob", "fred", "jon", "gav", "ian", "abe", "dan", "ed", "col", "hal"),
"bea" to listOf("bob", "abe", "col", "fred", "gav", "dan", "ian", "ed", "jon", "hal"),
"cath" to listOf("fred", "bob", "ed", "gav", "hal", "col", "ian", "abe", "dan", "jon"),
"dee" to listOf("fred", "jon", "col", "abe", "ian", "hal", "gav", "dan", "bob", "ed"),
"eve" to listOf("jon", "hal", "fred", "dan", "abe", "gav", "col", "ed", "ian", "bob"),
"fay" to listOf("bob", "abe", "ed", "ian", "jon", "dan", "fred", "gav", "col", "hal"),
"gay" to listOf("jon", "gav", "hal", "fred", "bob", "abe", "col", "ed", "dan", "ian"),
"hope" to listOf("gav", "jon", "bob", "abe", "ian", "dan", "hal", "ed", "col", "fred"),
"ivy" to listOf("ian", "col", "hal", "gav", "fred", "bob", "abe", "ed", "jon", "dan"),
"jan" to listOf("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)
fun buildPrefs(person: Person, stringPrefs: List<String>, population: List<Person>) {
person.preferences.addAll(
stringPrefs.map { name -> population.single { it.name == name } }
)
}
val males = inMales.keys.map { Person(it) }
val females = inFemales.keys.map { Person(it) }
males.forEach { buildPrefs(it, inMales[it.name]!!, females) }
females.forEach { buildPrefs(it, inFemales[it.name]!!, males) }
match(males)
males.forEach { println(it.showMatch()) }
println("#### match is stable: ${isStableMatch(males, females)}")
fun swapMatch(male1: Person, male2: Person) {
val female1 = male1.matchedTo!!
val female2 = male2.matchedTo!!
male1.matchedTo = female2
male2.matchedTo = female1
female1.matchedTo = male2
female2.matchedTo = male1
}
swapMatch(males.single { it.name == "fred" }, males.single { it.name == "jon" })
males.forEach { println(it.showMatch()) }
println("#### match is stable: ${isStableMatch(males, females)}")
}