Stable marriage problem: Difference between revisions

Content added Content deleted
m (→‎{{header|Haskell}}: Swapped last lang tag pair to pre to stop highlighting of random 'and'.)
(Added SPARK implementation and test output.)
Line 1,066: Line 1,066:
fay and jon like each other better than their present partners: dan and bea, respectively
fay and jon like each other better than their present partners: dan and bea, respectively
Engagement stability check FAILED</pre>
Engagement stability check FAILED</pre>

=={{header|SPARK}}==
The data set package:
<lang ada>package Preferences
is

type Guy_X is (no_guy, abe, bob, col, dan, ed, fred, gav, hal, ian, jon);
subtype Guy is Guy_X range Guy_X'Succ(Guy_X'First) .. Guy_X'Last;

type Girl_X is (no_girl, abi, bea, cath, dee, eve, fay, gay, hope, ivy, jan);
subtype Girl is Girl_X range Girl_X'Succ(Girl_X'First) .. Girl_X'Last;

type Extended_Rank is range 0 .. 10;
subtype Rank is Extended_Rank range 1 .. Extended_Rank'Last;

type His_Rank is array (Rank) of Girl;
type He_Prefers is array (Guy) of His_Rank;

Guys_Like : constant He_Prefers := He_Prefers'(
abe => His_Rank'(abi, eve, cath, ivy, jan, dee, fay, bea, hope, gay),
bob => His_Rank'(cath, hope, abi, dee, eve, fay, bea, jan, ivy, gay),
col => His_Rank'(hope, eve, abi, dee, bea, fay, ivy, gay, cath, jan),
dan => His_Rank'(ivy, fay, dee, gay, hope, eve, jan, bea, cath, abi),
ed => His_Rank'(jan, dee, bea, cath, fay, eve, abi, ivy, hope, gay),
fred => His_Rank'(bea, abi, dee, gay, eve, ivy, cath, jan, hope, fay),
gav => His_Rank'(gay, eve, ivy, bea, cath, abi, dee, hope, jan, fay),
hal => His_Rank'(abi, eve, hope, fay, ivy, cath, jan, bea, gay, dee),
ian => His_Rank'(hope, cath, dee, gay, bea, abi, fay, ivy, jan, eve),
jon => His_Rank'(abi, fay, jan, gay, eve, bea, dee, cath, ivy, hope));

type Her_Rank is array(Rank) of Guy;
type She_Prefers is array (Girl) of Her_Rank;

Girls_Like : constant She_Prefers := She_Prefers'(
abi => Her_Rank'(bob, fred, jon, gav, ian, abe, dan, ed, col, hal),
bea => Her_Rank'(bob, abe, col, fred, gav, dan, ian, ed, jon, hal),
cath => Her_Rank'(fred, bob, ed, gav, hal, col, ian, abe, dan, jon),
dee => Her_Rank'(fred, jon, col, abe, ian, hal, gav, dan, bob, ed),
eve => Her_Rank'(jon, hal, fred, dan, abe, gav, col, ed, ian, bob),
fay => Her_Rank'(bob, abe, ed, ian, jon, dan, fred, gav, col, hal),
gay => Her_Rank'(jon, gav, hal, fred, bob, abe, col, ed, dan, ian),
hope => Her_Rank'(gav, jon, bob, abe, ian, dan, hal, ed, col, fred),
ivy => Her_Rank'(ian, col, hal, gav, fred, bob, abe, ed, jon, dan),
jan => Her_Rank'(ed, hal, gav, abe, bob, jon, col, ian, fred, dan));

end Preferences;
</lang>
The package for creating the engagements and checking stability. This package can be analysed by the SPARK tools and proved to be free of any run-time error.
<lang ada>with Preferences;
--# inherit Preferences;
package Propose
is

type Engagements is array (Preferences.Girl) of Preferences.Guy_X;

procedure Engage (Pairs : out Engagements);
--# derives Pairs from ;

procedure Check_Stable (Pairs : in Engagements;
OK : out Boolean;
Other_Girl : out Preferences.Girl_X;
Other_Guy : out Preferences.Guy_X);
--# derives OK,
--# Other_Girl,
--# Other_Guy from Pairs;

end Propose;
</lang>
<lang ada>with Preferences;
use type Preferences.Extended_Rank;
use type Preferences.Girl;
use type Preferences.Guy;
package body Propose
is

-- renaming subtypes:
subtype Guy is Preferences.Guy;
subtype Girl is Preferences.Girl;

function Her_Rank_Of_Him (Her : Girl;
Him : Guy) return Preferences.Rank
is
R : Preferences.Rank;
begin
R := Preferences.Rank'First;
while Preferences.Girls_Like(Her)(R) /= Him
and R /= Preferences.Rank'Last
loop
R := Preferences.Rank'Succ(R);
end loop;
return R;
end Her_Rank_Of_Him;

function His_Rank_Of_Her (Him : Guy;
Her : Girl) return Preferences.Rank
is
R : Preferences.Rank;
begin
R := Preferences.Rank'First;
while Preferences.Guys_Like(Him)(R) /= Her
and R /= Preferences.Rank'Last
loop
R := Preferences.Rank'Succ(R);
end loop;
return R;
end His_Rank_Of_Her;

procedure Engage (Pairs : out Engagements)
is
type Free_Guy is array (Guy) of Boolean;
type Free_Girl is array (Girl) of Boolean;
type Last_Proposals is array (Guy) of Preferences.Extended_Rank;

-- Initialize all M in M_Free and W in W_Free to free.
M_Free : Free_Guy := Free_Guy'(others => True);
W_Free : Free_Girl := Free_Girl'(others => True);
Last_Proposal : Last_Proposals :=
Last_Proposals'(others => Preferences.Extended_Rank'First);

