将DAG签约到新DAG中的最小尺寸


15

我们有一个DAG。我们在节点上有一个函数(松散地说,我们为节点编号)。我们想使用这些规则创建一个新的有向图:F:VN

  1. 只有具有相同编号的节点才能签到相同的新节点。。(但是,。)F(x)F(y)xyxyF(x)F(y)
  2. 我们在新节点之间添加所有旧边:(x,y)Exy(x,y)E
  3. 此新图仍然是DAG。

| V'|的最小值是多少?|V|?创建最小新图的算法是什么?


1
因此,决策问题似乎是:给定一个顶点颜色的DAG和一个整数k,确定是否存在一个DAG,该DAG最多具有k个顶点,这些顶点是通过收缩具有相同颜色的顶点形成的。
安德拉斯·萨拉蒙

1
如果收缩两个连接的节点,是否会出现禁止的自环?
Yuval Filmus 2013年

1
不。再次阅读2 .:仅在收缩后的两个节点仍然不同时才添加边。如果将两个节点压缩为一个,则不添加边。
chx

1
@chx您要“最小”还是“最小”?
Realz Slaw

1
你能给点动力/体重吗?
vzn13年

Answers:


5

解决此问题的一种方法是使用整数线性规划(ILP)。让我们解决问题的决策版本:给定,有没有办法收缩同色顶点以获得大小的DAG ?kk

可以使用标准技术将其表示为ILP实例。我们在原始图形中获得了每个顶点的颜色。我建议我们用的标签来标记每个顶点;具有相同标签和相同颜色的所有顶点将被收缩。因此,决策问题就变成了:是否存在标签,从而使所有相同颜色的相同标签顶点收缩会产生DAG?{1,2,,k}

要将其表示为整数线性程序,为每个顶点引入一个整数变量,以表示顶点上的标签。添加不等式。vvv1vk

下一步是表达要求收缩图必须是DAG的要求。请注意,如果存在上面列出的形式的标签,而又不失一般性,则存在这样的标签,其中标签在收缩图上引起拓扑排序(即,如果在收缩图中位于之前,则的标签小于的标签)。因此,对于原始图形中的每个边,我们将添加以下约束:和具有相同的标签和相同的颜色,否则的标签小于的标签。具体来说,对于每个边vwvwvwvwvwvw在具有相同颜色的初始图中,添加不等式。对于每个边缘,其中具有不同的颜色,添加不等式。v,wvwvwv,wv<w

现在看看该整数线性程序是否有可行的解决方案。当且仅当标签具有所需的形式(即,收缩所有相同颜色的相同标签的顶点会产生DAG)时,才会有可行的解决方案。换句话说,当且仅当有一种方法可以将原始图压缩为大小为的DAG时,才会有可行的解决方案。我们可以使用任何整数线性规划求解器;如果ILP求解器给了我们答案,那么我们就可以解决原始决策问题。k

当然,这不能保证在多项式时间内完成。没有保证。但是,ILP求解器已经相当不错。我希望对于一个合理大小的图,您有很大的机会使ILP求解程序能够在合理的时间内解决此问题。

也可以将其编码为SAT实例并使用SAT解算器。我不知道这是否会更有效。不过,ILP版本可能更容易考虑。

(我希望这是正确的。我没有仔细检查每一个细节,所以请仔细检查我的推理!我希望我没有在任何地方出现问题。)


更新(10/21):通过以拓扑排序的顺序处理DAG并跟踪每个顶点的标签下限,看起来这种形式的ILP可以在线性时间内解决。这使我怀疑自己的解决方案:我在某处犯了错误吗?


感谢您的详细回答!我得到了限制,而且看起来确实合理。但是,尽管我不太熟悉ILP,但我认为整数线性编程需要一个您想要最大化(或最小化)的函数,而我在任何地方都看不到。我仅在Wikipedia中进行了检查,所以我可能是错的。
chx

