2013-12-09 4 views
3

Я пробовал писать двоичный решатель sudoku в swi-prolog. (объясняется двоичный файл sudoku here)proog sudoku solver закончился из глобального стека

Проблема в том, что я теперь исчерпал глобальный стек. Я даю ему 2 ГБ, которого должно быть более чем достаточно. Использую ли я испорченный алгоритм? Есть ли что-то, что я мог бы сделать лучше, чтобы избежать столкновения с отсутствием глобальной ошибки стека с такими маленькими головоломками?

Немного больше информации: Я уже исчерпал стек на головоломках 4X4, которые с первым ограничением применяют только 6^4 возможности. Вы можете запросить эту проблему с:

problems(2,Field),binary_sudoku(Field). 

код здесь:

:-use_module(library(clpfd)). 

valid_row(Row) :- 
    Row ins 0..1, 
    length(Row,L), 
    sum(Row,#=,L/2). 

matrixNth1(Matr,X,Y,El) :- 
    nth1(Y,Matr,CurRow), 
    nth1(X,CurRow,El). 

all_diff([]). 
all_diff([X|Y]) :- 
    maplist(dif(X),Y), 
    all_diff(Y). 


valid(_,1,1). 
valid(Rows,1,Y) :- 
    length(Rows,Y). 
valid(Rows,X,1) :- 
    length(Rows,X). 
valid(Rows,X,X) :- 
    length(Rows,X). 

valid(Rows,X,Y) :- 
    matrixNth1(Rows,X,Y,0). 
valid(Rows,X,Y):- 
    AboveY is Y-1, 
    matrixNth1(Rows,X,AboveY,0). 
valid(Rows,X,Y):- 
    BelowY is Y+1, 
    matrixNth1(Rows,X,BelowY,0). 
valid(Rows,X,Y):- 
    LeftX is X-1, 
    matrixNth1(Rows,LeftX,Y,0). 
valid(Rows,X,Y):- 
    RightX is X+1, 
    matrixNth1(Rows,RightX,Y,0). 

binary_sudoku(Rows) :- 
    length(Rows,Height), 
    transpose(Rows,Cols), 
    length(Cols,Height), 
    maplist(valid_row,Rows), 
    foreach(between(1,Height,X),foreach(between(1,Height,Y),valid(Rows,X,Y))), 
    all_diff(Rows),all_diff(Cols). 


problems(1,[[_,_],[_,_]]). 

problems(2,[[_,_,_,_],[_,_,_,_],[_,_,_,_],[_,_,_,_]]). 
+0

Как вы запрашивая для решения? Почему вы определяете свой собственный 'all_diff' вместо того, чтобы использовать' all_different/1' или 'all_distinct/1' уже имеющиеся? Почему вы не устанавливаете «все разные» ограничения раньше? –

+0

Я должен был добавить, что это первое, что я когда-либо делал в прологе, и все для меня кажется неуклюжим/волшебным, исходящим из C .... all_distinct не работал, потому что он ограничивает целые числа разными, в то время как я хочу списки от целых чисел. Независимо от того, что all_diff не является проблемой, очень мало решений с неповторимыми строками/столбцами. Насколько я могу судить, проблема с моей двойной петлей foreach. – camel

+0

Итак, вы получаете решение на плате 2x2? Как вы запрашиваете его? –

ответ

3

Есть несколько проблем с кодом. Если вы действительно новичок Prolog/clpfd, вы можете начать с чего-то немного легче.

Идея программ CLP заключается в том, чтобы сначала установить ограничения (которые являются детерминированными), а затем выполнить поиск решений (которые являются недетерминированными). В вашем коде valid_row/1 и all_diff/1 можно считать ограничениями, но действительный/3 является недетерминированным и делает много вариантов.

Итак, первое изменение, которое вы должны сделать, - это перевести свой вызов на действительный/3 до самого конца.

Затем вы должны изменить действительный/3 так, чтобы систематически перечислял все возможные назначения переменных. С текущего кода и матрицы 3х3, первые несколько решений, которые генерирует

foreach(between(1,Height,X),foreach(between(1,Height,Y),valid(Rows,X,Y))) 

являются

[[A, 0, C], [0, 0, 0], [G, 0, I]] 
[[A, 0, C], [0, 0, 0], [G, 0, 0]] 
[[A, 0, C], [0, 0, 0], [G, 0, I]] 
[[A, 0, C], [0, 0, 0], [G, 0, I]] 
[[A, 0, 0], [0, 0, F], [G, 0, I]] 
... 

Это плохо, потому что они все еще содержат переменные, и они даже не исключают друг друга. Оба из них означают, что вы посещаете одни и те же задания снова и снова. Перепишите его так, чтобы каждый раз, когда он преуспевал, все переменные устанавливались в 0/1, и все решения были разными. Затем вы обязательно пройдете поисковое пространство только один раз, и у вас будет возможность найти решение в разумные сроки.

+0

Итак, в прологе я должен попытаться вернуть дискретные решения вместо отношений между переменными? – camel

+0

Насколько я могу судить, действительный/3 не устанавливает никаких отношений между переменными. Он состоит из нескольких альтернативных предложений, которые все создают разные переменные. Скорее всего, это создать единую переменную для всех ее возможных значений. – jschimpf

5

Вот компактное решение в ECLiPSe (Prolog с ограничениями и расширениями моделирования, http://eclipseclp.org). Он использует ограничения сумм для числа 0/1s на строку/столбец, ограничения последовательности/4 для условия no-three-1s и lex_ne/2 для обеспечения различия между строками. Поиск решения выполняется путем вызова метки/1 в конце. Кроме того, используется матричная нотация, которая более удобна, чем списки в таких настройках.

:- lib(gfd). 

solve(Name, Mat) :- 
    problem(Name, Mat), 
    dim(Mat, [N,N]), 
    Mat #:: 0..1, 
    N #= 2*K, 
    (for(I,1,N), param(Mat,K,N) do 
     sum(Mat[I,1..N]) #= K, 
     sum(Mat[1..N,I]) #= K, 
     sequence(1, 2, 3, Mat[I,1..N]), 
     sequence(1, 2, 3, Mat[1..N,I]), 
     (for(J,I+1,N), param(Mat,I,N) do 
      lex_ne(Mat[I,1..N], Mat[J,1..N]), 
      lex_ne(Mat[1..N,I], Mat[1..N,J]) 
     ) 
    ), 
    labeling(Mat). 

problem(2, [](
    [](_,1,0,_,_,_,_,0,_,_,0,_), 
    [](_,1,1,_,_,1,_,_,_,_,_,_), 
    [](_,_,_,_,_,_,_,_,1,_,_,0), 
    [](_,_,0,0,_,_,_,_,_,_,_,0), 
    [](_,_,_,_,_,_,1,1,_,0,_,_), 
    [](_,1,_,0,_,1,1,_,_,_,1,_), 
    [](_,_,_,_,_,_,_,_,1,_,_,_), 
    [](1,_,_,1,_,_,_,_,_,_,0,_), 
    [](_,1,_,_,_,_,_,_,0,_,_,_), 
    [](_,_,_,_,_,_,_,0,_,_,_,_), 
    [](1,_,_,_,_,_,_,_,_,_,_,1), 
    [](_,1,_,1,_,_,_,_,_,0,0,_))). 

Это воспитывает (единственное) решение быстро:

?- solve(2, M). 
M = []([](1, 1, 0, 0, 1, 0, 1, 0, 1, 1, 0, 0), 
     [](0, 1, 1, 0, 0, 1, 0, 1, 0, 0, 1, 1), 
     [](1, 0, 0, 1, 1, 0, 1, 0, 1, 0, 1, 0), 
     [](1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0), 
     [](0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1), 
     [](0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0), 
     [](1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1), 
     [](1, 0, 1, 1, 0, 0, 1, 0, 0, 1, 0, 1), 
     [](0, 1, 0, 0, 1, 1, 0, 1, 0, 1, 1, 0), 
     [](0, 0, 1, 1, 0, 1, 1, 0, 1, 0, 1, 0), 
     [](1, 0, 1, 0, 1, 0, 0, 1, 0, 1, 0, 1), 
     [](0, 1, 0, 1, 0, 1, 0, 1, 1, 0, 0, 1)) 
Yes (0.03s cpu, solution 1, maybe more)