如何使用答案集编程从图中提取树?

问题描述

一个无向图(V,E),边上的权重w:E→N,a 目标k∈N,阈值O∈N。找到该k个顶点的树 图的重量小于阈值。换句话说,选择k V和E的顶点和k-1边分别使它们 构成一棵树,并且所选边的权重之和 小于O。

编写一个以V,E,w,k和O为输入的ASP程序,并找到 选择满足约束的边或输出 如果无法满足约束条件,则为“无法满足”。选择 边隐式地诱发了顶点的选择,因此没有 需要明确显示选定的顶点。

谓词vertex / 1为该问题提供了一个实例, 权重/ 3,目标/ 1和阈值/ 1。所有边缘都有权重,所以 形式为weight(a,b,10)的语句。可以用来声明 在顶点a和b之间同时存在一条边 声明其重量,并且不需要任何多余的边/ 2 谓词。

我尝试了以下操作:

% instance
vertex ( v1 ). vertex ( v2 ). vertex ( v3 ). 
vertex ( v4 ). vertex ( v5 ). vertex ( v6 ). 
vertex ( v7 ). vertex ( v8 ). vertex ( v9 ).
weight ( v1,v2,3). weight ( v1,v3,3). 
weight ( v2,v4,1). weight ( v2,v5,5). 
weight ( v3,3). weight ( v3,v6,4). 
weight ( v4,4). weight ( v4,v7,1). 
weight ( v5,7). 
weight ( v6,2). weight ( v6,v8,2). 
weight ( v7,v9,3). 
weight ( v8,2).
target (4).
threshold (4).

% encoding
(P-1) {select(X,Y) : weight(X,Y,Z)} (Q-1) :- target(P),target(Q).
sum(S) :- S = #sum {W,X,Y : select(X,Y),weight(X,W); W,Z : select(X,Z),Z,W) }.
:- sum(S),threshold(M),S > M.
:- select(A,B),select(C,D),A == C ; A == D ; B == C ; B == D. 

#show select/2.

我得到以下输出

clingo version 5.5.0
Reading from stdin
Solving...
Answer: 1
select(v2,v4) select(v4,v7) select(v6,v7)
Answer: 2
select(v2,v8)
Answer: 3
select(v2,v7) select(v8,v9)
SATISFIABLE

Models       : 3
Calls        : 1
Time         : 0.013s (Solving: 0.00s 1st Model: 0.00s Unsat: 0.00s)
cpu Time     : 0.000s

我只是期待

select(v2,v7)

因为其他人显然不是发束人。

我认为这是因为问题所在:

:- select(A,A == C ; A == D ; B == C ; B == D.

我该如何纠正?

解决方法

好的,那很复杂。我很确定我的解决方案并不完美,我也是一个初学者。

在开始编写代码之前,让我们再次检查问题:要求是选择k顶点和k-1边。如果稍微考虑一下,它可以恰好形成两种模式:一棵相连的树或多个至少有一个周期的非相连图。因此,如果您确定没有循环,您会得到一棵相连的树。

我在事实上添加了一些顶点,以检查是否形成了树或是否找到了便宜的未连接循环,为此,我不得不将targetthreshold更改为更高的值。 / p>

enter image description here1

#const n = 5.

vertex ( v1; v2; v3; v4; v5; v6; v7; v8; v9 ).
vertex ( m1; m2; m3 ). 
weight ( v1,v2,3). weight ( v1,v3,3). 
weight ( v2,v4,1). weight ( v2,v5,5). 
weight ( v3,3). weight ( v3,v6,4). 
weight ( v4,4). weight ( v4,v7,1). 
weight ( v5,7). 
weight ( v6,2). weight ( v6,v8,2). 
weight ( v7,v9,3). 
weight ( v8,2).
weight ( m1,m2,0).
weight ( m2,m3,0).
weight ( m3,m1,0).
target (n).
threshold (6).

现在是代码,后面有解释。

% select subset of nodes and vertices
(P) {select(X) : vertex(X)} (P) :- target(P).
(P-1) {select(X,Y) : weight(X,Y,Z)} (Q-1) :- target(P),target(Q).
     
% postion does not matter in an undirected graph.
directed(A,B):-select(A,B).
directed(B,A):-select(A,B).

% for every selected edge all nodes are selected
:- directed(A,_),vertex(A),not select(A).

% for every selected node there exists at least one edge
:- select(A),{directed(A,B):vertex(B)}0.

% select a direction for each selected edge
{dir(A,B);dir(B,A)}==1 :- select(A,B). 

% force them in an order
{ found(X,1..n) } == 1 :- select(X).
{ found(X,N):select(X) } == 1 :- N = 1..n.
% reject if one edge does not follow the order 
:- found(X,NX),found(Y,NY),dir(X,Y),NY<NX.
% reject if 2 different edges end in the same vertex 
:- dir(X,Z),dir(Y,X!=Y.

sum(S) :- S = #sum {W,X,Y : select(X,weight(X,W); W,Z : select(X,Z,W) }.
:- sum(S),threshold(M),S > M.

#show select/2.

说明:

  • 为了方便我,我在select/1谓词中添加了选定的顶点。
  • 由于处理无向图总是必须检查两个位置,因此我添加了directed/2谓词,这是所选边的有向图版本。
  • 接下来,我确保每个选定的顶点都有选定的边,反之亦然。
  • 现在出现了复杂的部分:检测周期。为此,我使用谓词dir/2在每个选定的边沿的两个方向之一上施压。在有向图中测试树比较容易。
  • 接下来,我向顶点下订单found/2。有向边dir/2仅按照该顺序排列。这会迫使周期变为某种行为。
  • 现在是循环破坏者:如果所选图形具有循环,则dir/2的两条边将在同一顶点结束。拒绝。如果这只是clingo的不幸猜测,那么它将发现一个更幸运的猜测,可以满足这个标准。
  • 总和的计算是从您那里复制并粘贴的。

输出为16倍

select(v2,v4) select(v4,v7) select(v6,v8)

重复项来自found/2中顶点的顺序可以不同但仍得到相同结果的事实。