@chx,我正在使用ILP来测试约束的可行性。这可以通过要求ILP求解器最大化您喜欢的任何目标函数(例如,最大化0),然后忽略目标函数的值而仅查看ILP是否可行来完成。无论是ILP求解器响应“不可行”(这意味着没有收缩规模的DAG )或它响应“可行”,并提供了客观的功能是能找到的最好的价值; 在这种情况下,你忽略了目标函数的值(你知道,确实存在着规模的DAG ķ)。kk
DW

参见,例如engineering.purdue.edu/~engelb/abe565/…(“我只是想知道是否存在可行的解决方案。”)
DW

关于您的线性时间解决方案;我尚未消化您的ILP公式,因此无法判断,但我可以肯定地说,我可以证明问题是NP困难的,这将使线性时间解很方便:P。我要尽快发布。
Realz Slaw

@RealzSlaw,谢谢!在那种情况下,我强烈怀疑我可能在某个地方犯了错误(尽管我不确定现在在哪里)。
DW

5

注意:AFAICT,DW在此减少中发现了一个漏洞,这是错误的(请参阅注释)。由于历史原因,将其保留在此处。

简介:首先,我将把单调3SAT问题简化为我们的问题。尽管单调3SAT问题可以轻松解决,但我们的问题可以进一步解决NP难的最小纯单调3SAT问题;因此,这个问题很难解决。

单调3SAT减少到我们的问题

我们有一个单调布尔表达式,表示为一系列变量和一系列子句。CNF的形式为,使得:Φ=(V,C)

(ciC) ci=(xjxkxl)||(xj,xk,xlV)

i=1nci|ciC,n=|C|.

转换次数

我们构造一个图G '中的每个顶点都有一个标签;具有相同标签的顶点可以收缩。G=V,EG

首先,我们构建了曲线图,如下所示:每个,我们提出两个节点,每个标记的X ,以及从一个到另一个的有向边(点击用于高分辨率视图图像)。xiVxi

在此处输入图片说明

这些节点当然可以收缩,因为它们具有相同的标签。我们将考虑将收缩的变量/节点的值为false,将未收缩的变量/节点的值为true

在此处输入图片说明

这一步后,应该包含2 &CenterDot;&| V | 节点。接下来,我们介绍子句约束。对于每个子句,Ç ÇÇ = X ĴX ķX | X ĴX ķX V,我们引入一个节点Ç ,和以下边:V2|V|ciC, ci=(xjxkxl)|xj,xk,xlVci

在此处输入图片说明

ci1ci

2|V|+|C|

xixj xkcici

这是另一个可视化,展开了子句约束:

在此处输入图片说明

因此,每个子句约束要求它包含的变量中的至少一个保持不变。由于未签约的节点的值为true,因此这要求变量之一为true。正是Monotone SAT的条款所需要的。

从最小真实单调3SAT降低

单调3SAT可以轻松满足;您只需将所有变量设置为true。

但是,由于我们的DAG最小化问题是找到最多的收缩,因此这转化为找到在CNF中产生最多错误变量的令人满意的赋值。这与找到最小的真实变量相同。问题有时称为最小真实单调3SAT此处(作为优化问题或决策问题),或称为k-True单调2SAT(作为较弱的决策问题);都是NP难题。因此,我们的问题是NP难题。


参考文献:

图表来源:


1
哇。那么DW的解决方案一定是错误的(或者我们已经证明了NP = P,我对此至少有一点怀疑:P)-但是在哪里?
chx

(x1x2x6)(x1x4x5)(x3x4x6)x1=x4=x6=False x2=x3=x5=Truec1x1x4x6c1

@DW也很高兴再次与您交谈:D,祝您好运,如果我们俩都对,我们的答案可能是P = NP!/ jk
Realz Slaw 2015年

(x1,x3)

