使用Python实现卡恩拓扑排序算法

2023-12-08

Kahn 在 62 中提出了一个算法拓扑排序任何 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

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

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

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

到目前为止我能想到的一种方法是创建一个deep获取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方法等突然出现在我的脑海中。)

有什么建议吗?


我将建议一种不那么字面意义的算法实现:你根本不需要操作 DAG,你只需要操作信息about有向无环图。该算法需要的唯一“有趣”的东西是从节点到其子节点的映射(与 DAG 实际存储的相反),以及每个节点的父节点数量的计数。

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

那么这应该有效:

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 while它正在迭代result。这是故意的。效果是每个元素result无论元素是否在初始元素中,外循环都会只处理一次result或稍后添加。

请注意,虽然输入DAG and Nodes 被遍历,其中没有任何内容被改变。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

使用Python实现卡恩拓扑排序算法 的相关文章

  • Python HTTP Post 方法将响应返回为 magicmock 对象而不是值

    我正在尝试使用 POST 方法触发某些 API 后检查响应状态代码 响应状态代码是 Magicmock 实例类型 我正在使用在 python 2 中工作但引发 TypeError 的比较运算符检查状态代码是否在 400 和 500 之间在P
  • C++ 类成员指向全局函数的指针

    我想要一个类 它的成员是一个指向函数的指针 这是函数指针 typedef double Function double 这是一个符合函数指针定义的函数 double f1 double x return 0 这是类定义 class Inte
  • 在Spyder(Python 3.6)中导入cv2时出现导入错误

    我已经在Windows操作系统中安装了opencv 3 0 0 我已运行该应用程序并已成功将其安装在C 驱动器并还复制了cv2 pyd文件输入C Python27 Lib site packages正如我在几个教程视频中看到的那样 在我的
  • setColumnStretch 和 setRowStretch 如何工作

    我有一个使用构建的应用程序PySide2它使用setColumnStretch用于柱拉伸和setRowStretch用于行拉伸 它工作得很好 但我无法理解它是如何工作的 我参考了 qt 文档 但它对我没有帮助 我被困在括号内的两个值上 例如
  • 在 Windows 上导入 scipy.linalg 时出错(python 3.3)

    我在 Windows 上使用 python 3 3 我下载了scipy 0 13 2 win32 py3 3 exe from scipy 库 http www lfd uci edu 7Egohlke pythonlibs scipy并安
  • 将文件拆分为块

    我正在尝试分割格式为以下的文件 some garbage lines target G0 S0 type xy 0 108847E 02 0 489034E 04 0 108711E 02 0 491023E 04 0 108574E 02
  • 在 pandas 中获取组名称的有效方法

    我有一个包含大约 300 000 行的 csv 文件 我将其设置为按特定列分组 每个组大约有 140 名成员 总共 2138 个组 我正在尝试生成组名称的 numpy 数组 到目前为止 我已经使用 for 循环来生成名称 但处理所有内容都需
  • Selenium 3 Firefox .click() 不起作用

    自从我升级到最新的 Selenium 版本后 我的 Firefox 驱动程序无法正常工作 未能通过搜索 Google Stack 找到答案 我希望这里有人能找到答案 我已经构建了一个页面对象模型 用于登录网页 单击管理站点并填写用户名 密码
  • selenium.common.exceptions.WebDriverException:消息:服务

    当我使用 selenium 控制 Chrome 时遇到了麻烦 这是我的代码 from selenium import webdriver driver webdriver Chrome When i tried to operate it
  • 使对象在运行时不可变 [C#]

    有什么方法 我希望利用反射 可以使实例化对象不可变及其所有公共财产 我有一个来自其他人的代码库 没有可用源 的类 我需要使用它 并且我基本上希望在实例化该类后 如果任何地方的任何代码段尝试调用该类中的公共设置器 则抛出异常 注意 我不想在类
  • python多重继承,调用基类函数

    我只是尝试在 python 中进行多重继承 我想出了这个 class ParentOne def foo self print ParentOne foo is called class ParentTwo def foo self pri
  • 如何跨多个文本文件查找字典中键的频率?

    我应该计算文档 individual articles 中所有文件中字典 d 的所有键值的频率 这里 文档 individual articles 大约有20000个txt文件 文件名为1 2 3 4 例如 假设 d Britain 5 7
  • 在 python 中指定文件夹位置时使用 / 和 \\ 有什么区别?

    我在 Windows 10 上使用 python v3 6 当指定字符串来表示目录位置时 下面的 2 种方法有什么区别 folder location C Users username Dropbox Inv folder location
  • 有没有办法在javascript中代理(拦截)一个类的所有方法?

    我希望能够在类本身的构造函数内代理类的所有方法 class Boy constructor proxy logic do something before each call of all methods inside class like
  • 如何从 curve_fit 获取置信区间

    我的问题涉及统计学和Python 我是两者的初学者 我正在运行模拟 对于自变量 X 的每个值 我都会为因变量 Y 生成 1000 个值 我所做的是计算每个 X 值的 Y 平均值 并使用 scipy optimize curve fit 拟合
  • 在 PHP 中使用可变变量是不好的做法吗?

    例如 一个简单的MVC类型系统 api class method使用重写为 PHP 变量 htaccess nginx conf 然后做类似的事情
  • 如何更改充当按钮的范围的文本

    我正在为自定义 Web 应用程序编写自动化测试 我遇到了无法更改跨度文本的问题 我尝试过使用 driver execute script 但没有运气 如果我更好地了解 javascript 这确实会有帮助 据我所知 您无法单击跨度 并且列表
  • 类属性在功能上依赖于其他类属性

    我正在尝试使用静态类属性来定义另一个静态类属性 我认为可以通过以下代码来实现 f lambda s s 1 class A foo foo bar f A foo 然而 这导致NameError name A is not defined
  • 使用 python 将文本发送到带有逗号分隔符的列

    如何使用分隔符 在 Excel 中将一列分成两列 并使用 python 命名标题 这是我的代码 import openpyxl w openpyxl load workbook DDdata xlsx active w active a a
  • 从 SpatialPolygons 和其他 sp 类中提取要素坐标

    Package sp为不同的空间概念 点 线 多边形 提供了许多类 对于某些类 访问要素坐标很简单 例如SpatialLines 所有示例均取自相应课程的帮助页面 l1 cbind c 1 2 3 c 3 2 2 l1a cbind l1

随机推荐