Logick é programovanie 4

46
Logické programovanie 4 backtracking a logické hádanky “zebra problem” alias kto chová rybičky ? cesta v grafe - prehľadávanie stavového priestor hanojské veže iné grafové algoritmy jednoťažky farbenie grafu prehľadávanie grafu do šírky druhorádové predikáty Cvičenie grafové algoritmy most, artikulácia grafu, súvislé komponenty, ...

description

Logick é programovanie 4. backtracking a logické hádanky “ zebra problem ” alias kto chová rybičky ? cesta v grafe - prehľadávanie stavového priestor hanojské veže i n é g rafov é algoritmy jednoťažky farbenie grafu prehľadávanie grafu do šírky druhorádové predikáty Cvičenie - PowerPoint PPT Presentation

Transcript of Logick é programovanie 4

Page 1: Logick é programovanie  4

Logické programovanie 4 backtracking a logické hádanky

“zebra problem” alias kto chová rybičky ? cesta v grafe - prehľadávanie stavového priestor

hanojské veže iné grafové algoritmy

jednoťažky farbenie grafu

prehľadávanie grafu do šírky druhorádové predikáty

Cvičenie grafové algoritmy

most, artikulácia grafu, súvislé komponenty, ...

Page 2: Logick é programovanie  4

Susedia (zebra problem)

Je rada piatich domov, pričom každý má inú farbu. V týchto domoch žije päť ľudí rôznych národností. Každý z nich chová iné zviera, rád pije iný nápoj a fajčí iné cigarety.

1. Brit býva v červenom dome.2. Švéd chová psa.3. Dán pije čaj.4. Zelený dom stojí hneď naľavo od bieleho.5. Majiteľ zeleného domu pije kávu.6. Ten, kto fajčí Pall Mall, chová vtáka.7. Majiteľ žltého domu fajčí Dunhill.8. Človek z prostredného domu pije mlieko.9. Nór býva v prvom dome.10. Ten, kto fajčí Blend, býva vedľa toho, kto chová mačku.11. Ten, kto chová kone, býva vedľa toho, kto fajčí Dunhill.12. Ten, kto fajčí Blue Master, pije pivo.13. Nemec fajčí Prince.14. Nór býva vedľa modrého domu.15. Ten, kto fajčí Blend, má suseda, ktorý pije vodu.Kto chová rybičky? (patríte medzi tie 2%) ?

Tento kvíz údane vymyslel Albert Einstein a údajne ho 98% ľudí vôbec nevyrieši.

Page 3: Logick é programovanie  4

Susedia - 1% dom 1 2 3 4 5% narod N1 N2 N3 N4 N5% zviera Z1 Z2 Z3 Z4 Z5% napoj P1 P2 P3 P4 P5% fajci F1 F2 F3 F4 F5% farba C1 C2 C3 C4 C5

susedia(N,Z,P,F,C) :-

N=[N1,N2,N3,N4,N5], perm([brit,sved,dan,nor,nemec],N),N1=nor, %- Nór býva v prvom dome

P=[P1,P2,P3,P4,P5], perm([caj,voda,pivo,kava,mlieko],P),P3=mlieko, ... %- Človek z prostredného domu pije mlieko

domy sú v rade indexované 1..5

Page 4: Logick é programovanie  4

Susedia - 2 Dán pije čaj.

index(dan,N,I2), index(caj,P,I2),

Brit býva v červenom dome.C=[C1,C2,C3,C4,C5], perm([cerveny,biely,modry,zlty,zeleny],C),

index(brit,N,I3), index(cerveny,C,I3),

Ten, kto fajčí Blend, býva vedľa toho, kto chová mačku.F=[F1,F2,F3,F4,F5], perm([pallmall,dunhill,prince,blend,bluemaster],F),

index(blend,F,I10), index(macka,Z,I11), vedla(I10,I11),

vedla(I,J) :- I is J+1 ; J is I+1.

z minulej prednášky:predikát index(X,Xs,I), ktorý platí, ak Xsi = Xindex(X,[X|_],1).index(X,[_|Ys],I):-index(X,Ys,I1),I is I1+1.

Page 5: Logick é programovanie  4

Susedia - 3?- susedia(N,Z,P,F,C).

N = [nor, dan, brit, nemec, sved]Z = [macka, kon, vtak, rybicky, pes]P = [voda, caj, mlieko, kava, pivo]F = [dunhill, blend, pallmall, prince, bluemaster]C = [zlty, modry, cerveny, zeleny, biely] ;

No

Kto chová rybičky?

Page 6: Logick é programovanie  4

Susedia(iné riešenie, iná reprezentácia)

Houses = [ [N1,Z1,F1,P1,C1], % 1.dom [národ,zviera,fajčí,pije,farba]

[N2,Z2,F2,P2,C2], % 2.dom [N3,Z3,F3,P3,C3], % 3.dom[N4,Z4,F4,P4,C4], % 4.dom[N5,Z5,F5,P5,C5] ].% 5.dom

ako vyjadríme fakt, že: nór býva v prvom dome

Houses = [[norwegian, _, _, _, _] | _] človek z prostredného domu pije mlieko

Houses = [ _, _, [_, _, milk, _, _], _, _] dán pije čaj

