计算树宽


14

无向图的树宽是图论中一个非常重要的概念。发明了许多图算法,如果您分解出具有小树宽的图,则该算法运行很快。

树宽通常根据树分解来定义。这是一个图以及该图的树分解,由维基百科提供:

在此处输入图片说明

树分解是一棵树,其中每个顶点与原始图的顶点子集相关联,具有以下属性:

  • 原始图中的每个顶点都在至少一个子集中。
  • 原始图中的每个边都在至少一个子集中拥有两个顶点。
  • 分解中所有子集包含给定原始顶点的顶点都已连接。

您可以检查上述分解是否遵循这些规则。树分解的宽度是其最大子集的大小减去一。因此,以上分解为二。图的树宽是该图的任何树分解的最小宽度。


在此挑战中,将为您提供一个连接的,无向的图,并且您必须找到其树宽。

虽然很难找到树分解,但还有其他方法可以计算树宽。Wikipedia页面上有更多信息,但是此处未提及的计算树宽的一种方法(最小消除顺序宽度)是算法中常用的一种计算树宽的方法。有关此事实的论文,请参见此处

在消除顺序中,一次消除一个图形的所有顶点。消除每个顶点后,将添加边,以将该顶点的所有邻居彼此连接。重复此操作,直到所有顶点消失。消除顺序宽度是此过程中要消除的任何顶点具有的最大邻居数。树形宽度等于消除顺序宽度的所有顺序中的最小值。这是一个使用此事实来计算树宽的示例程序:

import itertools
def elimination_width(graph):
    max_neighbors = 0
    for i in sorted(set(itertools.chain.from_iterable(graph))):
        neighbors = set([a for (a, b) in graph if b == i] + [b for (a, b) in graph if a == i])
        max_neighbors = max(len(neighbors), max_neighbors)
        graph = [edge for edge in graph if i not in edge] + [(a, b) for a in neighbors for b in neighbors if a < b]
    return max_neighbors

def treewidth(graph):
    vertices = list(set(itertools.chain.from_iterable(graph)))
    min_width = len(vertices)
    for permutation in itertools.permutations(vertices):
        new_graph = [(permutation[vertices.index(a)], permutation[vertices.index(b)]) for (a, b) in graph]
        min_width = min(elimination_width(new_graph), min_width)
    return min_width

if __name__ == '__main__':
    graph = [('a', 'b'), ('a', 'c'), ('b', 'c'), ('b', 'e'), ('b', 'f'), ('b', 'g'),
            ('c', 'd'), ('c', 'e'), ('d', 'e'), ('e', 'g'), ('e', 'h'), ('f', 'g'), ('g', 'h')]
    print(treewidth(graph))

例子:

[(0, 1), (0, 2), (0, 3), (2, 4), (3, 5)]
1

[(0, 1), (0, 2), (1, 2), (1, 4), (1, 5), (1, 6), (2, 3), (2, 4), (3, 4), (4, 6), (4, 7), (5, 6), (6, 7)]
2

[(0, 1), (0, 3), (1, 2), (1, 4), (2, 5), (3, 4), (3, 6), (4, 5), (4, 7), (5, 8), (6, 7), (7, 8)]
3

[(0, 1), (0, 2), (0, 3), (0, 4), (1, 2), (1, 3), (1, 4), (2, 3), (2, 4), (3, 4)]
4

您将收到图形作为输入,并且必须返回树宽作为输出。输入格式灵活。您可以将边列表,邻接图或邻接矩阵作为输入。如果您想使用其他输入格式,请在评论中提问。您可以假设输入已连接,并且可以将该假设构建为输入格式,例如通过使用边列表。

编辑:不允许使用计算树宽的内置操作。对于未预先指定此内容,我深表歉意。

最短的代码获胜。


由于图形在形式上是一个元组(V,E),这将是有效的输入吗?
ბიმო

@Bruce_Forte绝对。
isaacg

