Python 序列化有向加权图

Python 序列化有向加权图,python,optimization,python-3.x,graph,Python,Optimization,Python 3.x,Graph,我有一个有向加权图。图中的每个节点都表示为一个2元组,其第一个元素是节点的名称,第二个元素是一个元组,包含来自该节点的所有顶点,按其权重排序。基本上每个顶点的权重就是它在这个元组中的索引 免费范例: a = ('A', () ) a是一个名为a的节点,没有顶点起源于该节点 b = ('B', () ) a = ('A', (b,) ) a是名为a的节点,与名为B的节点有一个顶点,权重为0 b = ('B', () ) c = ('C', () ) a = ('A', (b, c) ) a是

我有一个有向加权图。图中的每个节点都表示为一个2元组,其第一个元素是节点的名称,第二个元素是一个元组,包含来自该节点的所有顶点,按其权重排序。基本上每个顶点的权重就是它在这个元组中的索引

免费范例:

a = ('A', () )
a
是一个名为a的节点,没有顶点起源于该节点

b = ('B', () )
a = ('A', (b,) )
a
是名为a的节点,与名为B的节点有一个顶点,权重为0

b = ('B', () )
c = ('C', () )
a = ('A', (b, c) )
a
是一个名为a的节点,其两个顶点分别指向名为B和C的节点,第一个为权重0,第二个为权重1

显然,
('A',(b,c))
并不等于
('A',(c,b))

现在我需要根据以下规则序列化此图:

  • 结果的第一个元素是起始节点
  • 然后按照权重递增的顺序跟随从起始节点直接访问的所有节点。如果结果中已存在节点,则不要再次追加该节点
  • 现在将规则1和规则2递归地应用于刚才添加的所有元素
  • 基本上,从低到高(重量)第一,深度第二

    这里是一个输入和输出示例:

    f = ('F', () )
    e = ('E', () )
    d = ('D', (e,) )
    c = ('C', (f, d, e) )
    b = ('B', (d,) )
    a = ('A', (b, c) )
    
    结果:

    ['A', 'B', 'C', 'D', 'F', 'E']
    
    现在我的第一个天真的方法是:

    def serialize (node):
        acc = []
    
        def serializeRec (node, level):
            tbd = [] #acc items to be deleted
            tbi = False #insertion index
            for idx, item in enumerate (acc):
                if item [1] > level and tbi == False:
                    tbi = idx
                if item [0] == node [0]:
                    if item [1] > level: tbd.append (item)
                    else: break
            else:
                if tbi == False: acc.append ( (node [0], level) )
                else: acc.insert (tbi, (node [0], level) )
            for item in tbd:
                acc.remove (item)
            for vertex in node [1]:
                serializeRec (vertex, level + 1)
    
        serializeRec (node, 0)
        #remove levels
        return [node for node, level in acc]
    
    这显然是一个非常糟糕的主意,因为在每次递归中,我都迭代各种列表。这就是我改用字典的原因:

    def serializeDict (node):
        levels = defaultdict (list) #nodes on each level
        nodes = {} #on which level is which node
    
        def serializeRec (node, level):
            try:
                curLevel = nodes [node [0] ]
                if curLevel > level:
                    nodes [node [0] ] = level
                    levels [curLevel].remove (node [0] )
                    levels [level].append (node [0] )
            except:
                nodes [node [0] ] = level
                levels [level].append (node [0] )
            for vertex in node [1]:
                serializeRec (vertex, level + 1)
    
        serializeRec (node, 0)
        #flatten dict items
        return [node for level in (v for _, v in sorted (levels.items (), key = lambda x: x [0] ) ) for node in level]
    
    除了非常小的图形,它的运行速度要快得多

    我现在的问题是:

    如何以最小化运行时为目标优化此序列化?

    内存使用是没有关系的(是的,宝贝),KLOC是没有关系的,只有运行时计数。除了输入数据的格式外,所有内容都可以更改。但是,如果最后节省了时间,我将很乐意在序列化函数中重新组织这些数据

    我非常感谢您阅读本TL;语言之墙博士


    用于鬼混的示例图:

    z = ('Z', () ); y = ('Y', (z,) ); x = ('X', (z, y) ); w = ('W', (x, y, z) ); v = ('V', (w, x) ); u = ('U', (w, v) ); t = ('T', (u, w) ); s = ('S', (z, v, u) ); r = ('R', (t, u, z) ); q = ('Q', (r, z) ); p = ('P', (w, u) ); o = ('O', (v, r, q) ); n = ('N', (r, z) ); m = ('M', (t,) ); l = ('L', (r,) ); k = ('K', (x, v) ); j = ('J', (u,) ); i = ('I', (n, k) ); h = ('H', (k, x) ); g = ('G', (l,) ); f = ('F', (t, m) ); e = ('E', (u,) ); d = ('D', (t, e, v) ); c = ('C', (m,) ); b = ('B', (n,) ); a = ('A', (g, m, v) )
    

    这在没有递归的情况下工作,并使用双端队列来提高效率:

    from collections import deque
    
    def serialize_plain(n):
        name, children = n
        output = [name]
    
        candidates = deque(children)
        while candidates:
            cname, cchildren = candidates.popleft()
            if cname not in output:
                output.append(cname)
                candidates.extend(cchildren)
    
        return output
    
    根据图形的大小,保留一组已处理的节点以避免昂贵的列表查询可能是有意义的:

    from collections import deque
    
    def serialize_with_set(n):
        name, children = n
        output = [name]
        done = {name}
    
        candidates = deque(children)
        while candidates:
            cname, cchildren = candidates.popleft()
            if cname not in done:
                output.append(cname)
                done.add(cname)
                candidates.extend(cchildren)
    
        return output
    
    现在我需要根据以下规则序列化此图:

    结果的第一个元素是起始节点。 然后按照权重递增的顺序跟随从起始节点直接访问的所有节点。如果结果中已存在节点,则不要再次追加该节点。 现在将规则1和规则2递归地应用于刚才添加的所有元素

    我想从理论的角度补充一点,这是一种非常常见的遍历图的方法,称为,另外还需要对相邻列表进行排序。正如第一个答案中提到的,通常使用队列来避免递归


    如果您的项目允许使用,您应该在任何自尊的图形库中找到宽度优先遍历,例如,应该是快速的,因为它是基于优秀的C++ <代码> Boo::图,这是C++世界中的事实上标准的图形库。尝试先进行拓扑排序(即O(V+E)),然后运行非递归算法进行序列化them@evhen14谢谢,我会调查的。非常感谢。我会调查你的答案。