The zebra puzzle is a typical constraint satisfaction problem (CSPs). Sudoku, Magic Square also belongs to CSPs problem. The language features of Erlang's list parsing allow it to solve such problems in a way that is very close to the natural language: it does not need to consider the process-solving steps to describe the constraints, and the Erlang interpreter matches the answer through a domain search. Someone has also used Erlang to write an extension to a finite domain constraint (finite domain Constraints).
Using Erlang's list parsing to generate all third-order magic squares is so simple:
% Magicsquare.erl
-module (Magicsquare).
-export ([magicsquare/0]).
Magicsquare ()->
[{A00, A01, A02, A10, A11, A12, A20, A21, A22} | |
A00 <-lists:seq (1, 9),
A01 <-lists:seq (1, 9)--[A00],
A02 <-lists:seq (1, 9)--[A00, A01],
A10 <-lists:seq (1, 9)--[A00, A01, A02],
A11 <-lists:seq (1, 9)--[A00, A01, A02, A10],
A12 <-lists:seq (1, 9)--[A00, A01, A02, A10, A11],
A20 <-lists:seq (1, 9)--[A00, A01, A02, A10, A11, A12],
A21 <-lists:seq (1, 9)--[A00, A01, A02, A10, A11, A12, A20],
A22 <-lists:seq (1, 9)--[A00, A01, A02, A10, A11, A12, A20, A21],
% of each line is 15
A00 + A01 + A02 =:= 15,
A10 + A11 + A12 =:= 15,
A20 + A21 + A22 =:= 15,
% of each column is 15
A00 + A10 + A20 =:= 15,
A01 + A11 + A21 =:= 15,
A02 + A12 + A22 =:= 15,
% of percent diagonal and 15
A00 + A11 + A22 =:= 15,
A02 + A11 + A20 =:= 15
].
Since the famous Zebra puzzle and magic square are the same type of problem, it is arguably not a difficult task to write a solution program in Erlang. Post the zebra issue first:
Five Men of different nationality (England, Spain, Japan, Italy, Norway) live in the The the the the "a street." They all have a profession (painter, diplomat, violinist, doctor, sculptor), one animal (dog, Zebra, fox, snail, hors e), and one favorite drink (juice, water, tea, coffee, milk), all different from the others. Each of the houses are painted in a color different to others (green, red, yellow, blue, white).
Furthermore:
The Englishman lives in the Red House.
The Spaniard owns the dog.
The Japanese is the painter.
The Italian likes tea.
The Norwegian lives in the leftmost house.
The owner of the Green House likes coffee.
The green house was to the right, the white one.
The sculptor breeds snails.
The diplomat lives in the Yellow House.
Milk is drunk into the third house.
The Norwegian ' s house was next to the blue one.
The violinist likes juice.
The "Fox is" in "house" next to the doctor's house.
The horse is in the house next to the diplomat ' s.
The problem is thus to infer who owns zebra drinks.
For the convenience of program description problems, first for nationalities, occupations, colours, pets, beverages these five items are mapped to 1~5 these several numbers go:
House:l to R 1 2 3 4 5
Nation England Spain Japan Italy Norway
profession painter diplomat violinist doctor sculptor
Color Green red Yellow blue white
Animal dog zebra fox snail Horse
Drink Juice Water Tea coffee milk
Then you can use Erlang to describe the problem:
% Zebra.erl version 1
-module (Zebra).
-compile (Export_all).
Perms ([])-> [[]];
Perms (L)-> [[h| T] | | H <-L, T <-perms (L--[h])].
Full_perms (N)->
Perms (Lists:seq (1, N)).
Zebra ()->
[{N, C, P, A, D} | |
N <-full_perms (5),
C <-full_perms (5),
P <-full_perms (5),
A <-full_perms (5),
D <-full_perms (5),
Lists:nth (1, N) =:= lists:nth (2, C),% condition 1
Lists:nth (2, N) =:= lists:nth (1, A),% condition 2
Lists:nth (3, N) =:= lists:nth (1, P),% condition 3
Lists:nth (4, N) =:= lists:nth (3, D),% condition 4
Lists:nth (5, N) =:= 1,% condition 5
Lists:nth (1, C) =:= lists:nth (4, D),% condition 6
Lists:nth (5, C) + 1 =:= lists:nth (1, c),% condition 7
Lists:nth (5, P) =:= lists:nth (4, A),% condition 8
Lists:nth (2, P) =:= lists:nth (3, C),% condition 9
Lists:nth (5, D) =:= 3,% condition 10
(((Lists:nth (5, N) + 1) =:= Lists:nth (4, C)) OrElse ((Lists:nth (4, C) + 1) =:= Lists:nth (5, N)),% condition 11
Lists:nth (3, P) =:= lists:nth (1, D),% condition 12
((Lists:nth (3, a) + 1) =:= lists:nth (4, p)) OrElse ((Lists:nth (4, p) + 1) =:= Lists:nth (3, a)),% condition 13
((Lists:nth (5, a) + 1) =:= lists:nth (2, p)) OrElse ((Lists:nth (2, p) + 1) =:= Lists:nth (5, a))% condition 14
].
Compile run:
$ erl
1> C (Zebra).
{OK, zebra}
2> Zebra:zebra ().
[{[3,4,5,2,1],
[5,3,1,2,4],
[5,1,4,2,3],
[4,5,1,3,2],
[4,1,2,5,3]}]
Yes, a few twenty or thirty lines of code (mostly in terms of constraints) can solve the correct answer. But it was running so slowly that it took about two hours on my machine! And the answer also needs to manually map the numbers to the corresponding attributes.
To solve the slow problem first: Remember the construction and interpretation of computer programs SICP exercise 4.39? The order of the constraints does not affect the answer, but it affects the size of the search scope when the program executes, resulting in a huge difference in the order in which different constraints are run. Putting a strong constraint on the front will significantly reduce the search scope and reduce the running time. Adjust the above zebra to solve the code, immediately solve the answer:
% Zebra.erl Version2
-module (Zebra).
-compile (Export_all).
Perms ([])-> [[]];
Perms (L)-> [[h| T] | | H <-L, T <-perms (L--[h])].
Full_perms (N)->
Perms (Lists:seq (1, N)).
Zebra ()->
[{N, C, P, A, D} | |
N <-full_perms (5),
Lists:nth (5, N) =:= 1,% condition 5
C <-full_perms (5),
(((Lists:nth (5, N) + 1) =:= Lists:nth (4, C)) OrElse ((Lists:nth (4, C) + 1) =:= Lists:nth (5, N)),% condition 11
Lists:nth (1, N) =:= lists:nth (2, C),% condition 1
Lists:nth (5, C) + 1 =:= lists:nth (1, c),% condition 7
P <-full_perms (5),
Lists:nth (3, N) =:= lists:nth (1, P),% condition 3
Lists:nth (2, P) =:= lists:nth (3, C),% condition 9
A <-full_perms (5),
Lists:nth (2, N) =:= lists:nth (1, A),% condition 2
Lists:nth (5, P) =:= lists:nth (4, A),% condition 8
((Lists:nth (3, a) + 1) =:= lists:nth (4, p)) OrElse ((Lists:nth (4, p) + 1) =:= Lists:nth (3, a)),% condition 13
((Lists:nth (5, a) + 1) =:= lists:nth (2, p)) OrElse ((Lists:nth (2, p) + 1) =:= Lists:nth (5, a)),% condition 14
D <-full_perms (5),
Lists:nth (5, D) =:= 3,% condition 10
Lists:nth (4, N) =:= lists:nth (3, D),% condition 4
Lists:nth (1, C) =:= lists:nth (4, D),% condition 6
Lists:nth (3, P) =:= lists:nth (1, D)% condition 12
].
Solve the performance problems, and then to improve the program, in the program mapping a variety of properties, so that you can better describe the constraints of the condition. The complete Erlang code is as follows:
% Zebra.erl final version
-module (Zebra).
-export ([zebra/0, zebra_print/0, who_own_zebra/0]).
% percent @spec (list::list (), Ele)-> integer ()
% @doc Returns the position of ' Ele ' in the ' List '. 0 is returned
%% when ' Ele ' isn't found.
% percent @end
POS (List, Ele)->
POS (List, Ele, 1).
POS ([Ele | _tail], Ele, POS)->
Pos;
POS ([_ | Tail], Ele, Pos)->
POS (Tail, Ele, pos+1);
POS ([], _ele, _)->
0.
Nation ()-> [' England ', ' Spain ', ' Japan ', ' Italy ', ' Norway '].
Profession ()-> [painter, diplomat, violinist, doctor, sculptor].
Color ()-> [Green, red, yellow, blue, white].
Animal ()-> [Dog, Zebra, fox, snail, horse].
Drink ()-> [Juice, water, tea, coffee, milk].
Data_inx (N, Data)->
Lists:nth (N, Data ()).
Data_pos (Name, Data)->
POS (Data (), Name).
Perms ([])-> [[]];
Perms (L)-> [[h| T] | | H <-L, T <-perms (L--[h])].
Full_perms (N)->
Perms (Lists:seq (1, N)).
Zebra_print ()->
[Io:format ("Nation:tt~-12s~-12s~-12s~-12s~-12s~n"
"Color:tt~-12s~-12s~-12s~-12s~-12s~n"
"Profession:t~-12s~-12s~-12s~-12s~-12s~n"
"Animal:tt~-12s~-12s~-12s~-12s~-12s~n"
"Drink:tt~-12s~-12s~-12s~-12s~-12s~n~n",
[Data_inx (POS (N, 1), Fun nation/0),
Data_inx (POS (N, 2), fun nation/0),
Data_inx (POS (N, 3), fun nation/0),
Data_inx (POS (N, 4), Fun nation/0),
Data_inx (POS (N, 5), fun nation/0),
Data_inx (POS (C, 1), Fun color/0),
Data_inx (POS (C, 2), fun color/0),
Data_inx (POS (C, 3), fun color/0),
Data_inx (POS (C, 4), Fun color/0),
Data_inx (POS (C, 5), fun color/0),
Data_inx (POS (P, 1), Fun profession/0),
Data_inx (POS (P, 2), fun profession/0),
Data_inx (POS (P, 3), fun profession/0),
Data_inx (POS (P, 4), Fun profession/0),
Data_inx (POS (P, 5), fun profession/0),
Data_inx (POS (A, 1), Fun animal/0),
Data_inx (POS (A, 2), fun animal/0),
Data_inx (POS (A, 3), fun animal/0),
Data_inx (POS (A, 4), Fun animal/0),
Data_inx (POS (A, 5), fun animal/0),
Data_inx (POS (D, 1), Fun drink/0),
Data_inx (POS (D, 2), fun drink/0),
Data_inx (POS (D, 3), fun drink/0),
Data_inx (POS (D, 4), Fun drink/0),
Data_inx (POS (D, 5), Fun drink/0)] | |
{N, C, P, A, D} <-Zebra ()
].
Who_own_zebra ()->
[Data_inx (POS (N, POS (A, Data_pos (Zebra, fun animal/0)), fun nation/0) | |
{N, _c, _p, A, _d} <-Zebra ()
].
Zebra ()->
[{N, C, P, A, D} | |
N <-full_perms (5),
%% The Norwegian lives in the leftmost house
Lists:nth (Data_pos (' Norway ', fun nation/0), N) =:= 1,
C <-full_perms (5),
%% of the Norwegian ' s house are next to the blue one
(((Lists:nth (Data_pos (' Norway ', fun nation/0), N) + 1) =:=
Lists:nth (Data_pos (blue, fun color/0), C) OrElse
((Lists:nth (4, C) + 1) =:= Lists:nth (5, N)),
%% The Englishman lives in the Red House
Lists:nth (Data_pos (' England ', fun nation/0), N) =:=
Lists:nth (Data_pos (red, fun color/0), C),
%% of the "green house" to "right" of the white one
Lists:nth (Data_pos (white, fun color/0), C) + 1 =:=
Lists:nth (Data_pos (green, fun color/0), C),
P <-full_perms (5),
%% of the Japanese is the painter
Lists:nth (Data_pos (' Japan ', fun nation/0), N) =:=
Lists:nth (Data_pos (painter, fun profession/0), P),
A <-full_perms (5),
%% The Spaniard owns the dog
Lists:nth (Data_pos (' Spain ', fun nation/0), N) =:=
Lists:nth (Data_pos (dog, Fun animal/0), A),
%% of the sculptor breeds snails
Lists:nth (Data_pos (sculptor, fun profession/0), P) =:=
Lists:nth (Data_pos (snail, fun animal/0), A),
D <-full_perms (5),
%% The diplomat lives in the yellow House
Lists:nth (Data_pos (diplomat, fun profession/0), P) =:=
Lists:nth (Data_pos (yellow, fun color/0), C),
% of the "Fox is" in "house" next to the Doctor ' s house
(((Lists:nth (Data_pos (Fox, Fun animal/0), A) + 1) =:=
Lists:nth (Data_pos (doctor, Fun profession/0), P) OrElse
((Lists:nth (Data_pos (doctor, Fun profession/0), P) + 1) =:=
Lists:nth (Data_pos (Fox, Fun animal/0), A)),
%% of the horse is in the house next to the diplomat ' s
(((Lists:nth (Data_pos (horse, Fun animal/0), A) + 1) =:=
Lists:nth (Data_pos (diplomat, fun profession/0), P) OrElse
((Lists:nth (Data_pos (diplomat, fun profession/0), P) + 1) =:=
Lists:nth (Data_pos (horse, Fun animal/0), A)),
% Milk is drunk in the third house
Lists:nth (Data_pos (milk, fun drink/0), D) =:= 3,
%% The Italian likes tea
Lists:nth (Data_pos (' Italy ', fun nation/0), N) =:=
Lists:nth (Data_pos (tea, fun drink/0), D),
%% of the owner of the Green house likes coffee
Lists:nth (Data_pos (green, fun color/0), C) =:=
Lists:nth (Data_pos (coffee, fun drink/0), D),
%% of the violinist likes juice
Lists:nth (Data_pos (violinist, fun profession/0), P) =:=
Lists:nth (Data_pos (juice, fun drink/0), D)
].
Compile run:
$ erl
>1 C (Zebra).
{OK, zebra}
>2 Zebra:zebra_print ().
Nation:norway Italy England Spain Japan
Color:yellow Blue Red White green
Profession:diplomat doctor sculptor violinist painter
Animal:fox horse snail Dog zebra
Drink:water Tea milk Juice coffee
[OK]
>3 Zebra:who_own_zebra ().
[' Japan ']