Answers:


7

八度,195字节

function x=F(a)r=rows(a);P=perms(s=1:r);x=r;for m=s;b=a;n=0;for z=P(m,:);(T=sum(b)(z))&&{b|=(k=accumarray(nchoosek(find(b(z,:)),2),1,[r r]))|k';n=max(T,n);b(z,:)=0;b(:,z)=0}{4};end;x=min(x,n);end

以邻接矩阵为输入的函数。它消耗大量内存,因此如果顶点数超过10-12,将无用。

  • 不需要,endfunction但是应该将其添加到tio。

在线尝试!

取消高尔夫:

function min_width = treewidth(graph_adj)
    Nvertices = rows(graph_adj)
    Permutations = perms(1:Nvertices);                                                            % do not try it for large number of vertices
    min_width = Nvertices;
    for v = 1:Nvertices;
        new_graph=graph_adj;
        max_neighbors=0;
        for p = Permutations(v,:)
            Nneighbors=sum(new_graph)(p);
            if(Nneighbors)>0
                connection=accumarray(nchoosek(find(new_graph(p,:)),2),1,[Nvertices Nvertices]);  % connect all neighbors
                new_graph|=connection|connection';                                                % make the adjacency matrix symmetric
                new_graph(p,:)=0;new_graph(:,p)=0;                                                % eliminate the vertex
                max_neighbors=max(Nneighbors,max_neighbors);
            end
        end
        min_width=min(min_width,max_neighbors);
    end
end

5

SageMath,29字节无竞争*

lambda L:Graph(L).treewidth()

*此答案在OP更改“禁止构建”的问题之前发布,因此我使其成为非竞争性的。

在线演示!


1
仔。太没意思了。不幸的是,我将不得不禁止内置插件,对此感到抱歉。
isaacg

@isaacg没问题。我手里还有另一件事 :)
rahnema1'7

@isaacg这个答案不违反标准漏洞吗?
PyRulez

rahnema1,请参阅我的编辑。禁止内置函数,因此不允许此答案。请删除它或将其标记
为非

@isaacg谢谢,我将其标记为不竞争。
rahnema17年

5

的Haskell(Lambdabot),329个 321 245字节

这是我的解决方案,这要归功于输入的灵活性,它可用于包含任何类型为的实例的图形的图形Eq

(&)=elem
l=length
t n g s=last$minimum[max(t n g b)$t(n++b)g$s\\b|b<-filterM(\_->[0>1,1>0])s,l b==div(l s)2]:[l[d|d<-fst g,not$d&n,d/=s!!0,(d&)$foldr(\x y->last$y:[x++y|any(&y)x])[s!!0]$join(>>)[e|e<-snd g,all(&(s!!0:d:n))e]]|1==l s]
w=t[]<*>fst

在线尝试!

非高尔夫版本:

type Vertex a = a
type Edge a   = [Vertex a]
type Graph a  = ([Vertex a],[Edge a])

vertices = fst
edges = snd

-- This corresponds to the function w
treeWidth :: (Eq a) => Graph a -> Int
treeWidth g = recTreeWidth g [] (vertices g)

-- This is the base case (length s == 1) of t
recTreeWidth graph left [v] =
    length [ w | w <- vertices graph
               , w `notElem` left
               , w /= v
               , w `elem` reachable (subGraph w)
           ]

  where subGraph w = [ e | e <- edges graph, all (`elem` v:w:left) e ]

        reachable g = foldr accumulateReachable [v] (g>>g)
        accumulateReachable x y = if any (`elem` y) x
                                  then x++y
                                  else y

-- This is the other case of t
recTreeWidth graph left sub =
  minimum [ comp sub' | sub' <- filterM (const [False,True]) sub
                      , length sub' == div (length sub) 2
          ]

  where comp b = max (recTreeWidth graph left b)
                     (recTreeWidth graph (left++b) (sub\\b))
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.