All_Paired : Boolean := False;
W : Girl;
M1 : Preferences.Guy_X;

begin
-- Initially set all engagements to null.
Pairs := Engagements'(others => Preferences.No_Guy);
-- while there is a free man M who still has a woman W to propose to
while not All_Paired loop
All_Paired := True;
for M in Guy loop
if M_Free(M) and Last_Proposal(M) < Preferences.Rank'Last then
All_Paired := False;
-- W = M's highest ranked such woman who he has not
-- proposed to yet
Last_Proposal(M) := Preferences.Rank'Succ(Last_Proposal(M));
W := Preferences.Guys_Like(M)(Last_Proposal(M));
-- if W is free
if W_Free(W) then
-- (M, W) become engaged
M_Free(M) := False;
W_Free(W) := False;
Pairs(W) := M;
else
-- else some pair (M1, W) already exists
M1 := Pairs(W);
if M1 > Preferences.no_guy and then
-- if W prefers M to M1
Her_Rank_Of_Him (Her => W, Him => M)
< Her_Rank_Of_Him (Her => W, Him => M1)
then
-- (M, W) become engaged
M_Free(M) := False;
Pairs(W) := M;
-- M1 becomes free
M_Free(M1) := True;
else
-- (M1, W) remain engaged
null;
end if;
end if;
end if;
end loop;
end loop;
end Engage;

procedure Check_Stable (Pairs : in Engagements;
OK : out Boolean;
Other_Girl : out Preferences.Girl_X;
Other_Guy : out Preferences.Guy_X)
is
M : Preferences.Guy_X;
W_Rank : Preferences.Rank;
begin
OK := True;
Other_Girl := Preferences.No_Girl;
Other_Guy := Preferences.No_Guy;
-- Loop over all girls.
for W in Girl loop
if Pairs(W) > Preferences.no_guy then
W_Rank := Her_Rank_Of_Him (W, Pairs(W));
-- Loop over all guys she prefers to her current guy.
for WR in Preferences.Rank range 1 .. W_Rank - 1 loop
M := Preferences.Girls_Like(W)(WR);
if M > Preferences.no_guy then
-- Loop over all girls for this guy in preference order.
for MR in Preferences.Rank
--# assert M > Preferences.no_guy;
loop
-- Exit if his current girl found before this girl.
exit when M = Pairs(Preferences.Guys_Like(M)(MR));
-- Unstable if this girl found before his current girl.
if Preferences.Guys_Like(M)(MR) = W then
OK := False;
Other_Girl := W;
Other_Guy := M;
end if;
end loop;
end if;
exit when not OK;
end loop;
end if;
exit when not OK;
end loop;
end Check_Stable;

end Propose;
</lang>
The test program tests all pairwise exchanges. This is Ada, it is not SPARK.

(Text IO is quite tedious in SPARK - it's not what the language was designed for.)
<lang ada>------------------------------------
-- Test program.
--
-- This is Ada, it is not SPARK.
------------------------------------

with Ada.Text_IO;
with Preferences;
with Propose;
procedure Matchmaker
is
-- renaming subtypes:
subtype Guy is Preferences.Guy;
subtype Girl is Preferences.Girl;

Marriages : Propose.Engagements;
Stable : Boolean;
Him : Preferences.Guy_X;
Her : Preferences.Girl_X;
Stable_Marriages : Propose.Engagements;

procedure Report_Stable
is
begin
if Stable then
Ada.Text_IO.Put_Line ("Pairs are Stable");
else
Ada.Text_IO.Put ("Pairs are Unstable: ");
Ada.Text_IO.Put_Line
(Guy'Image(Him) & " and " & Girl'Image(Her) & " prefer each other.");
end if;
end Report_Stable;

begin

Propose.Engage(Pairs => Marriages);
for W in Girl loop
Ada.Text_IO.Put_Line (Girl'Image(W) &
" marries " &
Guy'Image(Marriages(W)));
end loop;
Propose.Check_Stable (Pairs => Marriages,
OK => Stable,
Other_Girl => Her,
Other_Guy => Him);
Report_Stable;
Stable_Marriages := Marriages;

for W1 in Girl range Girl'First .. Girl'Pred(Girl'Last) loop
for W2 in Girl range Girl'Succ(W1) .. Girl'Last loop
Ada.Text_IO.New_Line;
Ada.Text_IO.Put_Line ("Exchange " & Guy'Image(Marriages(W1)) &
" with " & Guy'Image(Marriages(W2)));
Him := Marriages(W1);
Marriages(W1) := Marriages(W2);
Marriages(W2) := Him;
Propose.Check_Stable (Pairs => Marriages,
OK => Stable,
Other_Girl => Her,
Other_Guy => Him);
Report_Stable;
Marriages := Stable_Marriages;
end loop;
end loop;

end MatchMaker;
</lang>
The begining of the output from the test. All pairwise exchanges create unstable pairings.
<pre>ABI marries JON
BEA marries FRED
CATH marries BOB
DEE marries COL
EVE marries HAL
FAY marries DAN
GAY marries GAV
HOPE marries IAN
IVY marries ABE
JAN marries ED
Pairs are Stable

Exchange JON with FRED
Pairs are Unstable: FRED and BEA prefer each other.

Exchange JON with BOB
Pairs are Unstable: BOB and CATH prefer each other.

Exchange JON with COL
Pairs are Unstable: JON and ABI prefer each other.
</pre>


=={{header|Tcl}}==
=={{header|Tcl}}==