member([dane, _, _, tea, _], Houses) v susednom dome od … definujme pomocné predikáty next_to,

irightnext_to(X, Y, List) :- iright(X, Y, List) ; iright(Y, X, List).iright(L, R, [L, R | _]).iright(L, R, [_ | Rest]) :- iright(L, R, Rest).

http://sandbox.rulemaker.net/ngps/blog/119.html

Page 7: Logick é programovanie  4

Susedia (alias zebra problem)einstein(Houses, Fish_Owner) :- Houses = [[norwegian, _, _, _, _], _, [_, _, _, milk, _], _, _], member([brit, _, _, _, red], Houses), member([swede, dog, _, _, _], Houses), member([dane, _, _, tea, _], Houses), iright([_, _, _, _, green], [_, _, _, _, white], Houses), member([_, _, _, coffee, green], Houses), member([_, bird, pallmall, _, _], Houses), member([_, _, dunhill, _, yellow], Houses), next_to([_, _, dunhill, _, _], [_, horse, _, _, _], Houses), next_to([_, _, blend, _, _], [_, cat, _, _, _], Houses), next_to([_, _, blend, _, _], [_, _, _, water, _], Houses), member([_, _, bluemaster, beer, _], Houses), member([german, _, prince, _, _], Houses), next_to([norwegian, _, _, _, _], [_, _, _, _, blue], Houses),

member([Fish_Owner, fish, _, _, _], Houses). % kto chová rybičky ?

http://sandbox.rulemaker.net/ngps/blog/119.html

?- einstein(Houses, Fish_Owner).

Page 8: Logick é programovanie  4

Cesta v grafe majme graf definovaný predikátom hrana/2

hrana(a,b). hrana(c,a). hrana(c,b). hrana(c,d).

predikát cesta/2 znamená, že medzi X a Y existuje postupnosť hráncesta(X, X).cesta(X, Y) :- hrana(X, Z), cesta (Z, Y).cesta(X, Y) :- (hrana(X, Z) ; hrana(Z, X)), cesta (Z, Y).

?-cesta(a,d).no ?- cesta(a,d).…

a

cd

b

Page 9: Logick é programovanie  4

Cesta v cyklickom grafe

neohrana(X,Y) :- hrana(X,Y).neohrana(X,Y) :- hrana(Y,X).

cesta(X, X, _).cesta(X, Y,C):-neohrana(X, Z), not member(Z,C), cesta (Z, Y,[Z|C]).

?-cesta(a,d,[a]).yes

cesta(X, X, C, Res) :- Res = C.cesta(X, Y,C,Res) :- neohrana(X, Z),

not member(Z,C), cesta (Z, Y,[Z|C], Res).

?- cesta(a,d,[],Res).Res = [a,c,d]

a

cd

b

Page 10: Logick é programovanie  4

Misionári a kanibali(príklad použitia prehľadávania grafu – stavového priestoru)

Traja misionári a traja kanibali sa stretli na jednom brehu rieky. Na brehu bola malá loďka, na ktorú sa zmestia maximálne dve osoby. Všetci sa chcú prepraviť na druhý breh, ale na žiadnom brehu nesmie nikdy zostať prevaha kanibalov nad misionármi, inak by mohlo dôjst k tragédií. Akým spôsobom sa majú dostať na druhý breh?

http://profuvsvet.ic.cz/view.php?cisloclanku=2007040006

Page 11: Logick é programovanie  4

Misionári a kanibali(príklad použitia prehľadávania grafu do hľbky)

check(M,K) :- M = 0 ; K =< M.check2(M,K) :- check(M,K), M1 is 3-M, K1 is 3-K, check(M1, K1).

init(state(3,3,l)). % vľavo loďka, 3 missio a 3 canibsfinal(state(0,0,r)). % vpravo loďka, 3 missio a 3 canibs

% príklad prechodového pravidlahrana(state(M,K,l), state(M1, K1, r)) :- check2(M,K),

((M>1, M1 is M-2, K1 is K); (M>0, M1 is M-1, K1 is K); (K>0, M1 is M, K1 is K-1); (M>0, K>0, M1 is M-1, K1 is K-1); (K>1, M1 is M, K1 is K-2)),check2(M1,K1).

misio :- init(I), final(F), cesta(I,F,[],P), write(P).[state(0, 0, r), state(1, 1, l), state(0, 1, r), state(0, 3, l), state(0, 2, r), state(2, 2, l), state(1, 1, r), state(3, 1, l), state(3, 0, r), state(3, 2, l), state(2, 2, r), state(3, 3, l)]

Page 12: Logick é programovanie  4

Japončíci

http://freeweb.siol.net/danej/riverIQGame.swf

Pravidla hry jsou následující:

1. Na voru se mohou vést najednou maximálně dvě osoby.2. Otec nemůže zůstat ani s jednou dcerou bez přítomnosti matky3. Matka nemůže zůstat ani s jedním synem bez přítomnosti otce4. Kriminálník (v pruhovaném obleku) nemůže zůstat ani s jedním členem rodiny bez přítomnosti policisty.5. Jen otec, matka a policista se umí plavit na voru.

Page 13: Logick é programovanie  4

