【问题标题】:Implement Kahn's topological sorting algorithm using Python使用 Python 实现卡恩的拓扑排序算法
【发布时间】:2017-09-27 13:17:51
【问题描述】:

卡恩在62向topologically sort任意DAG(有向无环图)提出算法,伪代码抄自维基百科:

L ← Empty list that will contain the sorted elements 
S ← Set of all nodes with no incoming edges 
while S is non-empty do
    remove a node n from S
    add n to tail of L
    for each node m with an edge e from n to m do
        remove edge e from the graph  # This is a DESTRUCTIVE step!
        if m has no other incoming edges then
            insert m into S if graph has edges then
    return error (graph has at least one cycle) else 
    return L (a topologically sorted order)

我需要使用 IPython3 来实现它,并使用以下 DAG 实现:

class Node(object):
    def __init__(self, name, parents):
        assert isinstance(name, str)
        assert all(isinstance(_, RandomVariable) for _ in parents)
        self.name, self.parents = name, parents

其中name 是节点的标签,parents 存储其所有父节点。那么DAG类实现为:

class DAG(object):
    def __init__(self, *nodes):
        assert all(isinstance(_, Node) for _ in nodes)
        self.nodes = nodes

(DAG 实现是固定的,不会改进。) 然后我需要将 Kahn 算法实现为函数 top_order,它接受 DAG 实例并返回类似 (node_1, node_2, ..., node_n) 的排序.主要问题是,该算法具有破坏性,因为它的一个步骤是remove edge e from the graph(第 5 行),它将删除m.parents 的一个成员。但是,我必须保持 DAG 实例完好无损

目前我能想到的一种方法是创建一个副本的 DAG 实例(即使是浅副本也无法完成这项工作,因为算法仍然会通过以下方式破坏原始实例引用),并对这个副本执行破坏性算法,然后得到这个副本的节点名称的正确排序(假设节点之间没有命名冲突),然后使用这个名称排序来推断节点的正确排序原始实例,大致如下:

def top_order(network):
    '''takes in a DAG, prints and returns a topological ordering.'''
    assert type(network) == DAG
    temp = copy.deepcopy(network) # to leave the original instance intact

    ordering_name = []
    roots = [node for node in temp.nodes if not node.parents]
    while roots:
        n_node = roots[0]
        del roots[0]
        ordering_name.append(n_node.name)
        for m_node in temp.nodes:
            if n_node in m_node.parents:
                temp_list = list(m_node.parents)
                temp_list.remove(n_node)
                m_node.parents = tuple(temp_list)
                if not m_node.parents:
                    roots.append(m_node)

    print(ordering_name) # print ordering by name

    # gets ordering of nodes of the original instance
    ordering = []
    for name in ordering_name:
        for node in network.nodes:
            if node.name == name:
                ordering.append(node)

    return tuple(ordering)

两个问题:第一,当network很大时,深拷贝会消耗资源;其次,我想改进我的嵌套for 循环,它可以获取原始实例的顺序。 (第二次我想到了类似sorted 方法之类的东西。)

有什么建议吗?

【问题讨论】:

    标签: python-3.x class graph immutability deep-copy


    【解决方案1】:

    我将建议算法的字面意义不大的实现:您根本不需要操作 DAG,您只需要操作 关于 DAG 的信息。算法需要的唯一“有趣”的东西是从节点到其子节点的映射(与 DAG 实际存储的相反),以及每个节点父节点的数量。

    这些很容易计算,并且可以使用 dicts 将此信息与节点名称相关联(假设所有名称都是不同的 - 如果不是,您可以使用更多代码发明唯一名称)。

    那么这应该可以工作:

    def topsort(dag):
        name2node = {node.name: node for node in dag.nodes}
        # map name to number of predecessors (parents)
        name2npreds = {}
        # map name to list of successors (children)
        name2succs = {name: [] for name in name2node}
    
        for node in dag.nodes:
            thisname = node.name
            name2npreds[thisname] = len(node.parents)
            for p in node.parents:
                name2succs[p.name].append(thisname)
    
        result = [n for n, npreds in name2npreds.items() if npreds == 0]
        for p in result:
            for c in name2succs[p]:
                npreds = name2npreds[c]
                assert npreds
                npreds -= 1
                name2npreds[c] = npreds
                if npreds == 0:
                    result.append(c)
    
        if len(result) < len(name2node):
            raise ValueError("no topsort - cycle")
        return tuple(name2node[p] for p in result)
    

    这里有一个微妙的点:外部循环附加到result 它正在迭代result。那是故意的。其效果是,result 中的每个元素都被外循环只处理一次,而不管元素是在初始 result 中还是后来添加的。

    请注意,在遍历输入 DAGNodes 时,它们中的任何内容都不会改变。

    【讨论】:

    • 很高兴它有帮助!注意:如果事实证明给定的名称不是不同的,您可以使用 id(node) 来代替唯一的“名称”。
    猜你喜欢
    • 1970-01-01
    • 2020-10-26
    • 2019-08-07
    • 1970-01-01
    • 1970-01-01
    • 2022-11-26
    • 1970-01-01
    • 2017-02-27
    • 1970-01-01
    相关资源
    最近更新 更多