Prolog 路径/小径/步行的定义

Prolog 路径/小径/步行的定义,prolog,graph-theory,transitive-closure,meta-predicate,Prolog,Graph Theory,Transitive Closure,Meta Predicate,许多谓词定义某种非循环路径,该路径是由通过二进制关系定义的边构建的,非常类似于。因此需要一个通用的定义 请注意,图论中定义的概念并不容易与通常的预期相匹配。最值得注意的是,我们对边的名称不感兴趣 更糟糕的是,图论也发生了一些变化,引入了,注意的概念 传统上,小径指的是现在通常所说的开放式步行道。现在,如果在没有任何限定条件的情况下陈述,路径通常被理解为简单的,这意味着没有顶点(因此也没有边)重复。(术语链也用于指所有顶点和边都不同的行走。) 所以我的问题是:如何命名和定义这个功能 到目前为止,我

许多谓词定义某种非循环路径,该路径是由通过二进制关系定义的边构建的,非常类似于。因此需要一个通用的定义

请注意,图论中定义的概念并不容易与通常的预期相匹配。最值得注意的是,我们对边的名称不感兴趣

更糟糕的是,图论也发生了一些变化,引入了,注意的概念

传统上,小径指的是现在通常所说的开放式步行道。现在,如果在没有任何限定条件的情况下陈述,路径通常被理解为简单的,这意味着没有顶点(因此也没有边)重复。(术语链也用于指所有顶点和边都不同的行走。)

所以我的问题是:如何命名和定义这个功能

到目前为止,我所做的是定义:

path(Rel_2, Path, X0,X)
第一个参数必须是关系的延续。然后是
路径
或一对顶点

示例用法 实施
我看不出在path/4中定义参数“start node”和“end node”的原因。似乎一个简单的路径/2和规则以及节点列表就足够了

如果用户想要一个以某个节点(例如“a”)开头的列表,他可以查询如下语句:path(some_规则,['a'| Q])

例如,用户可以请求长度为10的路径:length(p,10),path(some_rule,p)

*附录1*

一些实用目标可以很容易地添加,但它们不是主要主题。例如,带有起始节点的路径/3为:

path( some_rule, [start|Q], start ) :- 
  path ( some_rule, [start|Q ] ).   
*附录2*

添加最后一个节点作为参数可能会让人误以为此参数驱动算法,但事实并非如此。举例来说:

n(a, b).
n(a, c).
n(a, d).
以及查询的跟踪算法执行:

[trace]  ?- path( n, P, X, d ).
   Call: (6) path(n, _G1025, _G1026, d) ? creep
   Call: (7) path(n, _G1107, _G1026, d, [_G1026]) ? creep
   Exit: (7) path(n, [], d, d, [d]) ? creep
   Exit: (6) path(n, [d], d, d) ? creep
P = [d],
X = d ;
   Redo: (7) path(n, _G1107, _G1026, d, [_G1026]) ? creep
   Call: (8) n(_G1026, _G1112) ? creep

   Exit: (8) n(a, b) ? creep

   Call: (8) non_member(b, [a]) ? creep
   Call: (9) dif:dif(b, a) ? creep
   Exit: (9) dif:dif(b, a) ? creep
   Call: (9) non_member(b, []) ? creep
   Exit: (9) non_member(b, []) ? creep
   Exit: (8) non_member(b, [a]) ? creep
   Call: (8) path(n, _G1113, b, d, [b, a]) ? creep
   Call: (9) n(b, _G1118) ? creep
   Fail: (9) n(b, _G1118) ? creep
   Fail: (8) path(n, _G1113, b, d, [b, a]) ? creep
   Redo: (9) non_member(b, []) ? creep
   Fail: (9) non_member(b, []) ? creep
   Fail: (8) non_member(b, [a]) ? creep
   Redo: (8) n(_G1026, _G1112) ? creep

   Exit: (8) n(a, c) ? creep

   Call: (8) non_member(c, [a]) ? creep
   Call: (9) dif:dif(c, a) ? creep
   Exit: (9) dif:dif(c, a) ? creep
   Call: (9) non_member(c, []) ? creep
   Exit: (9) non_member(c, []) ? creep
   Exit: (8) non_member(c, [a]) ? creep
   Call: (8) path(n, _G1113, c, d, [c, a]) ? creep
   Call: (9) n(c, _G1118) ? creep
   Fail: (9) n(c, _G1118) ? creep
   Fail: (8) path(n, _G1113, c, d, [c, a]) ? creep
   Redo: (9) non_member(c, []) ? creep
   Fail: (9) non_member(c, []) ? creep
   Fail: (8) non_member(c, [a]) ? creep
   Redo: (8) n(_G1026, _G1112) ? creep

   Exit: (8) n(a, d) ? creep

   Call: (8) non_member(d, [a]) ? creep
   Call: (9) dif:dif(d, a) ? creep
   Exit: (9) dif:dif(d, a) ? creep
   Call: (9) non_member(d, []) ? creep
   Exit: (9) non_member(d, []) ? creep
   Exit: (8) non_member(d, [a]) ? creep
   Call: (8) path(n, _G1113, d, d, [d, a]) ? creep
   Exit: (8) path(n, [], d, d, [d, a]) ? creep
   Exit: (7) path(n, [d], a, d, [a]) ? creep
   Exit: (6) path(n, [a, d], a, d) ? creep