japonci – stavy na ľavobrehu%--- [boat,father,mother,sons,daughters,policeman,criminal]

%--- next(state1, state2)next([1,1,M,S,D,P,C], [0,0,M,S,D,P,C]).next([1,F,1,S,D,P,C], [0,F,0,S,D,P,C]).next([1,F,M,S,D,1,C], [0,F,M,S,D,0,C]).next([1,1,1,S,D,P,C], [0,0,0,S,D,P,C]).. . . . .

next([1,1,M,S,D,P,C], [0,0,M,SX,D,P,C]) :- between(1,2,S), succ(SX,S).next([1,F,M,S,D,1,C], [0,F,M,SX,D,0,C]) :- between(1,2,S), succ(SX,S).next([1,F,M,S,D,1,C], [0,F,M,S,DX,0,C]) :- between(1,2,D), succ(DX,D).

. . . . .

Page 14: Logick é programovanie  4

japonci – kritické situácie

valid_father([_,F,M,_,D,_,_]) :- F = M; (F = 1, D = 0); (F = 0, D = 2).

valid_mother([_,F,M,S,_,_,_]) :- F = M; (M = 1, S = 0); (M = 0, S = 2).

valid_criminal([_,F,M,S,D,P,C]) :- C = P; (C = 1, F = 0, M = 0, S = 0, D = 0)

; (C = 0, F = 1, M = 1, S = 2, D = 2).

valid(S) :- valid_father(S), valid_mother(S), valid_criminal(S).

Page 15: Logick é programovanie  4

japonci – riešenie ?- solve.

