Решить динамическое программирование в Прологе с помощью corecursion

Я хотел бы решить следующую проблему динамического программирования с помощью corecursion в Prolog. Но я застрял в поиске в ширину, который хотел бы осуществить корректирующим образом:

Есть здание n этажей с лифтом, которое может подниматься только на 2 этажа одновременно и на 3 этажа одновременно. Используя динамическое программирование, напишите функцию, которая будет вычислять количество шагов, которое требуется лифту, чтобы добраться от этажа i до этажа j.

Я уже решил о ленивом представлении списка. Ленивый список - это просто замыкание Пролога С, которое можно вызвать, чтобы получить голову и новое замыкание для хвоста.

Пример потока из них:

 one(1, one).

Предикат взятия Haskell может быть просто закодирован следующим образом:

 take(0, _, L) :- !, L = [].
 take(N, C, [X|L]) :- N > 0, 
    call(C, X, D), 
    M is N-1, 
    take(M, D, L). 

Вот пример запуска:

 ?- take(5, one, X).
 X = [1, 1, 1, 1, 1].

 ?- take(10, one, X).
 X = [1, 1, 1, 1, 1, 1, 1, 1, 1|...].

1 ответ

В этом соврекурсивном решении Prolog нам нужны два строительных блока.

Один строительный блок - это способ перечисления дерева поиска ко-рекурсивно в Прологе. Мы принимаем идею о том, что термин закрытия Пролога должен содержать повестку дня с путями и, следовательно, узлами, которые должны быть расширены. Затем мы можем начать с повестки дня, которая содержит только корень:

% tree(-Path, -LazyPaths)
tree(H, T) :-
   tree([[]], H, T). 

Для архивирования ширины первого перечисления мы добавим новые расширенные пути и, таким образом, узлы в конце повестки дня. Это можно сделать с помощью простого вызова предиката добавления в список, так что отсутствующее определение выглядит следующим образом. В полном двоичном дереве пути и, следовательно, узлы всегда расширяются дважды:

% tree(+Paths, -Path, -LazyPaths)
tree([X|Y], X, tree(Z)) :-
   append(Y, [[0|X],[1|X]], Z). 

Вот пример запуска:

?- take(5, tree, L).
L = [[],[0],[1],[0,0],[1,0]]

?- take(10, tree, L).
L = [[],[0],[1],[0,0],[1,0],[0,1],[1,1],[0,0,0],[1,0,0],[0,1,0]] 

В случае проблемы с оценщиком у нас будет путь и, следовательно, расширение узла, которое не всегда приведет к двум преемникам. Если мы находимся на уровне k, лифт может перейти на уровень k+2 или k-3, только при условии, что лифт остается внутри здания. Таким образом, мы с готовностью приходим к ко-рекурсивным шагам предиката, которые действительно моделируют все возможные пути лифта:

?- take(5, steps(7,[[2]]), L).
L = [[2],[4,2],[1,4,2],[6,4,2],[3,1,4,2]]

?- take(10, steps(7,[[2]]), L).
L = [[2],[4,2],[1,4,2],[6,4,2],[3,1,4,2],[3,6,4,2],
    [5,3,1,4,2],[5,3,6,4,2],[2,5,3,1,4,2],[7,5,3,1,4,2]]

Последнее препятствие и второй строительный блок - получить Haskell dropWhile в Прологе. Мы не стремились к предикату, который принимает аргумент термина закрытия Пролога для логического условия, но вместо этого предоставили только предикат, который перечисляет ленивые элементы списка, и пользователь предиката может фильтровать в продолжении Пролога.

% drop_while(+LazyList, -Element)
drop_while(C, P) :-
   call(C, Q, T),
   (P = Q; drop_while(T, P)).

Если мы соберем все вместе, мы получим совместное решение Prolog, которое может даже потенциально перечислить все бесконечные решения задачи оценщика с помощью обратного отслеживания, кроме расчета результатов в первом порядке в ширину:

?- elevator(7,2,6,L), length(L,N).
L = [6,4,2],
N = 3 ;
L = [6,4,2,5,3,1,4,2],
N = 8 ;
L = [6,4,7,5,3,1,4,2],
N = 8 
Другие вопросы по тегам