2013-04-04 76 views
2

我目前正在實施prolog程序來計算兩點之間的最短路徑。 該框架已經存在於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秒才能返回結果。我不需要告訴你這是太多的時間。不過有時prolog只需要19ms。我無法弄清楚這取決於哪些情況。例如,一個包含99個元素的路徑列表需要19ms的時間來計算,但是在一個僅包含38個元素的列表上花費了6秒。

你能提出任何改進建議嗎?

感謝您的幫助提前!

回答

2

您可以使用Dijkstra算法。我實施了answeringthis的問題。我的代碼使用attributed variables,我認爲應該在GnuProlog中工作(我現在會測試)。無論如何,你會發現link到一個可行的純Prolog實現。

編輯好了,我想你可以糾正你的代碼,因爲有一個問題:

searchPath/3 Path2它是一個:那麼你顯然要總是末與第一個路徑,並且因爲第二個findPath/3將始終發現總是(如果數據庫不更改)與第一個相同的Cost和Path,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,您應該在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). 
+0

謝謝您的建議。代碼完美。但不幸的是,有一個原因,它不能幫助我:這個項目是大學的一項任務,我不允許複製任何代碼。對於我來說,dijkstra算法實現起來非常複雜:-( – Markus 2013-04-04 11:10:19

+0

感謝您爲我投入這麼多時間!!!您的實現工作正常!然而,我有一些問題或許可以回答:1.即使在閱讀手冊後, m沒有真正意識到'nb_setarg'和'setarg'之間的區別,是不是真的知道'nb_setarg'和'setarg'之間的區別?如果回溯找到另一個解決方案,但是'nb_setarg'不會發生這種情況,用'setarg'取代Value?2.我無法確定爲什麼在你的算法中有一個'fail'和'true',你能解釋一下它們的用途嗎? – Markus 2013-04-04 17:10:52

+0

這些是Prolog的特性.'fail'通過findPath產生下一個解決方案,但是在產生最後一個解決方案之後,它會失敗,'true'將允許完成這個過程。參見 - > [控制謂詞](http://www.swi-prolog.org/pldoc/doc_for?object=section%282,%274.8% 27,swi%28%27/doc/Manual/control.html%27%29%29)無關:我認爲重複/ 0可以被刪除編輯。 – CapelliC 2013-04-04 17:25:55