python 中有向图的单调着色

Monotonic coloring of the directed graph in python

一直在研究有向图单调着色算法的实现。这意味着以下规范:颜色数应最少,边开始的节点的颜色数必须小于边结束的节点的颜色数(例如:1 -> 3, 3 - > 10).如果有帮助,数据中没有循环。作为输入,我们有两个字典:{node number: {nodes that connect to it}, ... } 和 {node number: {nodes that it connects to}, ...}。

程序的结果是所需的最少颜色数。

我的代码似乎可以正常工作,但我不知道它是否真的找到了最小值,而不是任何合适的数字。这是:

import numpy as np

def check(cols, curr):
  # this is basically the same thing as in the main function, but recursive.
  if curr in new:
    for i in new[curr]:
      if cols[i] >= cols[curr]:
        cols[i] = cols[curr] - 1
        cols = check(cols, i)

  if curr in new1:
    for i in new1[curr]:
      if cols[i] <= cols[curr]:
        cols[i] = cols[curr] + 1
        cols = check(cols, i)

  return cols

def get_ans():
  cols = [-1 for i in range(ndots)] 

  for g in range(ndots):
    print(g)
    print(cols)

    
    if g in new:
      to = [cols[_] for _ in new[g]] # color of nodes connected to this one
      toi = list(new[g])             # index of nodes connected to this one
    else: 
      to = set()
      toi = set()
    if g in new1:
      fr = [cols[_] for _ in new1[g]] # color of nodes this one is connected to
      fri = list(new1[g])             # index of nodes which this one connects to
    else: 
      fr = set()
      fri = set()
    print('near', toi, fri)

    for i in range(len(fr)):
      if cols[g] == -1:
        cols[g] = ndots
        cols[fri[i]] = ndots + 1
      elif cols[fri[i]] <= cols[g]: # if the coloring inconsistency is found
        cols[fri[i]] = cols[g] + 1
        cols = check(cols, fri[i])  # check if anything has to be changed

    for i in range(len(to)):
      if cols[g] == -1:
        cols[g] = ndots
        cols[toi[i]] = ndots - 1
      elif cols[toi[i]] >= cols[g]: # if the coloring inconsistency is found
        cols[toi[i]] = cols[g] - 1
        cols = check(cols, toi[i]) # check if anything has to be changed

    # print(cols)

  return len(np.unique(cols))


ndots = 8 # number nodes
new = {0: {1, 2}, 3: {1}, 5: {1, 4, 7}, 6: {2, 4}, 4: {0}, 2: {3}, 7: {3, 6}}  
new1 = new 
new = {}
for i in new1:
  for j in new1[i]:
    if j not in new: new[j] = set()
    new[j].add(i)

temp = new
new = new1
new1 = temp

print(new)  # first dictionary
print(new1) # second dictionary

print(get_ans())

对上面的混乱表示抱歉:)。如果您对如何执行此操作有更好的想法(我想要的主要是知道它将是最小值),请帮助我,我们将不胜感激。

您的输入是有向无环图,因此您的节点中有一个topological order。如果按此顺序处理节点,则可以保证在处理 N 本身之前始终处理节点 N 的所有祖先。

因此,只需按此顺序遍历它们并分配

color[node] = max(color[ancestor] for ancestor in ancestors[node]) + 1
              or 0 if no ancestor exists

为您提供符合您要求的颜色。

它将为 DAG 的根赋予颜色 0。对于所有其他节点,这会将颜色设置为该节点可能具有的最低合法值。由于我们按拓扑顺序处理节点,因此我们可以确定所有祖先总是已经设置了正确的值。

注:这个算法思路通常用于求longest path到你DAG中每个节点的长度。这意味着你的问题和求最长路径的长度是一样的,你需要的颜色数总是和这个长度一样。

def monotonic_coloring(adjacencies):
    inverse_adjacencies = [set() for _ in range(len(adjacencies))]
    for node, adjacent_nodes in enumerate(adjacencies):
        for child in adjacent_nodes:
            inverse_adjacencies[child].add(node)

    unprocessed_incoming_edge_count = [
        len(inverse_adjacencies[node]) for node in range(len(adjacencies))
    ]
    without_unprocessed_incoming_edge = {
        node
        for node in range(len(adjacencies))
        if unprocessed_incoming_edge_count[node] == 0
    }
    colors = [0 for _ in range(len(adjacencies))]

    while len(without_unprocessed_incoming_edge) != 0:
        node = without_unprocessed_incoming_edge.pop()
        colors[node] = max(
            (colors[ancestor] + 1 for ancestor in inverse_adjacencies[node]),
            default=0
        )
        for child in adjacencies[node]:
            unprocessed_incoming_edge_count[child] -= 1
            if unprocessed_incoming_edge_count[child] == 0:
                without_unprocessed_incoming_edge.add(child)

    return colors

coloring1 = monotonic_coloring([
    {1, 2}, set(), {3}, {1}, {0}, {1, 4, 7}, {2, 4}, {3, 6},
])
coloring2 = monotonic_coloring([
    {1}, {3}, {3}, {4, 5}, {6}, {6}, set(), {6}, {9}, set(),
])

print(coloring1)
print(coloring2)

import numpy as np
print(len(np.unique(coloring2)))

如果我用第二张图调用你的代码,我得到输出 6,其中只需要 5 种不同的颜色。似乎主要问题是图中断开连接的部分(节点 8 和 9)。您可以使用以下输入重现此内容:

new = {0: {1}, 1: {3}, 2: {3}, 3: {4, 5}, 4: {6}, 5: {6}, 6: set(), 7: {6}, 8: {9}, 9: set()}
ndots = len(new)

通过图形传播变化的递归方法可以工作,但它的时间复杂度会很差 运行 因为对于错误的输入,您将不得不再次通过图形的同一部分传播大量变化又一次。