@RealzSlaw,恐怕我还不了解...我看不出必须转换我的公式的任何原因。我相信它已经 Minimum True Monotone 3SAT的一个实例。但是,让我将其提升一个层次。从更广泛的意义上讲,我看到了一个提议的减少量,但是我没有看到任何关于减少量正确的论点-缺少了。为了使还原正确,它必须将YES实例映射到YES实例,将NO实例映射到NO实例。我怀疑如果您尝试写出减少量正确性的证明,那么在考虑我给出的公式时会遇到问题。
DW

1

每次替换(亲子直接替换除外)时,您都添加了新的祖先后代关系,从而可以轻松地确定哪一个长期值得。因此,一般情况下,简单的贪婪算法将失败。但是,如果您采用蛮力方法,则可以确定最小的图形:

python风格(未经测试):

def play((V,E),F,sequence=[]):
  """
  (V,E) -- a dag.
  V     -- a set of vertices.
  E     -- a set of directed-edge-tuples.
  F     -- a function that takes a vertex, returns an integer.
  sequence -- the sequence of moved taken so far; starts with/defaults to
              an empty list, will contain tuples of the form (x,y)
              where x is removed and replaced with y.

  Returns the best recursively found solution.
  """

  #find all the integer values in the graph, remember which
  # values correspond to what vertices. Of the form {integer => {vertices}}.
  n2v = {}
  for x in V:
    n = F(x)

    #for each integer, make sure you have a set to put the vertices in.
    if n not in n2v:
      n2v[n] = set()

    #for each integer, add the vertex to the equivalent set.
    n2v[n].add(v)

  #record the best sequence/solution. You start with the current sequence,
  # and see if you can obtain anything better.
  best_solution = list(sequence)

  #Now you will try to combine a single pair of vertices, obtain a new
  # graph and then recursively play the game again from that graph. 

  #for each integer and equivalent set of vertices,
  for n,vset in n2v.iteritems():

    #pick a pair of vertices
    for x in vset:
      for y in vset:

        #no point if they are the same.
        if x == y:
          continue

        #If there is a path from x => y or y => x, then you will be
        # introducing a cycle, breaking a rule. So in that case, disregard
        # this pair.
        #However, the exception is when one is a direct child of the other;
        # in that case you can safely combine the vertices.
        if pathtest((V,E),x,y) and (x,y) not in E and (x,y) not in E:
          continue

        #combine the vertices (function is defined below), discard x,
        # replace it with y, obtain the new graph, (V',E').
        Vp,Ep = combine_vertex((V,E),x,y))

        #record the sequence for this move.
        sequencep = list(sequence) + [(x,y)]

        #recurse and play the game from this new graph.
        solution = play(Vp,Ep,F,sequencep)

        #if the returned solution is better than the current best,
        if len(solution) > len(best_solution):
          #record the new best solution
          best_solution = solution
  #return the best recorded solution
  return best_solution


def combine_vertex((V0,E0),x,y):
  """
  (V0,E0)   -- an initial digraph.
  V0        -- a set of vertices.
  E0        -- a set of directed-edge-tuples.
  x         -- vertex to discard.
  y         -- vertex to replace it with.

  returns a new digraph replacing all relationships to and from x to relate
   to y instead, and removing x from the graph entirely.
  """

  #the final vertex set will have everything except x
  V = set(V0)
  V.discard(x)

  #now you construct the edge set.
  E = set()

  #for every edge,
  for (u0,v0) in E0:
    #recreate the edge in the new graph, but replace any occurence
    # of x.  
    u,v = u0,v0
    #if x is in the edge: replace it
    if u == x:
      u = y
    if v == x:
      v == y

    #sometimes u=v=y and can now be pointing to itself, don't add that
    # edge
    if u == v:
      continue

    #add the new/replaced edge into the edge-set.
    E.add( (u,v) )
  return (V,E)

我不确定这是否真的是一个难题,但是手动操作一些图形似乎很容易组合。我很好奇是否可以将一些困难减少到此问题,或者是否有一种算法可以缩短运行时间。


1
我也很好奇:)
chx
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.