An introduction to Artificial Intelligence. Finlay and Dix. 1st Edition.

prune.p

download

Header

%  =================================================================
%  ==                                                             ==
%  ==          An Introduction to ARTIFICIAL INTELLIGENCE         ==
%  ==                  Janet Finlay and Alan Dix                  ==
%  ==                       UCL Press, 1996                       ==
%  ==                                                             ==
%  =================================================================
%  ==                                                             ==
%  ==         chapter 3, pages 50-51:  search tree pruning        ==
%  ==                                                             ==
%  ==            Prolog example, Alan Dix, August 1996            ==
%  ==                                                             ==
%  =================================================================

Code

%  This program solves magic squares too, but is more is more efficient
%  than simple generate and test.
%  Instead of waiting for a complete solution to be generated, whenever
%  enough of the solution has been chosen to test a constraint (that
%  is a row, column or diagonal sum), the partial solution is tested.
%  If the numbers in the square are allocated from the top right by rows,
%  tests can be done at the end of the first row (after the first three
%  numbers) at the end of the second row (after the next three) and as
%  each of the last row is completed.
%  Five predicates are defined to generate the solution: gen_magic_123,
%  gen_magic_456, gen_magic_7, gen_magic_8 and gen_magic_9.
%  Five matching test predicates are also defined: test_magic_123,
%  test_magic_456, test_magic_7, test_magic_8 and test_magic_9.
%  These are used alternately by the predicate prune_magic with.

prune_magic(Magic,Sum) :-
        gen_magic_123(Magic,[1,2,3,4,5,6,7,8,9],Rest1to3),
        write('trying partial solution '), write(Magic), nl,
        test_magic_123(Magic,Sum),
        write('OK so far ...'), nl,
        gen_magic_456(Magic,Rest1to3,Rest4to6),
        write('trying partial solution '), write(Magic), nl,
        test_magic_456(Magic,Sum),
        write('OK so far ...'), nl,
        gen_magic_7(Magic,Rest4to6,Rest7),
        write('trying partial solution '), write(Magic), nl,
        test_magic_7(Magic,Sum),
        write('OK so far ...'), nl,
        gen_magic_8(Magic,Rest7,Rest8),
        write('trying partial solution '), write(Magic), nl,
        test_magic_8(Magic,Sum),
        write('OK so far ...'), nl,
        gen_magic_9(Magic,Rest8,[]),
        write('trying partial solution '), write(Magic), nl,
        test_magic_9(Magic,Sum),
        write('YES!'), nl.

%  The generate predicates simply choose the relevant cells in
%  the solution (using the same representation as in the generte
%  and test code).
%  Notice how these simply break up the generate_magic predicate
%  from the generate and test code.

gen_magic_123([N11,N12,N13,_,_,_,_,_,_],List,Rest13) :-
        choose_list(N11,List,Rest11),
        choose_list(N12,Rest11,Rest12),
        choose_list(N13,Rest12,Rest13).
gen_magic_456([_,_,_,N21,N22,N23,_,_,_],List,Rest23) :-
        choose_list(N21,List,Rest21),
        choose_list(N22,Rest21,Rest22),
        choose_list(N23,Rest22,Rest23).

gen_magic_7([_,_,_,_,_,_,N31,_,_],List,Rest31) :-
        choose_list(N31,List,Rest31).

gen_magic_8([_,_,_,_,_,_,_,N32,_],List,Rest32) :-
        choose_list(N32,List,Rest32).

gen_magic_9([_,_,_,_,_,_,_,_,N33],List,Rest33) :-
        choose_list(N33,List,Rest33).


%  The same choose_list predicate is used as in the generate
%  and test solution.

choose_list(N,[N|Rest],Rest).
choose_list(N,[M|Rest],[M|R2]) :- choose_list(N,Rest,R2).

%  Like the generate predicates, the test predicates are a
%  split ou form of test_magic in the generate and test code.
%  In fact, this is often a good way to produce efficient search
%  code, simply write the generate and test code and then merge
%  the generate and test parts, testing as soon as possible. 

test_magic_123([N11,N12,N13,_,_,_,_,_,_],Sum) :-
        Sum is N11+N12+N13.  % row 1

test_magic_456([_,_,_,N21,N22,N23,_,_,_],Sum) :-
        Sum is N21+N22+N23.  % row 2

test_magic_7([N11,_,N13,N21,N22,_,N31,_,_],Sum) :-
        Sum is N11+N21+N31,  % column 1
        Sum is N13+N22+N31.  % diagonal running north east

test_magic_8([_,N12,_,_,N22,_,_,N32,_],Sum) :-
        Sum is N12+N22+N32.  % column 2

test_magic_9([N11,_,N13,_,N22,N23,N31,N32,N33],Sum) :-
        Sum is N31+N32+N33,  % row 3
        Sum is N13+N23+N33,  % column 3
        Sum is N11+N22+N33.  % diagonal running north west

Running this Code

%  RUNNING THIS CODE
%
%  You can type:
%>     prune_magic(Magic,Sum).
%
%  It will give you a solution somewhat faster than the
%  generate and test code (time them both if you can be bothered
%  to wait for generate and test!
%  
%  Howevere, for even faster reponse, you know that the sum of
%  the magic square must be 15 = (1+2+3+4+5+6+7+8+9)/3 so you
%  can instead type:
%>     prune_magic(Magic,15).
%
%  This will run much faster again.
%  Without the hint that the sum has to be 15, the test at stage '1to3'
%  will do no pruning and the first pruning cannot occur until '4to6',
%  when the first and second rows can be compared.
%
%  Lesson:  a little bit of foresight can make a search perform
%           phenominaly faster
%

%  EXERCISE
%
%  In the book, we noted that different orders for choosing the numbers
%  in the partial solution can make it faster or slower.
%  The example at the bottom of page 50 is particularly bad!
%  Modify the above code to make it search in an even more efficient
%  fashion!  (hint see page 73).
%

Examples

%  EXAMPLES
%
%> prune_magic(Magic,Sum).
%
%> prune_magic(Magic,15).


Query

Response