1 / 40

Examples in XSB Prolog

Examples in XSB Prolog. Επεξεργασία και Αναπαράσταση Γνώσης Άνοιξη 2010 Τμήμα Επιστήμης Υπολογιστών Πανεπιστημίου Κρήτης. Typing a program on the XSB command line.

rock
Download Presentation

Examples in XSB Prolog

An Image/Link below is provided (as is) to download presentation Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author. Content is provided to you AS IS for your information and personal use only. Download presentation by click this link. While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server. During download, if you can't get a presentation, the file might be deleted by the publisher.

E N D

Presentation Transcript


  1. Examples in XSB Prolog Επεξεργασία και Αναπαράσταση Γνώσης Άνοιξη 2010 Τμήμα Επιστήμης Υπολογιστών Πανεπιστημίου Κρήτης

  2. Typing a program on the XSB command line • Ο χρήστης του XSB έχει τη δυνατότητα να εισάγει τις προτάσεις (γεγονότα και κανόνες) του προγράμματός του χρησιμοποιώντας τη γραμμή εντολών του XSB, π.χ. | ?- [user]. Enter mode to type predicates & rules. [Compiling user] edge(1,2). edge(2,3). edge(2,4). reachable(X,Y) :- edge(X,Y). reachable(X,Y) :- edge(X,Z), reachable(Z, Y). end_of_file. Done with terminal mode [user compiled, cpu time used: 0.0500 seconds] [user loaded] yes

  3. Loading a program in XSB • Έχει επίσης τη δυνατότητα να «φορτώσει» ένα αρχείο που περιέχει το λογικό πρόγραμμα. Αυτό θα πρέπει να έχει κατάληξη .P | ?- [edges]. Load and run edges.P [edges loaded] yes • Για να «φορτώσει» ένα αρχείο που βρίσκεται σε διαφορετικό directory γράφετε: | ?- [‘source/edges’]. Load and run edges.P which is saved in directory ‘source’ [edges loaded] yes • To default directory για τον XSB είναι το: xsb-3.1-win32/config/x86-pc-windows/bin • Για να αυξήσετε το μέγεθος της κονσόλαςδεξί κλικ στην γραμμή εργαλείων  Προεπιλογές  Διάταξη  Μέγεθος παραθύρου

  4. Making queries and termination | ?- reachable(X,Y).X = 1Y = 2;  Type a semi-colon repeatedlyX = 2Y = 3;X = 2Y = 4;X = 1Y = 3;X = 1Y = 4;no | ?- halt.Command to Exit XSBEnd XSB (cputime 0.15 secs, elapsetime 96.23 secs)

  5. Prolog Program Semantics • Τα προγράμματα στην Prolog μπορούν να γίνουν κατανοητά με δύο τρόπους: • δηλωτικά (declaratively) • διαδικαστικά (procedurally) • Έτσι αν θεωρήσουμε την πρόταση P :- Q, R. αυτή μπορεί να ερμηνευτεί ως εξής: • Δηλωτικά: το Pείναι αληθές αν το Qείναι αληθές και το Rείναι αληθές • Διαδικαστικά: Για να αποδειχθεί ότι το Pείναι αληθές, πρέπει πρώτα να αποδείξω πρώτα ότι τo Qείναι αληθές και στη συνέχεια ότι και το Rείναι αληθές.

  6. Loading a program in XSB • Ένα αρχείο μπορεί να περιέχει: • export declarations: ορίζουν τα σύμβολα που μπορούν να χρησιμοποιηθούν από άλλα αρχεία • local declarations: ορίζουν τα σύμβολα με τοπική μόνο εμβέλεια • import declarations: επιτρέπουν τη χρήση συμβόλων που έχουν εξαχθεί από άλλα αρχεία • Οι δηλώσεις αυτές μπορούν να γίνουν σε οποιοδήποτε σημείο του προγράμματος και έχουν την ακόλουθη μορφή: :- export sym1, ...,syml. :- local sym1, ...,symm. :- import sym1, ...,symn from module. όπου το symiέχει τη μορφή functor/arity. π.χ. :- import reachable/2 from edges.

  7. Usefulexamples – or , if-then-else • Το λογικό or μπορείτε να το εκφράσετε και ως εξής: or(A):- (A = a ; A = b ), write('A is a or b'). • Προσοχή να μην ξεχνάτε τις παρενθέσεις. if (X=1) then write(b) else write(d): a(X):- write(a), ( X = 1 -> write(b) ; write(d) ), write(c).

  8. Usefulexamples - for • Προσοχή να μην ξεχνάτε την συνθήκη τερματισμού. • Call with i(5)  Result: 4,3,2,1,0 end condition: N>0 i(0). i(N) :- N>0, write(N), nl, N1 is N-1, i(N1). • Call with for(0,5)  Result: 0,1,2,3,4 end condition Start<End for(End , End ). for(Start, End ):- Start<End, write(Start), nl, Start1 is Start+1, for(Start1,End).

  9. Dynamic Predicates • Κατηγορήματα • Στατικά (static): τροποποιούνται μόνο με reloading του προγράμματος. • Δυναμικά (dynamic):μπορούν να τροποποιηθούν την ώρα που τρέχει ένα πρόγραμμα. • Για να μπορούμε να χειριστούμε μία δομή δυναμικά, στην αρχή του προγράμματος κάνουμε τη δήλωση: :- dynamic turn/1 ή :- dynamic pred/2. • Στη συνέχεια μπορούμε να προσθέσουμε / αφαιρέσουμε δεδομένα από τη βάση γνώσης με χρήση των: assert(…): εισάγει ένα νέο γεγονός στημνήμη. retract(…): διαγράφει ένα γεγονός από τη μνήμη.

  10. XSB library modules – List processing (1/3) • append(?List1, ?List2, ?List3) module: basics Succeeds if list List3 is the concatenation of lists List1 and List2. • member(?Element, ?List) module: basics Checks whether Element unifies with any element of list List, succeeding more than once if there are multiple such elements. • memberchk(?Element, ?List) module: basics Similar to member/2, except that memberchk/2 is deterministic, i.e. does not succeed more than once for any call. • delete_ith(+Index, +List, ?Element, ?RestList) module: listutil Succeeds if the ith element of the list List unifies with Element, and RestList is List with Element removed.

  11. XSB library modules – List processing (2/3) • ith(?Index, ?List, ?Element) module: basics Succeeds if the ith element of the list List unifies with Element. Fails if Index is not a positive integer or greater than the length of List. Either Index and List, or List and Element, should be instantiated at the time of the call. • length(?List, ?Length) module: basics Succeeds if the length of the list List is Length. • same_length(?List1, ?List2) module: basics Succeeds if list List1 and List2 are both lists of the same number of elements. • select(?Element, ?L1, ?L2) module: basics List2 derives from List1 by selecting (removing) an Element non-deterministically.

  12. XSB library modules – List processing (3/3) • reverse(+List, ?ReversedList) module: basics Succeeds if ReversedList is the reverse of list List. If List is not a proper list, reverse/2 can succeed arbitrarily many times. It works only one way. • perm(+List, ?Perm) module: basics Succeeds when List and Perm are permutations of each other. The main use of perm/2 is to generate permutations of a given list. Perm may be partly instantiated. • subseq(?Sequence, ?SubSequence, ?Complement) module: basics Succeeds when SubSequence and Complement are both subsequences of the list Sequence (the order of corresponding elements being preserved) and every element of Sequence which is not in SubSequence is in the Complement and vice versa. • merge(+List1, +List2, ?List3) module: listutil Succeeds if List3 is the list resulting from ”merging” lists List1 and List2

  13. Append example append([],L,L). append([X|L], M, [X|N]) :- append(L,M,N). append([1,2],[3,4],X)?

  14. Append example append([],L,L). append([X|L],M,[X|N]) :- append(L,M,N). append([1,2],[3,4],X)? X=1,L=[2],M=[3,4],A=[X|N]

  15. Append example append([],L,L). append([X|L],M,[X|N]) :- append(L,M,N). append([2],[3,4],N)? append([1,2],[3,4],X)? X=1,L=[2],M=[3,4],A=[X|N]

  16. Append example append([],L,L). append([X|L],M,[X|N’]) :- append(L,M,N’). append([2],[3,4],N)? X=2,L=[],M=[3,4],N=[2|N’] append([1,2],[3,4],X)? X=1,L=[2],M=[3,4],A=[1|N]

  17. Append example append([],L,L). append([X|L],M,[X|N’]) :- append(L,M,N’). append([],[3,4],N’)? append([2],[3,4],N)? X=2,L=[],M=[3,4],N=[2|N’] append([1,2],[3,4],X)? X=1,L=[2],M=[3,4],A=[1|N]

  18. Append example append([],L,L). append([X|L],M,[X|N’]) :- append(L,M,N’). append([],[3,4],N’)? L = [3,4], N’ = L append([2],[3,4],N)? X=2,L=[],M=[3,4],N=[2|N’] append([1,2],[3,4],X)? X=1,L=[2],M=[3,4],A=[1|N]

  19. Append example append([],L,L). append([X|L],M,[X|N’]) :- append(L,M,N’). A = [1|N] N = [2|N’] N’= L L = [3,4] Answer: A = [1,2,3,4] append([],[3,4],N’)? L = [3,4],N’ = L append([2],[3,4],N)? X=2,L=[],M=[3,4],N=[2|N’] append([1,2],[3,4],X)? X=1,L=[2],M=[3,4],A=[1|N]

  20. Building new predicates - Range • Create a list containing all integers within a given range. :- export range/3. range(I,I,[I]). range(I,K,[I|L]) :- I < K, I1 is I + 1, range(I1,K,L). % range(I,K,L) :- I <= K, and L is the list containing all % consecutive integers from I to K. • Π.χ. | ?- range(4, 7, L). L = [4,5,6,7]; no

  21. Building new predicates - Slice • Extract a slice from a list. :- export slice/4. slice([X|_],1,1,[X]). slice([X|Xs],1,K,[X|Ys]) :- K > 1, K1 is K - 1, slice(Xs,1,K1,Ys). slice([_|Xs],I,K,Ys) :- I > 1, I1 is I - 1, K1 is K - 1, slice(Xs,I1,K1,Ys). % slice(L1,I,K,L2) :- L2 is the list of the elements of L1 % between index I and index K (both included). • Π.χ. | ?- slice([19,21,35,43,56,68,77,89.90], 3, 5, L2). L2 = [35,43,56]; no

  22. Building new predicates - Split • Split a list into two parts. :- export split/4. split(L,0,[],L). split([X|Xs],N,[X|Ys],Zs) :- N > 0, N1 is N - 1, split(Xs,N1,Ys,Zs). % split(L,N,L1,L2) :- the list L1 contains the first N elements % of the list L, the list L2 contains the remaining elements. • Π.χ. | ?- split([1,2,3,4,5,6,7,8,9,10], 3, L1, L2). L1 = [1,2,3] L2 = [4,5,6,7,8,9,10]; no

  23. Building new predicates - Rotate • Rotate a list N places to the left. :- import append/3 from basics. :- import length/2 from basics. :- import split/4 from split. :- export rotate/3. rotate(L1,N,L2) :- N >= 0, length(L1,NL1), N1 is N mod NL1, rotate_left(L1,N1,L2). rotate(L1,N,L2) :- N < 0, length(L1,NL1), N1 is NL1 + (N mod NL1), rotate_left(L1,N1,L2). rotate_left(L,0,L). rotate_left(L1,N,L2) :- N > 0, split(L1,N,S1,S2),append(S2,S1,L2). % rotate(L1,N,L2) :- the list L2 is obtained from the list L1 % by rotating the elements of L1 N places to the left. • Π.χ. | ?- rotate([1,2,3,4,5,6,7,8], 2, L2). L2 = [3,4,5,6,7,8,1,2]; no

  24. Building new predicates – Random Selection • Extract a given number of randomly selected elements from a list. :- import random/3 from random. :- import length/2 from basics. :- import delete_ith/4 from listutil. :- export rnd_select/3. rnd_select(_,0,[]). rnd_select([X],1,[X]). rnd_select(Xs,N,[X|Zs]) :- N > 0, length(Xs,L), random(1,L,I), delete_ith(I,Xs,X,Ys), N1 is N-1,rnd_select(Ys,N1,Zs). % rnd_select(L,N,R) :- the list R contains N randomly selected % items taken from the list L. • Π.χ. | ?- rnd_select([1,2,3,4,5,6,7,8,9], 5, R). R = [7,2,8,1,4]; no

  25. Building new predicates – Random Permutation • Generate a random permutation of the elements of a list. :- import length/2 from basics. :- import rnd_select/3 from random_selection. :- export rnd_permu/2. rnd_permu(L1,L2) :- length(L1,N), rnd_select(L1,N,L2). % rnd_permu(L1,L2) :- the list L2 is a random permutation of % the elements of the list L1. • Π.χ. | ?- rnd_permu([1,2,3,4,5,6,7,8,9], L). L = [7,3,5,1,8,2,4,6,9]. no

  26. Eight Queens Problem • Ο στόχος είναι να τοποθετηθούν οχτώ βασίλισσες σε μία σκακιέρα χωρίς να μπορεί να απειλείται κάποια από οποιαδήποτε άλλη. Θα πρέπει επομένως η κάθε βασίλισσα να βρίσκεται σε διαφορετική γραμμή, στήλη και διαγώνιο της σκακιέρας. Η προτεινόμενη λύση αποτελεί μια γενική λύση για σκακιέρες (και αντίστοιχο πλήθος βασιλισσών) οποιουδήποτε μεγέθους. • Αναπαριστούμε τις θέσεις των βασιλισσών με μία λίστα των αριθμών 1-Ν. Για παράδειγμα η λίστα [4,2,7,3,6,8,5,1] σημαίνει ότι η βασίλισσα στην πρώτη στήλη βρίσκεται στη γραμμή 4, η βασίλισσα στη δεύτερη στήλη στη γραμμή 2. κ.ο.κ. Με αυτόν τον τρόπο εξασφαλίζουμε ότι η κάθε βασίλισσα βρίσκεται σε διαφορετική στήλη και γραμμή. Το μόνο που απομένει είναι να γίνει ο έλεγχος της διαγώνιου. Μία βασίλισσα τοποθετημένη στη στήλη Χ και γραμμή Υ καταλαμβάνει δύο διαγώνιους, μία με αριθμό C = X+Y και μία με αριθμό D=X-Y.

  27. Eight Queens – The Solution :- import memberchk/2 from basics. :- import range/3 from range. :- import select/3 from basics. % queens(N,Qs) :- Qs is a solution of the N-queens problem queens(N,Qs) :- range(1,N,Rs), permu(Rs,Qs), test(Qs,1,[],[]). permu([],[]). permu(Qs,[Y|Ys]) :- select(Y,Qs,Rs), permu(Rs,Ys). % test(Qs,X,Cs,Ds) :- the queens in Qs, representing columns X to % N, are not in conflict with the diagonals Cs and Ds; Cs and Ds keep % track of the already occupied diagonals. test([],_,_,_). test([Y|Ys],X,Cs,Ds) :- C is X-Y, \+ memberchk(C,Cs), D is X+Y, \+ memberchk(D,Ds), X1 is X + 1, test(Ys,X1,[C|Cs],[D|Ds]).

  28. Knight’s Tour Problem • Ο στόχος είναι να βρούμε μία πορεία που μπορεί να διαγράψει ένα άλογο σε όλες τις θέσεις μία σκακιέρας αυθαίρετου μεγέθους, χωρίς να επισκεφτεί την ίδια θέση παραπάνω από μία φορές.

  29. Knight’s Tour – The Solution (1/2) :- import memberchk/2 from basics. % knights(N,Knights) :- Knights is a knight's tour on a NxN chessboard knights(N,Knights) :- M is N*N-1, knights(N,M,[1/1],Knights). % closed_knights(N,Knights) :- Knights is a knight's tour on a NxN % chessboard which ends at the same square where it begun. closed_knights(N,Knights) :- knights(N,Knights), Knights = [X/Y|_], jump(N,X/Y,1/1). % knights(N,M,Visited,Knights) :- the list of squares Visited must be % extended by M further squares to give the solution Knights of the % NxN chessboard knight's tour problem. knights(_,0,Knights,Knights). knights(N,M,Visited,Knights) :- Visited = [X/Y|_], jump(N,X/Y,U/V), \+ memberchk(U/V,Visited), M1 is M-1, knights(N,M1,[U/V|Visited],Knights).

  30. Knight’s Tour – The Solution (2/2) % jumps on an NxN chessboard from square A/B to C/D jump(N,A/B,C/D) :- jump_dist(X,Y), C is A+X, C > 0, C =< N, D is B+Y, D > 0, D =< N. % jump distances jump_dist(1,2). jump_dist(2,1). jump_dist(2,-1). jump_dist(1,-2). jump_dist(-1,-2). jump_dist(-2,-1). jump_dist(-2,1). jump_dist(-1,2).

  31. Το πρόβλημα του βοσκού • Το πρόβλημα του βοσκού αποτελεί ένα κλασσικό πρόβλημα λογικής (puzzle). • Σε μια όχθη ενός ποταμού υπάρχουν ένας βοσκός, ένας λύκος ένα πρόβατο και ένα δεμάτι σανός. Η διαθέσιμη βάρκα χωρά μόνο δύο αντικείμενα κάθε φορά. Αν υποθέσουμε ότι σε κάθε στιγμή ο λύκος και το πρόβατο, καθώς και το πρόβατο και το δεμάτι δεν μπορούν να είναι μόνα τους σε μία όχθη (χωρίς τον βοσκό), ποία είναι η ακολουθία κινήσεων η οποία πρέπει να γίνει για να περάσουν όλοι απένταντι; • Το παραπάνω πρόβλημα είναι ουσιαστικά ένα πρόβλημα σχεδιασμού κινήσεων (Planning). Στα προβλήματα της κατηγορίας αυτής, μία λύση είναι η αναζήτηση στο χώρο των καταστάσεων του κόσμου του προβλήματος. Κάθε τέτοια κατάσταση περιγράφει τον "κόσμο" του προβλήματος την συγκεκριμένη χρονική στιγμή. Οι μεταβάσεις από μια συγκεκριμένη κατάσταση σε μια επόμενη γίνονται μέσω τελεστών.

  32. Επιλογή Αναπαράστασης • Είναι πολλές οι διαθέσιμες επιλογές με τις οποίες μπορούμε να αναπαραστήσουμε το συγκεκριμένο πρόβλημα. Η παρακάτω υλοποίηση χρησιμοποιεί για την περιγραφή της κατάστασης ένα σύνθετo όρο της μορφής: state(shepherd(A),sheep(B),wolf(C),hey(D)) όπου τα A, B, C, D είναι οι όχθες στις οποίες βρίκονται αντίστοιχα ο βοσκός, το πρόβατο, ο λύκος και το δεμάτι σανός. • Με βάση την παραπάνω αναπαράσταση ορίζονται οι τελεστές, που αντιστοιχούν ουσιαστικά στις κινήσεις που μπορούν να γίνουν για να λυθεί το πρόβλημα.

  33. Ορισμός Κατηγορημάτων • Το κατηγόρημα move/3 περιγράφει τις επιτρεπτές κινήσεις που μπορούν να γίνουν. Η μεταβλητή State είναι η "τρέχουσα" κατάσταση, η μεταβλητή Move είναι η κίνηση που λαμβάνει χώρα για να προκύψει η νέα κατάσταση που ενοποιείται με την μεταβλητή NewState. % move/3 % move(State,Move,NewState) % Move the shepherd move(state(shepherd(X1),sheep(S),wolf(W),hey(H)), go_to_other_shore(X1,X2), state(shepherd(X2),sheep(S),wolf(W),hey(H))):- opposite(X1,X2). % Take the sheep to the other side move(state(shepherd(X1),sheep(X1),wolf(W),hey(H)), move_sheep(X1,X2), state(shepherd(X2),sheep(X2),wolf(W),hey(H))):- opposite(X1,X2).

  34. Ορισμός Κατηγορημάτων % Take the wolf to the other side move(state(shepherd(X1),sheep(S),wolf(X1),hey(H)), move_wolf(X1,X2), state(shepherd(X2),sheep(S),wolf(X2),hey(H))):- opposite(X1,X2). % Take the hey to the other side move(state(shepherd(X1),sheep(S),wolf(W),hey(X1)), move_hey(X1,X2), state(shepherd(X2),sheep(S),wolf(W),hey(X2))):- opposite(X1,X2). • Το κατηγόρημα opposite/2 δηλώνει τις δύο απέναντι όχθες. % opposite/2 % opposite(X1,X2). opposite(a,b). opposite(b,a).

  35. Ορισμός Κατηγορημάτων • Το κατηγόρημα is_valid/1 πετυχαίνει όταν η κατάσταση State είναι επιτρεπτή, δηλαδή δεν παραβιάζει τους περιορισμούς του προβλήματος.. % is_valid/1 % is_valid(State). is_valid(state(shepherd(X),sheep(X),wolf(_),hey(_))). is_valid(state(shepherd(X),sheep(_),wolf(X),hey(X))).

  36. Ορισμός Κατηγορημάτων • Το κατηγόρημα end_state/1 πετυχαίνει όταν η State είναι η τελική κατάσταση, δηλαδή όλα τα αντικείμενα είναι στην όχθη b (θεωρούμε ότι αρχικά όλα τα αντικείμενα είναι στην όχθη a). % end_state/1 % end_state(State). end_state(state(shepherd(b),sheep(b),wolf(b),hey(b))).

  37. Ορισμός Κατηγορημάτων • Το κατηγόρημα solve/3 βρίσκει την σειρά των κινήσεων που πρέπει να γίνουν για να επιλυθεί το πρόβλημα. Ουσιαστικά ο αλγόριθμος αναζήτησης που χρησιμοποιείται είναι η κατα-βάθος αναζήτηση της Prolog. • Η μεταβλητή State είναι η "τρέχουσα" κατάσταση, η μεταβλητή Moves η λίστα των κινήσεων και η Previous_States η λίστα των καταστάσεων τις οποίες έχει "επισκευτεί" μέχρι τώρα η διαδικασία αναζήτησης. • Κάθε νέα κατάσταση που προκύπτει (NextState), δεν πρέπει να ανήκει στην λίστα αυτή για την αποφυγή βρόχων. % solve/3 % solve(State,Moves,Previous_States) solve(State,[],_):- end_state(State). solve(State,[Move|Moves],Previous_States):- move(State,Move,NextState), \+ member(NextState,Previous_States), is_valid(NextState), solve(NextState,Moves,[NextState|Previous_States]).

  38. Ορισμός Κατηγορημάτων • Το επόμενο κατηγόρημα τυπώνει στην οθόνη τα μέλη μιας λίστας, και χρησιμοποιείται για να τυπώνει τις κινήσεις στην οθόνη. % pretty_write/1 % pretty_write(List) pretty_write([]). pretty_write([First|Rest]):- write(' -> '), write(First), nl, pretty_write(Rest). • Το κατηγόρημα run/1, χρησιμοποιείται για να εκτελέσουμε το πρόγραμμα. run(Moves):-solve(state(shepherd(a),sheep(a),wolf(a),hey(a)),Moves, [state(shepherd(a),sheep(a),wolf(a),hey(a))]), pretty_write(Moves),nl.

  39. Ασκήσεις • (ασκ.1): Δημιουργήστε διαδικασία factorialπου θα υπολογίζει το παραγοντικό ενός αριθμού. Π.χ. ?- factorial(4,X). X=24 • (ασκ.2): Δημιουργήστε διαδικασία medianπου θα υπολογίζει τον μεσαίο μίας λίστας που περιέχει αριθμούς περιττού πλήθους. Π.χ. ?- median([2,8,6,4,7,9,3],X). X=6 • (ασκ.3): Δημιουργήστε διαδικασία set_equalityπου θα ελέγχει αν δύο λίστες περιέχουν τα ίδια στοιχεία (δεν μας ενδιαφέρει η σειρά που έχουν στη λίστα). Π.χ. ?- set_equality([1,2,3],[3,1,2]). yes ?- set_equality([1,2,3],[3,1]). no

  40. Ασκήσεις • (ασκ.4): Λύστε το πρόβλημα των ιεραπόστολων με τους κανίβαλους. Περιγραφή προβλήματος: Στις όχθες ενός ποταµού υπάρχουν συνολικά τρεις ιεραπόστολοι και τρεις κανίβαλοι έτσι ώστε σε κάθε όχθη να υπάρχει ίσος αριθµός ιεραποστόλων και κανιβάλων. Επίσης, υπάρχει και µια βάρκα σε µια από τις όχθες. Θέλουµε όλοι να µεταφερθούν σε µια όχθη. Όµως, η βάρκα για να κινηθεί χρειάζεται τουλάχιστον ένα άτοµο, χωρά µόνο µέχρι δύο άτοµα και σε καµία περίπτωση δεν πρέπει ο αριθµός των κανιβάλων να είναι µεγαλύτερος από αυτόν των ιεραποστόλων σε κάποια όχθη. nice link: http://gym-tsepel.ioa.sch.gr/play/problem2.htm

More Related