Reputation:
the rules are five students won five different places in five different disciplines - Alex, Bob, John, Deo, Sam.
The language student placed higher than Bob as much as Bob placed higher than the law student.
The IT student placed three spots higher than John.
Alex placed at an even rank and the math student at an odd one.
Sam placed two places below the physicist.
Who placed where and what do they study?
How do I go about accounting for the two unknowns in the conditions?
Upvotes: 0
Views: 191
Reputation: 4377
One method, after a bit of performance improvement:
go(Sol) :-
People = [alex, bob, john, deo, sam],
Topics = [lang, law, it, math, phys],
TopicsP = [LangP, LawP, ItP, MathP, PhysP],
Placements = [1, 2, 3, 4, 5],
CombLsts = [People, Topics, Placements],
% Break symmetry by specifying the people order
Sol = [
[alex, _, AlexP],
[bob, _, BobP],
[john, _, JohnP],
[deo, _, _],
[sam, _, SamP]
],
% Rule 1
when((nonvar(LangP), nonvar(BobP)), (
% Caution with "placed higher" = lower
LangPDiff is BobP - LangP,
LangPDiff >= 1,
when(nonvar(LawP), (
BobPDiff is LawP - BobP,
LangPDiff =:= BobPDiff
))
)),
% Rule 2
% Caution with "placed higher" = lower
when_both(ItP, JohnP, ItP is JohnP - 3),
% Rule 3
freeze(AlexP, 0 is AlexP mod 2),
freeze(MathP, 1 is MathP mod 2),
% Rule 4
% "places below" = higher number
when_both(SamP, PhysP, SamP is PhysP + 2),
% Find a solution
find_combs(CombLsts, Sol, Topics, TopicsP).
find_combs(CombLsts, [Comb|Sol], Topics, TopicsP) :-
% Select a combination, with remainder
maplist(select, Comb, CombLsts, Rem),
% Populate TopicsP
Comb = [_, Topic, Placement],
update_topic_topicp(Topics, Topic, Placement, TopicsP),
% Continue down this combination path
find_combs(Rem, Sol, Topics, TopicsP).
find_combs(CombLsts, _, _, _) :-
% Nothing left to try
maplist(=([]), CombLsts).
update_topic_topicp([HT|TT], Topic, Placement, [HTP|TTP]) :-
( HT = Topic -> HTP = Placement
; update_topic_topicp(TT, Topic, Placement, TTP)
).
when_both(V1, V2, Cond) :-
when((nonvar(V1), nonvar(V2)), Cond).
Result:
?- time(findall(S, go(S), Ss)).
% 23,998 inferences, 0.011 CPU in 0.011 seconds (101% CPU, 2111377 Lips)
Ss = [[[alex,lang,2],[bob,phys,3],[john,law,4],[deo,it,1],[sam,math,5]]].
Upvotes: 0
Reputation: 28963
How do I go about accounting for the two unknowns in the conditions?
I'm not sure which unknowns you are thinking of, but these kind of puzzles usually settle out into a single answer, and the rules that are given leave no choices for the other things. You know that the people need to be in five positions and five subjects; you can do as brebs does, and build a way for Prolog to try all possible answers and rule some out.
Or, this is ideal for the clpfd constraint solver, the people must get positions 1-5, and so must the languages, and they can't be several in the same position, and then it will find possible answers for which those constraints hold.
:- use_module(library(clpfd)). % Finite domain constraints
constrain(People, Disciplines) :-
People = [Alex, Bob, John, Deo, Sam],
People ins 1..5,
all_different(People),
Disciplines = [Language, Law, IT, Math, Physics],
Disciplines ins 1..5,
all_different(Disciplines),
(Bob - Language) #= (Law - Bob), % Rule 1
(Bob - Language) #>= 1, % Rule 1 tiebreaker: difference can't be 0
IT #= John - 3, % Rule 2
0 #= Alex mod 2, % Rule 3
1 #= Math mod 2, % Rule 3
Sam #= Physics + 2. % Rule 4
e.g.
?- constrain([Alex,Bob,John,Deo,Sam], [Language, Law, IT, Math, Physics]),
label([Alex,Bob,John,Deo,Sam]),
label([Language, Law, IT, Math, Physics]).
Deo = IT, IT = 1,
Alex = Language, Language = 2,
Bob = Physics, Physics = 3,
John = Law, Law = 4,
Math = Sam, Sam = 5
Upvotes: 0