Решатель мини-судоку на Прологе останавливается на полпути

Я работаю над «Семью языками за семь недель» и просто пытаюсь заставить работать пример из книги. Он решает сетку мини-судоку (4x4).

Автор использует gprolog, но я использую swi-prolog (по какой-то причине мне не удалось заставить gprolog работать на моей виртуальной машине, но swi-prolog сработал с первой попытки).

Я использую Ubuntu 10.04 в VirtualBox 4.0.4 r70112 (надеюсь, это не слишком важно!)

Вот код в моем пролог-файле:

:- use_module(library(clpfd)).

valid([]).
valid([Head|Tail]) :-
    all_different(Head),    % in the book, this is 'fd_all_different'
    valid(Tail).

 % beginning of sudoku rule itself
sudoku(Puzzle, Solution) :- 
Solution = Puzzle,
Puzzle = [S11, S12, S13, S14,
          S21, S22, S23, S24,
          S31, S32, S33, S34,
          S41, S42, S43, S44],
Puzzle ins 1..4,    % in the book, this is 'fd_domain'

Row1 = [S11, S12, S13, S14],
Row2 = [S21, S22, S23, S24],
Row3 = [S31, S32, S33, S34],
Row4 = [S41, S42, S43, S44],

Col1 = [S11, S21, S31, S41],
Col2 = [S12, S22, S32, S42],
Col3 = [S13, S23, S33, S43],
Col4 = [S14, S24, S34, S44],

Square1 = [S11, S12, S21, S22],
Square2 = [S13, S14, S23, S24],
Square3 = [S31, S32, S41, S42],
Square4 = [S33, S34, S43, S44],

valid([Row1, Row2, Row3, Row4,
       Col1, Col2, Col3, Col4,
       Square1, Square2, Square3, Square4]).

Единственные части, которые я (намеренно) изменил, были:

  • добавление use_module(library(clpfd)). вверху
  • изменение fd_all_different(Head), на all_different(Head),
  • изменение fd_domain(Puzzle, 1, 4), на Puzzle ins 1..4,

Вот вызов от swipl

?- sudoku([_, _, 2, 3,
           _, _, _, _,
           _, _, _, _,
           3, 4, _, _],
Solution).
Solution = [4, 1, 2, 3, 2, 3, 4, 1, 1|...] ;
false.

Решение правильно до тех пор, пока оно не обрывается, и в этот момент пролог, кажется, определяет, что решения нет. Но есть:

4 1 2 3
2 3 4 1
1 2 3 4
3 4 1 2

Я просмотрел код в поисках опечатки или неуместного столбца, но не смог найти источник этого. Любые идеи?


person Nick Knowlson    schedule 11.04.2011    source источник
comment
Разве это не должно быть :- use_module(library(clpfd))?   -  person Fred Foo    schedule 11.04.2011
comment
Да, должно быть, спасибо. Я предполагаю, что я загрузил библиотеку ранее прямо в swipl, и это не привело к ошибке (пока я не вышел и не перезапустил ее). К сожалению, даже после исправления он все равно дает тот же результат.   -  person Nick Knowlson    schedule 11.04.2011
comment
Та часть, на которой он обрывается, — это место, где Пролог перестает показывать элементы массива, но все еще там. Вам придется написать дополнительный предикат, чтобы помочь вам отобразить все элементы в массиве.   -  person Raceimaztion    schedule 11.04.2011


Ответы (2)


Мне кажется, что вы полагаетесь на отображение по умолчанию в SWI-Prolog для записи списка, представляющего решение, и особенностью SWI-Prolog является то, что в этом случае он не печатает все записи длинного списка, заменяя хвост после девяти ст с "многоточием"...

Вы наткнулись на это, когда добавили write(Puzzle) к цели и таким образом увидели весь список. На сайте SWI-Prolog есть часто задаваемые вопросы об этом "аббревиатуре" списков.

person hardmath    schedule 11.04.2011
comment
Спасибо, это очень приятно знать! Изменение значения max_depth по умолчанию с помощью оператора, предложенного в FAQ, на который вы ссылаетесь, отлично работает! - person Nick Knowlson; 11.04.2011
comment
@Nick Knowlson: Обратите внимание, что другой способ сделать это на лету — нажать w вместо ;. Это вынуждает интерпретатор верхнего уровня выдавать полный список (или структуру, поскольку на самом деле ограничение глубины вложенности влияет на отображение). - person hardmath; 11.04.2011
comment
Я бы сделал это, но после того, как я вышел из swipl и перезагрузил его (чтобы убедиться, что я исправил синтаксис импорта модуля), он завершает работу и дает мне решение (независимо от того, выполнено ли оно полностью или частично), не запрашивая меня сейчас. Возможно, потому что теперь он может сказать, что для этого конкретного запроса есть только одно решение? Впрочем, я запомню это на будущее. - person Nick Knowlson; 11.04.2011
comment
@Nick Knowlson: Вы, вероятно, захотите написать небольшой предикат для отображения сетки судоку в ее знакомом макете (как показано в вашем последнем разделе кода). Конечно, это делает его более читабельным даже для случая 4x4, не говоря уже о сложности разбора сетки 9x9 в простом формате списка. - person hardmath; 12.04.2011
comment
Это на самом деле следующий шаг в этих проблемах! :) Я хотел убедиться, что понял, что происходит, прежде чем двигаться дальше. И я согласен, я бы точно не хотел видеть, получаю ли я правильные решения, читая плоский список из 81 числа! - person Nick Knowlson; 12.04.2011

Вы сами ввели ;, не так ли? ; просит больше решений. Поскольку вы не использовали labeling для переменных, Prolog только ограничивает их, фактически не создавая полного решения (он делает некоторые распространение ограничений для получения некоторых значений). Есть только один способ поставить ограничения, так что второго решения нет. Если вы поместите вызов labeling в конце предложения sudoku, вы сможете переключаться между решениями.

(PS.: sudoku не требует двух аргументов, так как вы объединяете их с Solution = Puzzle.)

person Fred Foo    schedule 11.04.2011
comment
Да, я сам набрал ;, наверное, надо было это включить. Я понимаю, что второго решения нет. На самом деле, после повторного запуска в новом swipl вывод выглядит немного иначе, он дает только один ответ, не спрашивая меня, хочу ли я искать больше. Боюсь, я не понимаю, что вы имеете в виду, когда говорите «позвоните labeling в конце». - person Nick Knowlson; 11.04.2011
comment
Хорошая мысль о том, что не нужны два аргумента. На самом деле, как только я изменил его, чтобы вместо этого использовать один аргумент, и добавил write(Puzzle) в конце, он распечатал все это! Почему это случилось? - person Nick Knowlson; 11.04.2011
comment
Итак, если бы я хотел сохранить прежний способ и использовать labeling, что бы это повлекло за собой? Я просмотрел документы и попытался добавить label(Solution) в конце, но это ничего не изменило. Это если бы он все еще использовал два параметра (ради любопытства) - person Nick Knowlson; 11.04.2011
comment
@Nick: в этом случае решение может быть полностью определено ограничениями, но в целом вам нужен вызов labeling или label для фактического поиска решений в проблеме ограничений. Конечно, если есть только одно решение, labeling не найдет второго. - person Fred Foo; 11.04.2011
comment
Это хорошо знать, что в общем случае мне нужен вызов маркировки или метки в задачах с ограничениями. Я сделал вызов label, как я сказал в своем последнем комментарии, но я, должно быть, «делаю это неправильно», потому что он по-прежнему вернул только часть решения. - person Nick Knowlson; 11.04.2011