P = [a, d],
X = a .
如您所见,在这种情况下,算法无法使用暴力。
出于这个原因,如果算法没有改进,我建议不要将“end node”添加为“path”参数。

我想重点关注谓词的命名

  • 不像, 参数顺序在这里不是最重要的

  • 谓词名称应该使各个参数的含义清晰

到目前为止,我最喜欢从边到边的路径,但它也有它的优点和缺点

path_from_to_edges(Path,From,To,Edges_2) :-
    path(Edges_2,Path,From,To).
让我们把它分开:

  • pro:
    path
    是一个名词,不能误读为动词。对我来说,暗示了顶点列表

  • pro:
    from
    代表顶点,
    to
    也代表顶点

  • 缺点:
    edges
    有些模糊,但在这里使用是最通用的选择

  • 缺点:根据,路径是所有顶点(第一个和最后一个可能除外)都不同的轨迹。因此,这需要在描述中加以澄清


将lambdas用于相邻顶点的列表
Ess

?- Ess  = [a-[b],b-[c,a]], 
   From = a,
   path_from_to_edges(Path,From,To,\X^Y^(member(X-X_neibs,Ess),member(Y,X_neibs))).
Ess = [a-[b],b-[c,a]], From = a, To = a, Path = [a]     ;
Ess = [a-[b],b-[c,a]], From = a, To = b, Path = [a,b]   ;
Ess = [a-[b],b-[c,a]], From = a, To = c, Path = [a,b,c] ;
false.

编辑2015-06-02 又一次尝试更好的命名!这更倾向于
maplist/2

graph_path_from_to(P_2,Path,From,To) :-
   path(P_2,Path,From,To).
在这里,
graph
当然是一个名词,而不是动词


关于“路径”的含义:路径绝对应该允许
From=To
,默认情况下不排除这一点(使用成对的术语不等式)。用一个额外的
dif(From,to)
目标很容易排除这一点,但反过来就不行了。

这样定义
path/4
怎么样

path(R_2, Xs, A,Z) :-                   % A path `Xs` from `A` to `Z` is ...
   walk(R_2, Xs, A,Z),                  % ... a walk `Xs` from `A` to `Z` ...
   all_dif(Xs).                         % ... with no duplicates in `Xs`.
为了帮助普遍终止,我们将上述两个目标结合起来交换

path(R_2, Xs, A,Z) :-
   all_dif(Xs),                         % enforce disequality ASAP
   walk(R_2, Xs, A,Z).
。。。并使用以下延迟实现的
all_dif/1

all_dif(Xs) :- % enforce pairwise term inequality freeze(Xs, all_dif_aux(Xs,[])). % (may be delayed) all_dif_aux([], _). all_dif_aux([E|Es], Vs) :- maplist(dif(E), Vs), % is never delayed freeze(Es, all_dif_aux(Es,[E|Vs])). % (may be delayed)
上面的IMO
path/4
更简单、更容易接近,特别是对于新手。你会同意吗?

是路径/5尾递归吗?@pasa:只有在目标
调用(R_2,X0,X1)
是确定的情况下。你应该找到一种方法来接受这个问题的答案,以增加其在搜索结果中的可见性。我知道它在那里,所以我通常会搜索“[prolog]路径跟踪”,但在不太具体的搜索中,它显示得并没有它应该显示的那么高,特别是考虑到问题本身有多少选票。@Boris:到目前为止,已经花了很多时间来提高可见性。我只把我的观点放在上面10k@XXX:无论如何,这是一个遗憾。不提供额外的两个参数会使许多查询变得麻烦。例如:从
a
b
存在哪些路径?这将读作:
Path=[a | |,Path(connex,Path),last(Path,b)
将其与
Path(connex,Path,a,b)
对比您提供的示例显示:a)启动节点不是必需的;只有在使用更有效的算法之前,结束节点才是必需的。结束情况,以及通常的任何中间或最终情况,都需要比原始消息中提供的算法更好的算法。继续,很好!那么请提供一些!广告附录2:这是一个准确的观察结果。事实上,结束节点是一个稳定的参数。然而,对于一个完整的搜索,没有什么是可以期待的<代码>边建议一个边列表。缺点:与Richard O'Keefe的论点排序相反,后者将元论点放在很早的时候。此外,元参数可能没有变化,因此人们永远不会执行
调用(path_from_to_edges(path,from,to),P_2)
类似于
映射列表(path_from_to_edges(path,from,to),p2s)
表示
边的列表
——虽然我们没有一些
DIF
通常称为
所有不同的
所有不同的
@false。两全其美!谢谢!
path(R_2, Xs, A,Z) :-
   all_dif(Xs),                         % enforce disequality ASAP
   walk(R_2, Xs, A,Z).
all_dif(Xs) :- % enforce pairwise term inequality freeze(Xs, all_dif_aux(Xs,[])). % (may be delayed) all_dif_aux([], _). all_dif_aux([E|Es], Vs) :- maplist(dif(E), Vs), % is never delayed freeze(Es, all_dif_aux(Es,[E|Vs])). % (may be delayed)
:- meta_predicate walk(2, ?, ?, ?).
walk(R_2, [X0|Xs], X0,X) :-
   walk_from_to_step(Xs, X0,X, R_2).

:- meta_predicate walk_from_to_step(?, ?, ?, 2).
walk_from_to_step([], X,X, _).
walk_from_to_step([X1|Xs], X0,X, R_2) :-
   call(R_2, X0,X1),
   walk_from_to_step(Xs, X1,X, R_2).