转载自:https://mp.weixin.qq.com/s/jkWcTwLV9tH8yfOjnzaE9A
初步说明:图论中的词汇差异很大。一些作者使用相同的词具有不同的含义。有些作者用不同的词来表示同一件事。我希望我们的定义没有矛盾。
图被定义为一组节点和一组边,其中每个边是一对节点。
在Prolog中有几种表示图的方法。
一种方法是将每个边分别表示为一个子句(事实)。以这种形式,相应的图表示为以下谓词:
edge(h,g).
edge(k,f).
edge(f,b).
...
我们称此为edge-clause。
显然,独立的节点无法表示。另一种方法是将整个图表示为一个数据对象。根据图的定义为两个集合对(节点和边),我们可以使用以下Prolog项来表示上述示例图:
graph([b,c,d,f,g,h,k],[e(b,c),e(b,f),e(c,f),e(f,k),e(g,h)])
我们称这种图项形式。注意,列表保持排序,它们实际上是集合,没有重复的元素。每个边在边列表中仅出现一次;即,从一个节点x到另一个节点y的边表示为e(x,y),则不存在项e(y,x)。图项形式是我们的默认表示形式。 在SWI-Prolog中,有预定义的谓词可用于集合。
第三种表示方法是与每个节点关联与该节点相邻的节点集。我们称其为 邻接列表形式。在我们的示例中:
[n(b,[c,f]), n(c,[b,f]), n(d,[]), n(f,[b,c,k]), ...]
到目前为止,我们介绍的表示形式都是Prolog项,因此非常适合自动化处理,但是它们的语法不是非常用户友好。手工输入这些项很麻烦且容易出错。我们可以定义一个更紧凑和“对人类友好(human-friendly)”的表示法,如下所示:图由X-Y类型的原子和项(即函子’-‘和arity 2)的列表表示。原子代表独立的节点,X-Y项描述边。如果X出现为边的端节点,则会自动将其定义为节点。我们的示例可以写成:
[b-c, f-c, g-h, d, f-b, k-f, h-g]
我们称其为人类友好的形式(human-friendly)。如示例所示,列表不必排序,甚至可以多次包含相同的边。注意独立节点d。(实际上,在Prolog的意义上,独立的节点甚至不必是原子,它们可以是复合项,例如d(3.75,blue)而不是示例中的d)。
当边有指向后,我们称其为弧(arc)。这些由有序对表示。这样的图称为有向图(digraph)。为了表示有向图,对上面讨论的形式进行了一些修改。相应的示例图如下所示:
Arc-clause形式
arc(s,u).
arc(u,r).
...
图项(Graph-term)形式
digraph([r,s,t,u,v],[a(s,r),a(s,u),a(u,r),a(u,s),a(v,u)])
邻接表(adjacency-list)
[n(r,[]),n(s,[r,u]),n(t,[]),n(u,[r,s]),n(v,[u])]
请注意,邻接表(adjacency-list)没有有关它是图还是有向图的信息。
人性化形式
[s > r, t, u > r, s > u, u > s, v > u]
最后,图和有向图可能具有附加到节点和边(弧)的附加信息。对于节点而言,这没有问题,因为我们可以轻松地用任意复合词替换单个字符标识符,例如city(’London’,4711)。另一方面,对于边,我们必须扩展符号。在边上附加了附加信息的图称为标记图。
Arc-clause形式
arc(m,q,7).
arc(p,q,9).
arc(p,m,5).
图项形式
digraph([k,m,p,q],[a(m,p,7),a(p,m,5),a(p,q,9)])
邻接表
[n(k,[]),n(m,[q/7]),n(p,[m/5,q/9]),n(q,[])]
请注意边缘信息的状态与函子“ /”和arity 2以及相应的节点一起打包到一个术语中。
人性化形式
[p>q/9, m>q/7, k, p>m/5]
带标签图的符号也可用于所谓的 多图,其中两个给定节点之间允许有多个边(或弧)。
1、图表示之间的转换
编写谓词以在不同的图表示形式之间进行转换。使用这些谓词,所有表示都是等效的。即对于以下问题,您始终可以自由选择最方便的格式(form)。
我们使用以下符号:
邻接表 adjacency-list (alist): [n(b,[c,g,h]), n(c,[b,d,f,h]), n(d,[c,f]), …]
图项 graph-term (gterm) graph([b,c,d,f,g,h,k],[e(b,c),e(b,g),e(b,h), ...]) 或 digraph([r,s,t,u],[a(r,s),a(r,t),a(s,t), …])
边子句 edge-clause(ecl): edge(b,g). (程序内数据库)
弧子句 arc-clause (acl): arc(r,s). (程序内数据库)
人性化 human-friendly (hf): [a-b,c,g-h,d-e] 或 [a>b,h>g,c,b>a]
主要的转换谓词是:alist_gterm/3和human_gterm/2,这两个谓词(希望)都可以在任意方向上工作,也可以用于图和有向图,无论是否标记。
alist_gterm(Type,AL,GT) :- 在邻接表和图项表示之间进行转换。类型是“图”或“有向图”。
(atom,alist,gterm) (+,+,?) or (?,?,+)
1 alist_gterm(Type,AL,GT):-
2 nonvar(GT),
3 !,
4 gterm_to_alist(GT,Type,AL).
5 alist_gterm(Type,AL,GT):-
6 atom(Type),
7 nonvar(AL),
8 alist_to_gterm(Type,AL,GT).
9
行注释:
1. 原子列表与图项互转
2. 确保GT为非变量
3. 截断
4. 图项转为原子列表
5. 原子列表与图项互转
6. 确保Type为非变量
7. 确保AL为非变量
8. 原子列表转为图项
9. 空行
10 gterm_to_alist(graph(Ns,Es),graph,AL) :-
11 memberchk(e(_,_,_),Es),
12 ! ,
13 lgt_al(Ns,Es,AL).
14 gterm_to_alist(graph(Ns,Es),graph,AL) :-
15 !,
16 gt_al(Ns,Es,AL).
17 gterm_to_alist(digraph(Ns,As),digraph,AL) :-
18 memberchk(a(_,_,_),As), !,
19 ldt_al(Ns,As,AL).
20 gterm_to_alist(digraph(Ns,As),digraph,AL) :-
21 dt_al(Ns,As,AL).
22
行注释:
10. 图项转换为原子列表
11. 内部谓词,检查参数1是否在参数2的列表中
12. 截断
13. 标记图列表转为原子列表
14. 图项转换为原子列表
15. 截断
16. 无标记图列表转为原子列表
17. 有向图项转换为原子列表
18. 内部谓词,检查参数1是否在参数2的列表中
19. 标记有向图列表转为原子列表
20. 有向图项转换为原子列表
21. 无标记有向图列表转为原子列表
22. 空行
23 % 标记图
24 lgt_al([],_,[]).
25 lgt_al([V|Vs],Es,[n(V,L)|Ns]) :-
26 findall(T,((member(e(X,V,I),Es) ;
27 member(e(V,X,I),Es)),T = X/I),L),
28 lgt_al(Vs,Es,Ns).
29
行注释:
24. 递归终止条件
25.
26. findall是内部谓词的参阅高阶谓词 符号‘;’为或运算符
27. member内部谓词,将X/I的值T放入列表L中
28. 继续递归
29. 空行
30 % 无标记图
31 gt_al([],_,[]).
32 gt_al([V|Vs],Es,[n(V,L)|Ns]) :-
33 findall(X,(member(e(X,V),Es) ;
34 member(e(V,X),Es)),L),
35 gt_al(Vs,Es,Ns).
36
行注释:
31. 终止条件
32.
33. findall是内部谓词的参阅高阶谓词 符号‘;’为或运算符
34. member内部谓词,将X的值放入列表L中
35. 继续递归
36. 空行
37 % 标记有向图
38 ldt_al([],_,[]).
39 ldt_al([V|Vs],As,[n(V,L)|Ns]) :-
40 findall(T,(member(a(V,X,I),As), T=X/I),L),
41 ldt_al(Vs,As,Ns).
42
行注释:
38. 终止条件
39.
40. findall是内部谓词的参阅高阶谓词 member内部谓词,将X/I的值T放入列表L中
41. 继续递归
42. 空行
43 % 无标记有向图
44 dt_al([],_,[]).
45 dt_al([V|Vs],As,[n(V,L)|Ns]) :-
46 findall(X,member(a(V,X),As),L),
47 dt_al(Vs,As,Ns).
48
行注释:
43.
44. 终止条件
45.
46. findall是内部谓词的参阅高阶谓词 member内部谓词,将X的值放入列表L中
47. 继续递归
48. 空行
49 alist_to_gterm(graph,AL,graph(Ns,Es)) :-
50 !,
51 al_gt(AL,Ns,EsU,[]),
52 sort(EsU,Es).
53 alist_to_gterm(digraph,AL,digraph(Ns,As)) :-
54 al_dt(AL,Ns,AsU,[]),
55 sort(AsU,As).
56
行注释:
49. 原子列表转换为图项(无向图)
50. 截断
51. 将AL转为Ns和EsU
52. 列表Es是列表EsU排序后的结果
53. 原子列表转换为图项(有向图)
54. 将AL转为Ns和AsU
55. 列表As是列表AsU排序后的结果
56. 空行
57 al_gt([],[],Es,Es).
58 al_gt([n(V,Xs)|Ns],[V|Vs],Es,Acc) :-
59 add_edges(V,Xs,Acc1,Acc),
60 al_gt(Ns,Vs,Es,Acc1).
61
行注释:
57. 终止条件
58. 将参数1转为参数2和参数3
59. 增加边
60. 继续递归
61. 空行
62 add_edges(_,[],Es,Es).
63 add_edges(V,[X/_|Xs],Es,Acc) :-
64 V @> X,
65 !,
66 add_edges(V,Xs,Es,Acc).
67 add_edges(V,[X|Xs],Es,Acc) :-
68 V @> X,
69 !,
70 add_edges(V,Xs,Es,Acc).
71 add_edges(V,[X/I|Xs],Es,Acc) :-
72 V @=< X,
73 !,
74 add_edges(V,Xs,Es,[e(V,X,I)|Acc]).
75 add_edges(V,[X|Xs],Es,Acc) :-
76 V @=< X,
77 add_edges(V,Xs,Es,[e(V,X)|Acc]).
78
行注释:
62. 终止条件
63. 满足第二个参数列表中[X/_|Xs]表首X后有’/‘符号时
64. 项V大于项X
65. 截断
66. 继续递归
67.
68. 项V大于项X
69. 截断
70. 继续递归
71. 满足第二个参数列表中[X/I|Xs]表首X后有’/‘符号时
72. 项V小于等于项X
73. 截断
74. 将项’e(V,X,I)’加入参数4列表的表首后继续递归
75.
76. 项V小于等于项X
77. 将项’e(V,X)’加入参数4列表的表首后继续递归
78. 空行
79 al_dt([],[],As,As).
80 al_dt([n(V,Xs)|Ns],[V|Vs],As,Acc) :-
81 add_arcs(V,Xs,Acc1,Acc),
82 al_dt(Ns,Vs,As,Acc1).
83
行注释:
79. 终止条件
80. 原子列表转为有向图
81. 增加弧
82. 继续递归
83. 空行
84 add_arcs(_,[],As,As).
85 add_arcs(V,[X/I|Xs],As,Acc) :-
86 !,
87 add_arcs(V,Xs,As,[a(V,X,I)|Acc]).
88 add_arcs(V,[X|Xs],As,Acc) :-
89 add_arcs(V,Xs,As,[a(V,X)|Acc]).
90
91 %
92
93 % ecl_to_gterm(GT) :-
94 %从程序数据库中的edge/2事实构造一个图项。
95
行注释:
84. 终止条件
85. 第二参数列表X后有符号’/’时
86. 截断
87. 参数4列表首增加项a(V,X,I)继续递归
88.
89. 参数4列表首增加项a(V,X)继续递归
90~95. 注释
96 ecl_to_gterm(GT) :-
97 findall(E,(edge(X,Y),E=X-Y),Es),
98 human_gterm(Es,GT).
99
100 % acl_to_gterm(GT) :-
101 % 从程序数据库中的arc/2事实构造图项
102
103 acl_to_gterm(GT) :-
104 findall(A,(arc(X,Y),A= >(X,Y)),As),
105 human_gterm(As,GT).
106
107 % ---------------------------------------------------------------------------
108
109 % human_gterm(HF,GT) :- 在人类友好和图项表示之间转换。
110
111 % (list,gterm) (+,?) or (?,+)
112
行注释:
96. 从程序数据库中的edge/2事实构造图项
97. 搜索所有边edge(X,Y)的解集使得E=X-Y,将E的值放入列表Es
98. 将Es转为GT
99. 空行
100~102. 注释
103. 从程序数据库中的arc/2事实构造图项
104. 搜索所有弧arc(X,Y)的解集使得A= >(X-Y),将A的值放入列表As
105. 将As转为GT
106~112. 注释
113 human_gterm(HF,GT):-
114 nonvar(GT),
115 !,
116 gterm_to_human(GT,HF).
117 human_gterm(HF,GT):-
118 nonvar(HF),
119 human_to_gterm(HF,GT).
120
行注释:
113. 从’图项’GT转换为人类友好表示法HF
114. GT不是变量
115. 截断
116. 从’图项’GT转换为人类友好表示法HF
117. 从人类友好表示法HF转成图项表示法GT
118. HF不是变量
119. 从人类友好表示法HF转成图项表示法GT
120. 空行
121 gterm_to_human(graph(Ns,Es),HF) :-
122 memberchk(e(_,_,_),Es),
123 !,
124 lgt_hf(Ns,Es,HF).
125 gterm_to_human(graph(Ns,Es),HF) :-
126 !,
127 gt_hf(Ns,Es,HF).
128 gterm_to_human(digraph(Ns,As),HF) :-
129 memberchk(a(_,_,_),As),
130 !,
131 ldt_hf(Ns,As,HF).
132 gterm_to_human(digraph(Ns,As),HF) :-
133 dt_hf(Ns,As,HF).
行注释:
121. 从’图项’转换为人类友好表示法
122. Es的项是e(_,_,_)
123. 截断
124. 调用标记图项转人类友好
125.
126. 截断
127. 调用无标记图项转人类友好
128. 从有向’图项’转换为人类友好表示法
129. As的项是a(_,_,_)
130. 截断
131. 调用有标记有向图转人类友好
132.
133. 调用无标记有向图转人类友好
134 % 标记图
135 lgt_hf(Ns,[],Ns).
136 lgt_hf(Ns,[e(X,Y,I)|Es],[X-Y/I|Hs]) :-
137 delete(Ns,X,Ns1),
138 delete(Ns1,Y,Ns2),
139 lgt_hf(Ns2,Es,Hs).
140
行注释:
135. 终止条件
136.
137. 删除列表Ns中匹配Y值的元素结果放入Ns1
138. 删除列表Ns1中匹配Y值的元素结果放入Ns2
139. 继续递归
140. 空行
141 % 无标记图
142 gt_hf(Ns,[],Ns).
143 gt_hf(Ns,[e(X,Y)|Es],[X-Y|Hs]) :-
144 delete(Ns,X,Ns1),
145 delete(Ns1,Y,Ns2),
146 gt_hf(Ns2,Es,Hs).
147
行注释:
142. 终止条件
143.
144. 删除列表Ns中匹配Y值的元素结果放入Ns1
145. 删除列表Ns1中匹配Y值的元素结果放入Ns2
146. 继续递归
147. 空行
148 % 标记有向图
149 ldt_hf(Ns,[],Ns).
150 ldt_hf(Ns,[a(X,Y,I)|As],[X>Y/I|Hs]) :-
151 delete(Ns,X,Ns1),
152 delete(Ns1,Y,Ns2),
153 ldt_hf(Ns2,As,Hs).
154
行注释:
149. 终止条件
150.
151. 删除列表Ns中匹配Y值的元素结果放入Ns1
152. 删除列表Ns1中匹配Y值的元素结果放入Ns2
153. 继续递归
154. 空行
155 %无标记有向图
156 dt_hf(Ns,[],Ns).
157 dt_hf(Ns,[a(X,Y)|As],[X>Y|Hs]) :-
158 delete(Ns,X,Ns1),
159 delete(Ns1,Y,Ns2),
160 dt_hf(Ns2,As,Hs).
161
行注释:
156. 终止条件
157.
158. 删除列表Ns中匹配Y值的元素结果放入Ns1
159. 删除列表Ns1中匹配Y值的元素结果放入Ns2
160. 继续递归
161. 空行
162 % 我们猜想如果有一个“>”项,那么它就是一个有向图,否则就是一个图
163 human_to_gterm(HF,digraph(Ns,As)) :-
164 memberchk(_>_,HF), !,
165 hf_dt(HF,Ns1,As1),
166 sort(Ns1,Ns),
167 sort(As1,As).
168 human_to_gterm(HF,graph(Ns,Es)) :-
169 hf_gt(HF,Ns1,Es1),
170 sort(Ns1,Ns),
171 sort(Es1,Es).
172 % 记住:sort/2删除重复项!
173
行注释:
162.
163. 从人类友好表示法转成有向图项表示法
164. 列表HF的元素满足_>_
165. 调用人类友好转有向图项
166. 排序Ns1到结果集Ns
167. 排序Es1到结果集Es
168. 从人类友好表示法转成图项表示法
169. 调用人类友好转图项
170. 排序Ns1到结果集Ns
171. 排序Es1到结果集Es
172. 单行注释
173. 空行
174 hf_gt([],[],[]).
175 hf_gt([X-Y/I|Hs],[X,Y|Ns],[e(U,V,I)|Es]) :-
176 !,
177 sort0([X,Y],[U,V]),
178 hf_gt(Hs,Ns,Es).
179 hf_gt([X-Y|Hs],[X,Y|Ns],[e(U,V)|Es]) :-
180 !,
181 sort0([X,Y],[U,V]),
182 hf_gt(Hs,Ns,Es).
183 hf_gt([H|Hs],[H|Ns],Es) :-
184 hf_gt(Hs,Ns,Es).
185
行注释:
174. 终止条件
175. 人类友好转有标记图项
176. 截断
177. 排序[X,Y],解集为[U,V]
178. 继续递归
179. 人类友好转无标记图项
180. 截断
181. 排序[X,Y],解集为[U,V]
182. 继续递归
183. 人类友好转只有节点没有边
184. 继续递归
185. 空行
186 hf_dt([],[],[]).
187 hf_dt([X>Y/I|Hs],[X,Y|Ns],[a(X,Y,I)|As]) :-
188 !,
189 hf_dt(Hs,Ns,As).
190 hf_dt([X>Y|Hs],[X,Y|Ns],[a(X,Y)|As]) :-
191 !,
192 hf_dt(Hs,Ns,As).
193 hf_dt([H|Hs],[H|Ns],As) :-
194 hf_dt(Hs,Ns,As).
195
行注释:
186. 终止条件
187. 人类友好转有标记有向图
188. 截断
189. 继续递归
190. 人类友好转无标记有向图
191. 截断
192. 继续递归
193. 人类友好转有向图只有节点没有弧
194. 继续递归
195. 空行
196 sort0([X,Y],[X,Y]) :-
197 X @=< Y,
198 !.
199 sort0([X,Y],[Y,X]) :-
200 X @> Y.
201
行注释:
196. 排序
197. X小于等于Y,参数1中列表的顺序不变匹配参数2
198. 截断
199. 将参数1中列表的顺序交换
200. X大于Y
201. 空行
202 % tests ------------------------------------------------------------------
203
204 testdata([b-c,f-c,g-h,d,f-b,k-f,h-g]).
205 testdata([s>r,t,u>r,s>u,u>s,v>u]).
206 testdata([b-c/5,f-c/9,g-h/12,d,f-b/13,k-f/3,h-g/7]).
207 testdata([p>q/9,m>q/7,k,p>m/5]).
208 testdata([a,b(4711),c]).
209 testdata([a-b]).
210 testdata([]).
211
212 test :-
213 testdata(H1),
214 write(H1), nl,
215 human_gterm(H1,G1),
216 alist_gterm(Type,AL,G1),
217 alist_gterm(Type,AL,G2),
218 human_gterm(H2,G2),
219 human_gterm(H2,G1),
220 write(G1), nl, nl,
221 fail.
222 test.
行注释:
212.
213. 初始化测试用数据
214. 打印输出测试数据H1
215. 从人类友好表示法H1转成图项表示法G1
216. 从图项表示法G1转换和识别为原子列表和类型
217. 从原子列表和类型转换为’图项’(两个方向都可以互转)
218. 从’图项’转换为人类友好表示法H2
219. 从人类友好表示法H2转换为’图项’(两个方向都可以互转)
220. 输出’图项’
221. 强制失败回溯
222.
?- test.
[b-c,f-c,g-h,d,f-b,k-f,h-g]
graph([b,c,d,f,g,h,k],[e(b,c),e(b,f),e(c,f),e(f,k),e(g,h)])
graph([b,c,d,f,g,h,k],[e(b,c),e(b,f),e(c,f),e(f,k),e(g,h)])
[s>r,t,u>r,s>u,u>s,v>u]
digraph([r,s,t,u,v],[a(s,r),a(s,u),a(u,r),a(u,s),a(v,u)])
digraph([r,s,t,u,v],[a(s,r),a(s,u),a(u,r),a(u,s),a(v,u)])
[b-c/5,f-c/9,g-h/12,d,f-b/13,k-f/3,h-g/7]
graph([b,c,d,f,g,h,k],[e(b,c,5),e(b,f,13),e(c,f,9),e(f,k,3),e(g,h,7),e(g,h,12)])
graph([b,c,d,f,g,h,k],[e(b,c,5),e(b,f,13),e(c,f,9),e(f,k,3),e(g,h,7),e(g,h,12)])
[p>q/9,m>q/7,k,p>m/5]
digraph([k,m,p,q],[a(m,q,7),a(p,m,5),a(p,q,9)])
digraph([k,m,p,q],[a(m,q,7),a(p,m,5),a(p,q,9)])
[a,b(4711),c]
graph([a,c,b(4711)],[])
graph([a,c,b(4711)],[])
[a-b]
graph([a,b],[e(a,b)])
graph([a,b],[e(a,b)])
[]
graph([],[])
graph([],[])
true.
?-