我有一个如下定义的图表:
edge(a1,c1,7,blue).
edge(a1,d1,3,blue).
edge(a1,l1,8,red).
edge(b1,c1,11,blue).
edge(d1,e1,2,red).
....
等等。
我知道如何编写代码来检查是否有路径并计算权重和最短路径。 但我想要做的是输出2个整数:一个用于no.of蓝色,一个用于从X到Y的路径中的红色边缘。
从本质上讲,我想要这样的事情:
colour(X,Y,C).
要么返回:
"blue" if all the edges in the path are blue(or Red for all red)
"false" if there are mixed edges.
或类似的东西:
blue:5 red:2 if there are 7 edges
我是Prolog的新手,如何创建“计数器”类型变量非常令人困惑。
答案 0 :(得分:4)
对于edge/4
谓词,可能还有两种方法可以计算相同颜色的边数:
1)使用谓词查找所有解决方案,例如findall/3
:
opt1 :-
findall(_,edge(_,_,_,blue),ListBlue), % collect all blue edges in ListBlue
length(ListBlue,L), % find out the length of ListBlue
display('blue: '),display(L),nl. % print output
_
符号代表“匿名变量”,这是一个您不关心的绑定变量。
这个解决方案的缺点是它不是资源方面的:首先你通过所有edge/4
事实(不可避免的成本),然后你使用额外的内存来存储ListBlue
列表然后你必须遍历此列表。
2)在故障驱动的循环中使用side effects(见下文)和dynamic谓词(谓词妓女条款可以在运行时添加/删除):
Side effects是从一个程序状态转换到另一个程序状态时可以执行的一些额外操作,例如在将值赋给变量(直接效果)的操作期间打印文本(副作用)。
:- dynamic counter/2. % a dynamic predicate to be used as a mutable counter
opt2 :-
retractall(counter(_,_)), % \_ delete any old value of a counter from previous
asserta(counter(blue,0)), % / runs and set it to 0 for the blue edges
%
count_edges(blue,NBlue),
display('blue: '),display(NBlue),nl.
count_edges(Col,_N) :-
edge(_,_,_,Col), % locate an edge of desired color Col
counter(Col,N), % \
N1 is N + 1, % \_ update the counter value
retract(counter(Col,N)), % /
asserta(counter(Col,N1)), % /
fail. % fail to force backtracking
count_edges(Col,N) :-
counter(Col,N). % read the current counter value
强制回溯可以探索edge(_,_,_,Col)
查询的所有可能解决方案,从而找到所有蓝色边缘。
我已经尝试了代码online,但它确实有效。
答案 1 :(得分:3)
我想发布一些基准测试,这可能有助于比较@ s0nata发布的两个不错的解决方案。这是一个额外的答案,主要是因为评论太长了。
Prolog是一种设计和运行基准测试的好语言,也是因为单个查询可以很容易地描述大量的基准测试。
例如,让我们使用以下查询对使用findall/3
的选项1 进行基准测试:
?- length(_, E), N #= 2^E, portray_clause(E), forall(between(1, N, _), assertz(edge(x, y, 1, blue))), time(opt1), false.
请注意,我们正在使用assertz/1
为edge/4
创建更多事实。这是非常不优雅的,这意味着我们必须在这些运行之间重新启动 Prolog或者再次清除数据库以便从一个干净的平板开始。我把它作为一种练习让它更优雅。
在任何情况下,都会产生以下输出:
0. 'blue: '1 % 3,454 inferences, 0.001 CPU in 0.009 seconds (16% CPU, 2364134 Lips) 1. 'blue: '3 % 22 inferences, 0.000 CPU in 0.000 seconds (76% CPU, 785714 Lips) 2. 'blue: '7 % 26 inferences, 0.000 CPU in 0.000 seconds (74% CPU, 1181818 Lips) 3. 'blue: '15 % 34 inferences, 0.000 CPU in 0.000 seconds (77% CPU, 1478261 Lips) 4. 'blue: '31 % 50 inferences, 0.000 CPU in 0.000 seconds (79% CPU, 1851852 Lips) ... lines omitted ... 21. 'blue: '4194303 % 4,194,322 inferences, 1.301 CPU in 1.439 seconds (90% CPU, 3224179 Lips)
为了进行比较,我们使用选项2 :
?- length(_, E), N #= 2^E, portray_clause(E), forall(between(1, N, _), assertz(edge(x, y, 1, blue))), time(opt2), false. 0. 'blue: '1 % 3,448 inferences, 0.001 CPU in 0.009 seconds (16% CPU, 2347175 Lips) 1. 'blue: '3 % 20 inferences, 0.000 CPU in 0.000 seconds (81% CPU, 769231 Lips) 2. 'blue: '7 % 32 inferences, 0.000 CPU in 0.000 seconds (80% CPU, 1142857 Lips) 3. 'blue: '15 % 56 inferences, 0.000 CPU in 0.000 seconds (84% CPU, 888889 Lips) 4. 'blue: '31 % 104 inferences, 0.000 CPU in 0.000 seconds (85% CPU, 1350649 Lips) ... lines omitted ... 21. 'blue: '4194303 % 12,582,920 inferences, 10.100 CPU in 10.305 seconds (98% CPU, 1245782 Lips)
为了更优雅地推理这些基准测试,我们现在会稍微更改 opt1/0
和opt2/0
,以便这些谓词不会弄乱我们自己的基准输出< / strong>即可。也就是说,我们将从这些谓词中删除所有副作用,从而通过使实际计数器可用作参数<来更轻松地测试这些谓词/ strong>即可。我们可以写例如:
opt1(L) :- findall(_,edge(_,_,_,blue),ListBlue), length(ListBlue, L). opt2(NBlue) :- retractall(counter(_,_)), asserta(counter(blue,0)), count_edges(blue, NBlue).
此外,让我们介绍以下辅助定义,以获得执行目标所需的墙时间。这可能包括由于其他任务,垃圾收集等引起的轻微扰动,但重要的是要考虑执行目标需要多少实际时间(以秒为单位):
wall_time(Goal, T) :- statistics(walltime, [T0|_]), Goal, statistics(walltime, [T1|_]), T is (T1 - T0)/1000.
现在让我们发帖:
?- format("~tE~t~5+~tNum~t~15+~tT1~10+~tT2~10+~n"), length(_, E), N #= 2^E, forall(between(1, N, _), assertz(edge(x, y, 1, blue))), wall_time(opt1(Num), T1), wall_time(opt2(Num), T2), format("~t~w~5+~t~w~15+~t~2f~10+~t~2f~10+~n", [E,Num,T1,T2]), false.
这导致下表:
E Num T1 T2 0 1 0.00 0.00 1 3 0.00 0.00 2 7 0.00 0.00 3 15 0.00 0.00 4 31 0.00 0.00 5 63 0.00 0.00 6 127 0.00 0.00 7 255 0.00 0.00 8 511 0.00 0.00 9 1023 0.00 0.00 10 2047 0.00 0.01 11 4095 0.00 0.01 12 8191 0.00 0.03 13 16383 0.01 0.04 14 32767 0.02 0.08 15 65535 0.02 0.16 16 131071 0.04 0.32 17 262143 0.09 0.64 18 524287 0.18 1.31 19 1048575 0.37 2.55 20 2097151 0.74 5.14 21 4194303 1.44 10.29
在比较两种解决方案时,请同时考虑这些实际基准。在Prolog中,杂质经常会导致很多效率低下。
请注意我们如何利用这个机会验证两个选项产生相同的结果!
答案 2 :(得分:0)
显示您显示的数据库片段,以及库(aggregate):
?- aggregate(count,X^Y^W^edge(X,Y,W,C),N).
C = blue,
N = 3 ;
C = red,
N = 2.