В настоящее время я реализую программу пролога для вычисления кратчайшего пути между двумя точками. Фреймворк уже существует в проекте Java. Как требование, путь должен быть реализован в прологе. Поэтому я использую gnu.prolog ( http://www.gnu.org/software/gnuprologjava/)

Из java я вызываю searchPath(1,5,Path), который вернет Path=[5,4,3,2,1]

Вот мой код пролога:

:- dynamic(path/3).

findPath( [Goal | Rest], Goal, Temp, Temp, [Goal | Rest]).

findPath( [A | Rest], Goal, Cost, Temp, Path) :-
    path(A,B,C),
    \+member(B, [A | Rest]),
    NewCosts is (Temp + C),
    findPath([B, A | Rest], Goal, Cost, NewCosts, Path).

searchPath(Start,Goal,Path_to_goal) :-
    findPath([Start], Goal, Cost1, 0, Path),
    findPath([Start], Goal, Cost2, 0, Path2),
    Cost1=<Cost2,
    Path_to_goal = Path.

У меня есть две проблемы с этим:

  1. Метод searchPath должен возвращать кратчайший путь . Однако это не делает НЕ . Это приводит к тому, что мой призрак «решает» переключить направление в какой-то момент, приводящее к дрожанию призрака слева направо.

  2. Мой код пролога возвращает результат до 6 секунд . я не нужно говорить вам, что это слишком много времени. Однако иногда для пролога требуется всего 19 мс . Я не мог понять, от каких обстоятельств это зависит. Например, для расчета списка путей, содержащего 99 элементов, требуется 19 мс, но 6 секунд были потрачены на список, содержащий всего 38 элементов.

Вы можете предложить какие-нибудь улучшения?

Заранее спасибо за помощь!

2
Markus 4 Апр 2013 в 13:46

1 ответ

Лучший ответ

Вы можете использовать алгоритм Дейкстры. Я реализовал его отвечая на это вопрос. В моем коде используется переменные с атрибутами, я думаю, они должны работать в GnuProlog (сейчас я протестирую). В любом случае, там вы найдете ссылку на рабочий чистый Prolog реализация.

edit ну, я думаю, вы могли бы исправить свой код, потому что есть проблема:

Path2 в searchPath / 3 это singleton : тогда вы явно собираетесь всегда заканчиваться первым Path, а поскольку второй findPath / 3 найдет всегда (если база данных не изменяется) те же Стоимость и Путь, что и первый, Cost1=<Cost2, всегда будет истинным. Вы могли бы попробовать, если

searchPath(Start,Goal,Path_to_goal) :-
    findall(Cost-Path, findPath([Start], Goal, Cost, 0, Path), Paths),
    sort(Paths, [_-Path_to_goal|_]).

Достаточно быстро для вашего задания. В противном случае вам нужно будет реализовать инкрементный поиск, что нелегко, потому что Prolog «возвращает» альтернативные пути при обратном отслеживании, а затем заставляет использовать какой-то побочный эффект для выбора минимального значения.

дополнительное редактирование findall / 3 приведет к слишком медленной работе кода. Я написал что-то более эффективное, используя присваивание без возврата (я использовал SWI-Prolog nb_setarg / 3, вы должны использовать setarg / 3 в GProlog).

findPath(_Limit, [Goal | Rest], Goal, Temp, Temp, [Goal | Rest]) :- !.

findPath(Limit, [A | Rest], Goal, Cost, Temp, Path) :-
    path(A,B,C),
    \+member(B, Rest),
    NewCosts is (Temp + C),
    NewCosts < Limit,
    findPath(Limit, [B, A | Rest], Goal, Cost, NewCosts, Path).

% ?- searchPath(aberdeen, glasgow, Path, Length).
%
searchPath(Start, Goal, Path_to_goal, L) :-
    S = path_len([], 1000000),
    repeat,
    arg(2, S, Limit),
    (   findPath(Limit, [Start], Goal, Cost, 0, Path)
    ->  (   Cost < Limit
        ->  nb_setarg(1, S, Path),
        nb_setarg(2, S, Cost),
        fail
        )
    ;   true
    ),
    arg(1, S, Rev),
    reverse(Rev, Path_to_goal),
    arg(2, S, L).
2
Community 23 Май 2017 в 15:13
Спасибо за ваше предложение. Код работает отлично. Но, к сожалению, есть причина, по которой это мне не помогает: этот проект является заданием для университета, и мне не разрешено копировать какой-либо код. Алгоритм Дейкстры для меня слишком сложен для повторной реализации :-(
 – 
Markus
4 Апр 2013 в 15:10
Спасибо, что уделили мне столько времени !!! Ваша реализация работает нормально! Тем не менее, у меня есть несколько вопросов, на которые вы, возможно, сможете ответить: 1. Даже после прочтения руководства я не совсем понимаю разницу между nb_setarg и setarg. Верно ли, что с setarg Значение заменяется, если при поиске с возвратом найдено другое решение, но этого не произойдет с nb_setarg? 2. Я не могу понять, почему в вашем алгоритме есть fail и true. Не могли бы вы объяснить их цель?
 – 
Markus
4 Апр 2013 в 21:10
Это особенности Пролога. fail создает следующее решение с помощью findPath, но после того, как оно сгенерирует последнее доступное, оно завершится ошибкой, и true позволит завершить процедуру. См. -> на управляющие предикаты. Несвязанный: я думаю, что repeat / 0 можно было бы удалить.
 – 
CapelliC
4 Апр 2013 в 21:25