【问题标题】:Performance issues on prologprolog 的性能问题
【发布时间】:2013-04-04 09:46:17
【问题描述】:

我目前正在实施一个 prolog 程序来计算两点之间的最短路径。 该框架已存在于 Java 项目中。作为要求,路径必须在 prolog 中实现。 因此我使用 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 秒 才能返回结果。一世 不必告诉你,这是太多的时间。但是有时 prolog 只需要 19ms。我无法弄清楚这取决于哪种情况。例如,一个包含 99 个元素的路径列表需要 19 毫秒的时间来计算,但 6 秒花费在一个仅包含 38 个元素的列表上。

您能提出任何改进建议吗?

提前感谢您的帮助!

【问题讨论】:

    标签: performance path prolog shortest-path


    【解决方案1】:

    您可以使用 Dijkstra 算法。我将answering 实现到this 问题。我的代码使用attributed variables,我认为应该在 GnuProlog 中工作(我现在将测试)。无论如何,在那里你会发现 link 可以工作的纯 Prolog 实现。

    编辑好吧,我想你可以更正你的代码,因为有一个问题:

    Path2 in searchPath/3 这是一个 singleton:那么你显然会总是以第一个 Path 结尾,并且因为第二个 findPath/3会发现总是(如果数据库没有改变)与第一个相同的成本和路径,Cost1=&lt;Cost2, 将始终为真。你可以试试

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

    对于您的任务来说足够快。否则,您将需要实现增量搜索,这并不容易,因为 Prolog 在回溯时会“返回”替代路径,然后强制使用某种副作用来选择最小值。

    more edit findall/3 会导致代码太慢。我使用不可回溯赋值编写了更高效的代码(我使用了 SWI-Prolog nb_setarg/3,你应该在 GProlog 中使用 setarg/3)。

    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).
    

    【讨论】:

    • 感谢您的建议。代码完美运行。但不幸的是,它没有帮助我是有原因的:这个项目是大学的作业,我不允许复制任何代码。 dijkstra 算法对我来说太复杂了:-(
    • 感谢您为我投入这么多时间!!!您的实施工作正常!不过,我有一些问题您或许可以回答: 1. 即使阅读了手册,我也不是很清楚nb_setargsetarg 之间的区别。对吗,如果回溯找到另一个解决方案,setarg 会替换值,但nb_setarg 不会发生这种情况? 2. 我不明白为什么你的算法中有failtrue。你能解释一下他们的目的吗?
    • 这些是 Prolog 的特性。 fail 通过 findPath 生成下一个解决方案,但在生成最后一个可用的解决方案后,它将失败,true 将允许完成该过程。见->control predicates。不相关:我认为可以删除 repeat/0。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-04-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多