[boat,father,mother,sons,daughters,policeman,criminal[1, 1, 1, 2, 2, 1, 1] počiatočný stav[0, 1, 1, 2, 2, 0, 0] P+C ->[1, 1, 1, 2, 2, 1, 0] <- P[0, 1, 1, 1, 2, 0, 0] P+S ->[1, 1, 1, 1, 2, 1, 1] <- P+C[0, 0, 1, 0, 2, 1, 1] F+S ->[1, 1, 1, 0, 2, 1, 1] <- F[0, 0, 0, 0, 2, 1, 1] F+M ->[1, 0, 1, 0, 2, 1, 1] <- M[0, 0, 1, 0, 2, 0, 0] P+C ->[1, 1, 1, 0, 2, 0, 0] <- F[0, 0, 0, 0, 2, 0, 0] M+F ->[1, 0, 1, 0, 2, 0, 0] <- M[0, 0, 0, 0, 1, 0, 0] M+D->[1, 0, 0, 0, 1, 1, 1] <- P+C[0, 0, 0, 0, 0, 0, 1] P+D->[1, 0, 0, 0, 0, 1, 1] <- P[0, 0, 0, 0, 0, 0, 0] P+C ->

Page 16: Logick é programovanie  4

Hanoi classiquehttp://www.csupomona.edu/~jrfisher/www/prolog_tutorial/2_3.html

•hmove(1,X,Y,_) :- write('Move top disk from '), write(X), write(' to '), write(Y), nl. •hmove(N,X,Y,Z) :- N>1, N1 is N-1, hmove(N1,X,Z,Y), hmove(1,X,Y,_), hmove(N1,Z,Y,X).

?- hmove(3,a,b,c).Move top disk from a to bMove top disk from a to cMove top disk from b to cMove top disk from a to bMove top disk from c to aMove top disk from c to bMove top disk from a to b

Page 17: Logick é programovanie  4

Hanoi inak•počiatočný a koncový stav v tvare Xs:Ys:Zs (kotúče sú očíslované)hanoi_init(N,Xs:[]:[]):-jednaAzN(N,Xs).hanoi_final(N,[]:[]:Zs):-jednaAzN(N,Zs).

•legálne prechody zo stavu do stavu (trochu rozšafný test hanoi_move([X|Xs]:Ys:Zs,Xs:[X|Ys]:Zs) :- vzostupne([X|Ys]).hanoi_move([X|Xs]:Ys:Zs,Xs:Ys:[X|Zs]) :- vzostupne([X|Zs]).hanoi_move(Xs:[Y|Ys]:Zs,[Y|Xs]:Ys:Zs) :- vzostupne([Y|Xs]).hanoi_move(Xs:[Y|Ys]:Zs,Xs:Ys:[Y|Zs]) :- vzostupne([Y|Zs]).hanoi_move(Xs:Ys:[Z|Zs],[Z|Xs]:Ys:Zs) :- vzostupne([Z|Xs]).hanoi_move(Xs:Ys:[Z|Zs],Xs:[Z|Ys]:Zs) :- vzostupne([Z|Ys]).

doit(N,Len) :- hanoi_init(N,I), hanoi_final(N,F), cesta(I,F,P), length(P,Len),write(P),nl,write(Len),nl,fail.

Page 18: Logick é programovanie  4

Hanoi ešte inak

hanoi_move([X|Xs]:Ys:Zs,Xs:[X|Ys]:Zs) :- vzostupneParNepar([X|Ys]).

hanoi_move([X|Xs]:Ys:Zs,Xs:Ys:[X|Zs]) :- vzostupneParNepar([X|Zs]).

hanoi_move(Xs:[Y|Ys]:Zs,[Y|Xs]:Ys:Zs) :- vzostupneParNepar([Y|Xs]).

hanoi_move(Xs:[Y|Ys]:Zs,Xs:Ys:[Y|Zs]) :- vzostupneParNepar([Y|Zs]).

hanoi_move(Xs:Ys:[Z|Zs],[Z|Xs]:Ys:Zs) :- vzostupneParNepar([Z|Xs]).

hanoi_move(Xs:Ys:[Z|Zs],Xs:[Z|Ys]:Zs) :- vzostupneParNepar([Z|Ys]).

?- doit(3,L).[[]:[]:[1, 2, 3], [1]:[]:[2, 3], []:[1]:[2, 3], [2]:[1]:[3], [1, 2]:[]:[3], [1, 2]:[3]:[], [2]:[3]:[1], []:[2, 3]:[1], []:[1, 2, 3]:[], [1]:[2, 3]:[], [1]:[3]:[2], []:[3]:[1, 2], [3]:[]:[1, 2], [3]:[1]:[2], [2, 3]:[1]:[], [1, 2, 3]:[]:[]]16;

?- doit(4,16).[[]:[]:[1, 2, 3, 4], []:[1]:[2, 3, 4], [2]:[1]:[3, 4], [1, 2]:[]:[3, 4], [1, 2]:[3]:[4], [2]:[3]:[1, 4], []:[2, 3]:[1, 4], []:[1, 2, 3]:[4], [4]:[1, 2, 3]:[], [1, 4]:[2, 3]:[], [1, 4]:[3]:[2], [4]:[3]:[1, 2], [3, 4]:[]:[1, 2], [3, 4]:[1]:[2], [2, 3, 4]:[1]:[], [1, 2, 3, 4]:[]:[]]16

?- doit(4,16).

Page 19: Logick é programovanie  4

Reprezentácia grafu reprezentácia grafu: hrana(X,Y)

hrana(a,b). hrana(c,a). hrana(c,b). hrana(c,d). ?- hrana(c,X).X = a;X = b;X = d;No

iná reprezentácia grafu: susedia(c,[a,b,d])

ako z prvej reprezentácie do druhej do druhej ??

s tým, čo vieme, to nejde ... lebo backtracking...

http://406notacceptable.com/java/breadth-first-tree-traversal-in-java/

Page 20: Logick é programovanie  4

Druho-rádové predikáty bagof(Term, Cieľ, Bag) –

Bag je zoznam inštancií Termu pre riešenia Cieľa

setof(Term, Cieľ, Set) –Set je množina inštancií Termu pre riešenia Cieľa

Príklady: [1..N]

jednaAzN(N,List) :- bagof(X,between(1,N,X),List).

všetky kombinácie päťprvkovej množinyallCombinations(List) :- setof(C,comb(C,[1,2,3,4,5]),List).

Page 21: Logick é programovanie  4

Existenčné premenné

?- bagof(X,(between(1,3,X),member(Y,[a,b,c])),Bag).Y = a, Bag = [1, 2, 3] ;Y = b, Bag = [1, 2, 3] ;Y = c, Bag = [1, 2, 3].

% existuje také Y, že X patrí do 1..3 a Y do a..c?- bagof(X,Y^(between(1,3,X),member(Y,[a,b,c])),Bag).Bag = [1, 1, 1, 2, 2, 2, 3, 3, 3].

findall(Term, Cieľ, Bag) – Bag je zoznam inštancií Termu pre riešenia Cieľa,

pričom všetky voľné premenné sú existenčne viazané

?- findall(X,(between(1,3,X),member(Y,[a,b,c])),Bag).Bag = [1, 1, 1, 2, 2, 2, 3, 3, 3].

Page 22: Logick é programovanie  4

Susedia, stupeň vrchola súvislé komponenty grafu obsahujúci X

suvislyKomp(X,Comp):- setof(Y,Path^cesta(X,Y,[],Path),Comp).

susedia(X, Neibourghs) :- setof(Y,hrana(X,Y), Neibourghs).

graph(G) :- setof( (X,Neibourghs), susedia(X, Neibourghs),G).

?- graph(G).G = [ (a, [b]), (b, []), (c, [a, b, d]), (d, []) ]

stupenVrchola(X,N) :- susedia(X, Neibourghs), length(Neibourghs,N).

späť na grafy...

a

cd

b

Page 23: Logick é programovanie  4

Prehľadávanie grafu do šírky

cesta(X,Y) :- cestaDoSirky([X],[],Y).

cestaDoSirky([Y|_],_,Y).cestaDoSirky([X|Xs],Navstivene,Y):-X\=Y, % zober všetky dostupné vrcholy z X, kde si ešte nebol

setof(Z,(hrana(X,Z), not(member(Z,Navstivene))),Nasledovnici), % pridaj ich do fronty, na začiatok či koniec ?

%append(Nasledovnici,Xs,Xs1), -- toto je do hľbkyappend(Xs,Nasledovnici,Xs1), -- a toto do šírky

% opakuj v rekurzii kým vo fronte nie je vrchol kam smeruješcestaDoSirky(Xs1,[X|Navstivene],Y).

+ dostaneme najkratšiu cestu- nevidíme ju (priamo)Treba sa zamyslieť nad tým, ako si uchovať informácie pre rekonštrukciu

cesty

Page 24: Logick é programovanie  4

Symbol rezu (cut)Symbol rezu – označovaný predikátovým symbolom ! -

predstavuje cieľ, ktorý je v triviálne splnený, má jedno riešenie, a pri pokuse o znovusplnenie je neúspešný. Okrem toho zakáže hľadať alternatívne riešenia pre:

ciele vľavo od neho v klauzule, kde sa nachádza, a vo všetkých klauzulách nachádzajúcich sa pod touto klauzulou.

Príklad: Ak neplatí x, k symbolu ! sa výpočet nedostane. Ak sa podarí nájsť riešenie pre x, ! platí triviálne, hľadajú sa riešenia pre y.Side-effect symbolu ! je to, že iné riešenia pre x sa už nehľadajú ďalšie klauzuly pre b sa už nepoužijú

Príklad:foo:- a, b, c. b :- x,!, y. b :- z, w. –-–––––––

Page 25: Logick é programovanie  4

Príklad použitia !(if-then-else)

definujme predikát, ktorý implementuje tradičný if-then-elseifthenelse(C,T,E) :- C,T.ifthenelse(C,T,E) :- not(C),E.

not(C) sa implementuje tak, že sa spustí výpočet C, a ak existuje riešenie pre C, not(C) neplatí, neexistuje riešenie pre C, not(C) platí.tzv. negation as a failure.

Problém: predikát C sa vyhodnocuje dvakrát v prípade, ak neplatí C.Riešenie (pomocou symbolu rezu):

ifthenelse(C,T,E) :- C,!,T.ifthenelse(C,T,E) :- E.

Veľký problém: po sémantickej stránke je to katastrofa.

Page 26: Logick é programovanie  4

Príklady použitia symbolu rezupríklad z generátora zoznamunAzJedna(0,[]).nAzJedna(N,[N|X]):-N>0,N1 is N-1,nAzJedna(N1,X).

pomocou !nTo1(0,[]):-!.nTo1(N,[N|X]):-N1 is N-1,nTo1(N1,X).

Minulé cvičenie unary2bin(X,Y):-unary2bin(X,0,Y).

unary2bin(0, A, A):-!.unary2bin(X,A,Y):-mod2(X,0),!,div2(X,X2),unary2bin(X2,o(A),Y).unary2bin(X,A,Y):- div2(X,X2),unary2bin(X2,i(A),Y).

Page 27: Logick é programovanie  4

Zelené vs. červené !Zelené rezy – jeho odstránenie nemení sémantiku programu,

používame ho len pre zvýšenie efektívnosti – program je deterministickejšísign(X,-1) :- X<0, !. sign(X, 0):-X=0, !. sign(X, 1):-X>0, !.

Červené rezy – menia význam programunot C :- C, !, fail. not C.

fail je predikát, ktorý nikdy neplatítrue je predikát, ktorý vždy platí, a má jediné riešenierepeat je predikát, ktorý vždy platí a má nekonenčne veľa

riešenírepeat.repeat:-repeat.

Page 28: Logick é programovanie  4

Cyklus repeat-failZadaj dvojciferné číslo z intervalu 10..99 a kým ho nezadáš,

program ťa nepustí ďalej

cisloXX(XX) :- repeat, write('Zadaj dvociferne cislo '), read(XX),(XX<100,XX>9, !, write(ok) ; write(zle), nl, fail).

ak prejde test XX<100,XX>9, znefunkční sa druhá vetva ‘zle’ aj alternatívy pre repeat

cisloXX1(XX) :- repeat, write('Zadaj dvociferne cislo '), read(XX),(XX<100,XX>9 -> write(ok)

; write(zle), nl, fail).! ukrytý v C->T;E čo je syntax pre ifthenelse nemá vplyv na túto

klauzulu, t.j. ak zadáme dvojciferné čislo, systém chce ďalšie

, !

Page 29: Logick é programovanie  4

Kaliningradské jednoťažky

neohr(1,2).neohr(1,3).neohr(1,4).neohr(1,5).

neohr(2,3).neohr(2,4).

neohr(3,4).neohr(3,5).

Kaliningradský problém – prejsť všetky mosty a skončiť tam, kde sme začali

hrany(Hrany) :- setof((X,Y), neohr(X,Y), Hrany).

jednotazka(X,Res) :- hrany(Hrany), jednotazka(X,Hrany,[X],Res).

jednotazka(X,[],C,Res):-Res = C.jednotazka(X,Hrany,C,Res):-

( delete((X,Y),Hrany,Hrany1) ;

delete((Y,X),Hrany,Hrany1)),jednotazka(Y,Hrany1,[Y|C],Res).

?- jednotazka(2,R).R = [4, 2, 1, 5, 3, 1, 4, 3, 2] ;R = [4, 2, 1, 3, 5, 1, 4, 3, 2] ;R = [4, 1, 5, 3, 1, 2, 4, 3, 2] ;

Page 30: Logick é programovanie  4

Farbenie mapymapa( [susedia(portugal, Portugal, [Spain]), susedia(spain, Spain, [France,Portugal]), susedia(france, France,

[Spain,Italy,Switzerland,Belgium,Germany,Luxemburg]), susedia(belgium, Belgium, [France,Holland,Luxemburg,Germany]), susedia(holland, Holland, [Belgium,Germany]), susedia(germany, Germany,

[France,Austria,Switzerland,Holland,Belgium,Luxemburg]), susedia(luxemburg,Luxemburg,[France,Belgium,Germany]), susedia(italy, Italy, [France,Austria,Switzerland]), susedia(switzerland,Switzerland,[France,Austria,Germany,Italy]), susedia(austria, Austria, [Italy,Switzerland,Germany]) ]).

colors([green,red,blue,yellow]). % zoznam farieb

subset([],_). % všetci sa nachádzajú v...

subset([X|Xs],Ys) :- member(X,Ys), subset(Xs,Ys).

Page 31: Logick é programovanie  4

Farbenie mapycolorMap([],_).colorMap([susedia(_,Color,Neighbors)|Xs],Colors) :-

select(Color,Colors,Colors1), % vyber farbu, ofarbi krajinusubset(Neighbors,Colors1), % prever, či susedia majúcolorMap(Xs,Colors). % iné farby a opakuj, kým []

?- mapa(M), colors(Cols), colorMap(M,Cols), write(M),nl.

[susedia(portugal, green, [red]), susedia(spain, red, [green, green]), susedia(france, green, [red, red, blue, red, yellow, blue]), susedia(belgium, red, [green, green, blue, yellow]), susedia(holland, green, [red, yellow]), susedia(germany, yellow, [green, green, blue, green, red, blue]), susedia(luxemburg, blue, [green, red, yellow]), susedia(italy, red, [green, green, blue]), susedia(switzerland, blue, [green, green, yellow, red]), susedia(austria, green, [red, blue, yellow])]

Page 32: Logick é programovanie  4

Hra NIMjednokopový NIM: dvaja hráči postupne berú z kopy 1,2 alebo 3 zápalky,

vyhráva ten, kto berie poslednú zápalku (prehráva, kto nemá čo brať)

Rekurzívna definícia predikátov: vitaznaPozicia1NIM/1, prehravajucaPozicia1NIM/1, spravnyTah1NIM

vitaznaPozicia1NIM(N):-between(1,3,N).vitaznaPozicia1NIM(N):-

tah1NIM(N,N1), % existuje správny ťah z N do N1,prehravajucaPozicia1NIM(N1).% že konfigurácia N1 je

prehrávajúca

tah1NIM(N,N1):- % existuje konkrétny Tahbetween(1,3,Tah), % v súľade s pravidlami

jednokopovéhoTah=<N, N1 is N-Tah. % NIMu

http://www.archimedes-lab.org/game_nim/nim.html

Page 33: Logick é programovanie  4

Hra NIM - prehrávajúcaprehravajucaPozicia1NIM(0). % nemá čo braťprehravajucaPozicia1NIM(N):- % pre ľubovoľný ťah z N do N1,

bagof(vitaznaPozicia1NIM(N1), tah1NIM(N,N1), All), forall(All). % nová konfigurácia je víťazná

forall([]). % platia všetky podciele zoznamuforall([G|Gs]):-G, forall(Gs).

spravnyTah1NIM(N):-tah1NIM(N,N1), prehravajucaPozicia1NIM(N1),write('nechaj ='), write(N1),nl.

zlé pozície sú tvaru 4*k (k>0), t.j. 4, 8, 12, … a vítazná stratégia je:ak super zoberie Z zápaliek, ja beriem 4-Z, ak som vo vítaznej pozícii,ak som v prehrávajúcej pozícii, beriem čo najmenej (snáď sa

pomýli…).

Page 34: Logick é programovanie  4

Hra NIM - traceAk si dáme vypisovať, pre ktoré ciele sa forall dokazuje, že platia, vidíme,

že overovanie, že pozícia N je víťazná, sa vykonáva mnohokrát.

?- prehravajucaPozicia1NIM(8).[vitaznaPozicia1NIM(7), vitaznaPozicia1NIM(6), vitaznaPozicia1NIM(5)][vitaznaPozicia1NIM(5), vitaznaPozicia1NIM(4), vitaznaPozicia1NIM(3)][vitaznaPozicia1NIM(3), vitaznaPozicia1NIM(2), vitaznaPozicia1NIM(1)][vitaznaPozicia1NIM(2), vitaznaPozicia1NIM(1)][vitaznaPozicia1NIM(1)][][vitaznaPozicia1NIM(4), vitaznaPozicia1NIM(3)][vitaznaPozicia1NIM(2), vitaznaPozicia1NIM(1), vitaznaPozicia1NIM(0)][vitaznaPozicia1NIM(1), vitaznaPozicia1NIM(0)][vitaznaPozicia1NIM(0)][vitaznaPozicia1NIM(1), vitaznaPozicia1NIM(0)][vitaznaPozicia1NIM(0)]. . . . . . . . . .

Page 35: Logick é programovanie  4

3-kopový NIMpravidlá (http://en.wikipedia.org/wiki/Nim): hráč berie z ľubovoľnej kopy (ale len jednej) ľub.počet zápaliek vyhráva ten, kto berie poslednú (t.j. prehráva ten, kto už nemá

čo brať).

predikáty: vitaznaPozicia3NIM/1, prehravajucaPozicia3NIM/1,spravnyTah3NIM

%- pravidlá závislé na hre (konfigurácia a povolené ťahy):

vitaznaPozicia3NIM([A,0,0]):-A>0.vitaznaPozicia3NIM([0,B,0]):-B>0.vitaznaPozicia3NIM([0,0,C]):-C>0.

http://www.mathematische-basteleien.de/nimgame.html

Page 36: Logick é programovanie  4

Hra 3-NIM - prehrávajúca%- toto je nezávislé na hrevitaznaPozicia3NIM(N):-tah3NIM(N,N1), % existuje správny ťah z N

do N1,lemma(prehravajucaPozicia3NIM(N1)).% že N1 je

prehrávajúca

prehravajucaPozicia3NIM([0,0,0]).prehravajucaPozicia3NIM(N):- % pre ľub.ťah, nová konfigurácia je

vítaznábagof(lemma(vitaznaPozicia3NIM(N1)), tah3NIM(N,N1),

All),forall(All).

spravnyTah3NIM(N):-tah3NIM(N,N1), lemma(prehravajucaPozicia3NIM(N1)),write('nechaj ='), write(N1),nl.

[1,2,3] •[0,2,3] -> [0,2,2] •[1,1,3] -> [1,1,0] •[1,0,3] -> [1,0,1] •[1,2,2] -> [0,2,2] •[1,2,1] -> [1,0,1] •[1,2,0] -> [1,1,0]

Page 37: Logick é programovanie  4

Hra 3-NIM – tabelizácia%- toto je nezávislé na hrevitaznaPozicia3NIM(N):-tah3NIM(N,N1), % existuje správny ťah z N do N1,

lemma(prehravajucaPozicia3NIM(N1)).% že N1 je prehrávajúca

prehravajucaPozicia3NIM([0,0,0]).prehravajucaPozicia3NIM(N):- % pre ľub.ťah, nová konfigurácia je vítazná

bagof(lemma(vitaznaPozicia3NIM(N1)), tah3NIM(N,N1), All),forall(All).

% lemma(P) je predikát logicky ekvivalentný P. Ale výpočtovo optimálnejší: ak sa nám raz podarilo P dokázať, zapamätáme si to ako fakt P:-true., a keď opätovne dokazujeme P, tak sa pozrieme, či sme už P dokazovali.

:-dynamic prehravajucaPozicia3NIM/1, vitaznaPozicia3NIM/1.lemma(P):-clause(P,true),!

; P, assertz(P:-true).

Page 38: Logick é programovanie  4

3-NIM a XOR3 = 0112

4 = 1002

5 = 1012

----------- XOR 0102

správny ťah 3-2 = 1

3 = 00112

7 = 01112

9 = 10012

----------- XOR 11012

správny ťah 9-5 = 4

1 = 0012

4 = 1002

5 = 1012

----------- XOR 0002

3 = 00112

7 = 01112

9 = 01002

----------- XOR 00002

Prehrávajúca konfiguráciaje taká, že XOR jednotlivýchkôp je 0.

http://www.numericana.com/answer/games.htm

Page 39: Logick é programovanie  4

Práca s „databázou“ asserta(Clause), assertz(Clause) – pridá klauzulu do databázy,

asserta na začiatok, assertz na koniec procedúry, clause(Head, Body) – hľadá klauzulu s porovnateľnú s Head:-Body, retract(Clause) – odstráni klauzulu z databázy, retractall(Clause) – odstráni všetky klauzuly z databázy.

Predikáty spôsobujú side-effect: po backtracku sa ich účinky NEODSTRÁNIA, t.j. pridaná klauzula sa nevymaže a vymazaná sa nepridá.

?-otec(jonatan, izidora).No.?-asserta(otec(jonatan,izidora)).Yes.?- otec(jonatan,izidora).Yes.?-retractall(otec(_,_)).Yes.?- otec(jonatan,izidora).No.

Reflexívne programovanie

Hurá, máme globálnu premennú (databázu)

Page 40: Logick é programovanie  4

Tabelizácialemma(P):-write('zistujem '), write(P), nl, fail.lemma(P):-(clause(P,true),write('nasiel '), write(P), nl, !)

; (P,write('dokazal '), write(P), nl,

assertz(P:-true)).

% deklarácia, že predikát fib/2 bude modifikovaný.:-dynamic fib/2.

fib(N,1):-N<2,!.fib(N,F):-N1 is N-1, N2 is N-2,

lemma(fib(N1,F1)), lemma(fib(N2,F2)),

F is F1+F2.

?- fib(5,N).zistujem fib(4, _G342) zistujem fib(3, _G345) zistujem fib(2, _G348) zistujem fib(1, _G351) dokazal fib(1, 1) zistujem fib(0, _G357) dokazal fib(0, 1) dokazal fib(2, 2) zistujem fib(1, _G369) nasiel fib(1, 1) dokazal fib(3, 3) zistujem fib(2, _G381) nasiel fib(2, 2)dokazal fib(4, 5) zistujem fib(3, _G393) nasiel fib(3, 3)N = 8.

Page 41: Logick é programovanie  4

Ilustrácia findall,assert-retractNa ilustráciu realizujme predikát findall pomocou assert-retract% vygeneruje všetky riešenia do databázyfindall(Term,Goal,_):-Goal, assertz(bof(Term)), fail. % pozbiera všetky riešenia z databázy do zoznamufindall(_,_,List):-loop(List), !. loop([X|List]):-retract(bof(X)), !, loop(List). loop([]).

?- gen(4,List).assertz(bof(1)).assertz(bof(2)).assertz(bof(3)).assertz(bof(4)).

List = [1, 2, 3, 4]?- gengen1(4,List).

List = [[[[[1], 1, 2], 1, 2, 3], 1, 2, 3, 4]].Kde je problém ???

Page 42: Logick é programovanie  4

Príklad použitia findallÚmyselne vnoríme jeden findall do volania druhého% gen(N,List) ak List = [1,…,N]gen(N,List):-findall(X,between(1,N,X),List).

% gen1(N,List) ak existuje 1<=M<=N, že List = [1,…,M]gen1(N,List):-between(1,N,M), gen(M,List).

% gengen1(N,List) ak List = [[1],[1,2],[1,2,3], …, [1,…,N]]gengen1(N,ListList):-findall(List,gen1(N,List),ListList).

?- gen(5,L). L = [1, 2, 3, 4, 5] ; No ?- gen1(5,L). L = [1] ; L = [1, 2] ; L = [1, 2, 3] ; L = [1, 2, 3, 4] ; L = [1, 2, 3, 4, 5]

; No ?- gengen1(5,L). L = [[1], [1, 2], [1, 2, 3], [1, 2, 3, 4], [1, 2, 3, 4, 5]] ; No

Page 43: Logick é programovanie  4

findall2Prvá verzia findall nefunguje v prípade vnorených konštrukcií

findall - vylepšíme ho (predpokladáme, že end je nevyskytujúci sa extra

symbol):

% bof(end) je použité ako zarážka, odkiaľ boli riešenia generované

findall(Term,Goal,_):-asserta(bof(end)),Goal,asserta(bof(Term)), fail.

findall(_,_,List):-retract(bof(X)), loop(X, [], List), !. loop(end,L,L):- !. % stop, ak narazíme na zarážku

bof(end)loop(X,L1,L):-retract(bof(Y)), !, loop(Y,[X|L1], L).

?- genn(4,List).List = [1, 2, 3, 4].

?- genngenn1(4,List).List = [[1], [1, 2], [1, 2, 3], [1, 2, 3, 4]].

Page 44: Logick é programovanie  4

Čo znamená ?vyjadrite význam pomocou predikátu findall/setof/bagoffoo(X):-(X,(retract(db(fooo,_)),!,fail;

asserta(db(fooo,X)), fail) ) ; retract(db(fooo,X)).

:-dynamic db/2.

a. % predikát s jedným riešením b. % predikát s dvomi riešeniami b. c. % predikát s tromi riešeniami c. c. f:-fail. % predikát so žiadnym riešením

?- foo(a). ?- foo(b). ?- foo(c). ?- foo(f).

Page 45: Logick é programovanie  4

Rébus 1ak X nie je splniteľný, foo(X) neplatí, posledný retract sa nepodari, foo(X)

zlyhá, ak X má riešenie, nájdeme prvé, retract(db(fooo,_)) zlyhá, takže

assert(db(fooo,X)) uspeje, a následne fail, ak X nemá ďalšie riešenie posledný retract(db(fooo,X)) odmaže, čo

assert vsunul. Teda, databáza je v pôvodnom stave. ak X má druhé riešenie, retract(db(foo,_)) vymaže prvé riešenie, urobí

cut-fail takže výpočet pokračuje retract(db(fooo,X)), ktorý zlyhá. Podobne, ak má viac riešení. ?- foo(a). Yes ?- foo(b). No ?- foo(c). No ?- foo(f). No takže, foo(X) vlastne znamená (že X má práve jedno riešenie):foo(X):-findall(X,X,[_]).

foo(X):-(X,(retract(db(fooo,_)),!,fail; asserta(db(fooo,X)),

fail) ) ; retract(db(fooo,X)).

Page 46: Logick é programovanie  4

Rébus 2predefinujte predikát goo bez assert a retract goo(X):- (X,

( retract(db(gooo,_)),! ;

asserta(db(gooo,1)), fail ) ); (retract(db(gooo,_)), fail).

Ak X nemá riešenie, goo(X) neplatí. Ak má X jedno riešenie, tak retract(db(gooo,_)) zlyhá, vykoná sa

asserta(db(gooo,1)), fail zlyhá. Ak neexistuje ďalšie riešenie pre X, retract(db(gooo,_)) odmaže

db(gooo,1) a klauzula zlyhá. Ak existuje ďalšie riešenie pre X (napr. X má dve riešenia),

retract(db(gooo,_)),! odmaže prvé a cut odstráni alternatívy.

?- p(1). Yes ?- p(2). Yes?- p(3). No

:-dynamic p/1.p(1):-retract(p(_):-_),assert(p(2)).

Príklady programov, o ktorých nemožno tvrdiť, že sú logické, len preto,že sú v Prologu...