Python 如何根据图形数据标记作为循环中初始顶点的节点

Python 如何根据图形数据标记作为循环中初始顶点的节点,python,algorithm,Python,Algorithm,我需要实现一个算法,这样在一组唯一且有序的图边中,我可以找到一个循环节点 例如,对于a->b,b->c,c->a,则'a'是一个循环节点,因此我想在该边缘用'a@'对其进行注释,其他节点则类似 作为示例数据,我使用以下内容: a = [('a', 'b'), ('b', 'c'), ('c', 'd'), ('d', 'a'), ('a', 'e'), ('e', 'a'), ('f', 'e')] 那么这将成为: [('a', 'b'), ('b', 'c'), ('c', 'd'), ('

我需要实现一个算法,这样在一组唯一且有序的图边中,我可以找到一个循环节点

例如,对于
a->b,b->c,c->a
,则
'a'
是一个循环节点,因此我想在该边缘用
'a@'
对其进行注释,其他节点则类似

作为示例数据,我使用以下内容:

a = [('a', 'b'), ('b', 'c'), ('c', 'd'), ('d', 'a'), ('a', 'e'), ('e', 'a'), ('f', 'e')]
那么这将成为:

[('a', 'b'), ('b', 'c'), ('c', 'd'), ('d', 'a@'), ('a', 'e'), ('e', 'a@'), ('f', 'e')]
如何在python中实现这一点

这就是我所尝试的:

collection={}
数据,结果=[],[]
对于a中的i,j:
如果我在collection.keys()中:
集合[i]。追加(j)
其他:
集合[i]=[j]
如果j在collection.keys()中:
对于范围内的项目(len(集合[i]):
如果集合[i][item]==j:
nr+=1
集合[i][item]=j+'@'

打印(收集)
此解决方案构建它在边缘列表中遇到的所有可能路径,因为我们不知道循环从何处开始。如果图形很大,它还会修剪创建的路径列表,以防止内存膨胀。这是丑陋的,但工作的基础上,你的需要

a = [('a', 'b'), ('b', 'c'), ('c', 'd'), ('d', 'a'), ('a', 'e'), ('e', 'a'), ('f', 'e')]

annotated = []
paths = []
for edge in a:
    new_paths = []
    paths.append(''.join(edge))
    annotated.append(edge)
    cycle = ''
    for path in paths[:]:
        if path.endswith(edge[0]):
            if path.startswith(edge[1]):
                annotated[-1] = (annotated[-1][0], annotated[-1][1]+'@')
                cycle = path + edge[1]
            else:
                new_paths.append(path + edge[1])
        else:
            new_paths.append(path)
    paths = [x for x in new_paths if x not in cycle]
    print(paths)
print(f'Result: {annotated}')

"""
Out:

['ab']
['abc', 'bc']
['abcd', 'bcd', 'cd']
[]
['ae']
[]
['fe']
Result: [('a', 'b'), ('b', 'c'), ('c', 'd'), ('d', 'a@'), ('a', 'e'), ('e', 'a@'), ('f', 'e')]
"""
问题:对于
a->b,b->c,c->a
,那么
'a'
是一个循环节点,因此我想在这一边缘用
'a@
对其进行注释,其他节点则类似

如果找到有效的节点链,例如
a->b,b->c,c->a
,则用
'@'
标记最后一个节点

def mark_cycle_end(nodes, initial, marker='@'):
    chained = None, None
    cycle_start = None

    for i, node in enumerate(nodes):
        if cycle_start is not None \
            and chained[0] == chained[1]:
            chained = nodes[i - 1][1], node[0]

        if node[0] == initial:
            cycle_start = i
            chained = None, None

        elif node[1] == initial:
            start, end, end_ = cycle_start, i, i + 1

            if chained == (None, None):
                print('missing start node, end is: {}'
                      .format(nodes[end:end_]))
            elif not chained[0] == chained[1]:
                print('missing link {} in {}'
                      .format(chained, nodes[start:end_]))
            else:  # is chained
                print('mark:{}'
                      .format((start, end, nodes[start:end_])))
                nodes[end] = (nodes[end][0], nodes[end][1] + marker)

            cycle_start = None
            chained = None, None
用法

mark:(0, 3, [('a', 'b'), ('b', 'c'), ('c', 'd'), ('d', 'a')])
mark:(4, 5, [('a', 'e'), ('e', 'a')])
MATCH:[('a', 'b'), ('b', 'c'), ('c', 'd'), ('d', 'a@'), ('a', 'e'), ('e', 'a@'), ('f', 'e')]
missing link ('z', 'b') in [('a', 'z'), ('b', 'z'), ('c', 'd'), ('d', 'a')]
missing start node, end is: [('z', 'a')]
MATCH:[('a', 'b'), ('a', 'c'), ('a', 'z'), ('b', 'z'), ('c', 'd'), ('d', 'a'), ('d', 'z'), ('z', 'd'), ('z', 'c'), ('z', 'a'), ('z', 'y'), ('z', 'b')]
输出

mark:(0, 3, [('a', 'b'), ('b', 'c'), ('c', 'd'), ('d', 'a')])
mark:(4, 5, [('a', 'e'), ('e', 'a')])
MATCH:[('a', 'b'), ('b', 'c'), ('c', 'd'), ('d', 'a@'), ('a', 'e'), ('e', 'a@'), ('f', 'e')]
missing link ('z', 'b') in [('a', 'z'), ('b', 'z'), ('c', 'd'), ('d', 'a')]
missing start node, end is: [('z', 'a')]
MATCH:[('a', 'b'), ('a', 'c'), ('a', 'z'), ('b', 'z'), ('c', 'd'), ('d', 'a'), ('d', 'z'), ('z', 'd'), ('z', 'c'), ('z', 'a'), ('z', 'y'), ('z', 'b')]

就我从你们的例子、定义和问题中可以看出,连接两个已经间接连接的节点的任何边都会导致循环节点

因此,如果跟踪连接的节点,则可以确定边是否连接新节点。如果它没有连接新节点,则边连接到循环节点(在这种情况下,可能说它是循环边更有用吗?)

编辑:我在这里假设图是,因为没有提到边的方向。如果这个问题是针对有向图的,那么我的答案是无效的,因为你根本无法使用有向图中的集合来计算循环

以下是函数:

def markcyclicnodes(edges):
    groups = []
    result = []
    for edge in edges:
        cyclicnode = False
        newlink = False
        newnode = False

        group1count = 0
        group2count = 0
        tolink = 0
        todelete = 0
        for group1 in groups:
            if cyclicnode == False and newlink == False and newnode == False:
                if edge[0] in group1 and edge[1] in group1:
                    cyclicnode = True

                elif edge[0] in group1:
                    tolink = group1count
                    for  group2 in groups:
                        if edge[1] in group2:
                            newlink = True
                            todelete = group2count
                        group2count += 1
                    if newlink == False:
                        groups[group1count].append(edge[1])
                        newnode = True

                elif edge[1] in group1:
                    tolink = group1count
                    for group2 in groups:
                        if edge[0] in group2:
                            newlink = True
                            todelete = group2count
                        group2count += 1
                    if newlink == False:
                        groups[group1count].append( edge[0])
                        newnode = True
            group1count += 1

        if newlink == False and cyclicnode == False and newnode == False :
            groups.append([edge[0],edge[1]])
        if newlink == True:
            for node in groups[todelete]:
                groups[tolink].append (node)
            del groups[todelete]

        if cyclicnode == True:
            result.append ((edge[0], edge[1]+'@'))
        else:
            result.append(edge)      
    return result

这是我的测试数据:

edges1 = [('a', 'b'), ('b', 'c'), ('c', 'd'), ('d', 'a'), ('a', 'e'), ('e', 'a'), ('f', 'e')]
edges2 =  [('a', 'b'), ('a', 'c'), ('a', 'z'), ('b', 'z'), ('c', 'd'), ('d', 'a'), ('d', 'z'), ('z', 'd'), ('z', 'c'), ('z', 'a'), ('z', 'y'), ('z', 'b')]
edges3 = [('a', 'b'), ('b', 'c'),('z', 'bye'), ('bye', 'c'), ('hi', 'shy'),('shy','a'),('shy','z')]
以下是每组测试数据的结果

[('a', 'b'),
 ('b', 'c'),
 ('c', 'd'),
 ('d', 'a@'),
 ('a', 'e'),
 ('e', 'a@'),
 ('f', 'e')]
简短的变体:

a = [('a', 'b'), ('b', 'c'), ('c', 'd'), ('d', 'a'), ('a', 'e'), ('e', 'a'), ('f', 'e')]

repeated_nodes = set()
result = []

for edge in a:
    if edge[1] not in repeated_nodes:
        result.append(edge)
    else:
        result.append((edge[0], f'@{edge[1]}'))
    repeated_nodes.add(edge[0])

print(result)

[('a', 'b'), ('b', 'c'), ('c', 'd'), ('d', '@a'), ('a', 'e'), ('e', '@a'), ('f', '@e')]
或者如果你喜欢理解,那么:

repeated_nodes = set()
result = []

for edge in a:
    result.append(edge if edge[1] not in repeated_nodes else (edge[0], f'@{edge[1]}'))
    repeated_nodes.add(edge[0])

print(result)

[('a', 'b'), ('b', 'c'), ('c', 'd'), ('d', '@a'), ('a', 'e'), ('e', '@a'), ('f', '@e')]

为什么要注释第二个和第四个
a
,而不是第一个和第三个?因为在这些边上,特定的“a”也将表示作为循环源的初始“a”。因此,对于a->b,b->c,c->d,d->a,我只想注释最后一个“a”。我这样做是为了将图形数据转换为树数据。我想说你应该注释第三个,然后第二个
a
就结束了循环。无论如何,您可能需要查看图形分析。为什么在您的示例中,
a
是唯一注释的节点?是什么让它成为周期的“初始”顶点?看起来没人理解你。你能在你的问题中加入你的其他例子和它们的预期结果吗?我指的是你上一份副本中的例子和@Ivan Popov answer下的例子。你能确切地解释一下哪个节点应该被标记为循环节点以及为什么吗?对于[('a','b'),('a','c'),('a','z'),('b','z'),('c','d'),('d','z'),('z','c'),('z','a'),('z','y'),('z','b'),它将失败。('z','a')和('z','b')不存在true。然而,在这个例子中(z,c@)也应该存在,因为我们有序列
(c,d),(d,z),(z,c)
。您对周期的开始和结束的定义是任意的,并且这些边不是以逻辑方式“排序”的。序列
(c,d)、(z,c)、(d,z)
的输出是什么?如果测试数据是
[('a','b'),('a','c'),('a','z'),('b','z')]
,我想你会在没有循环的情况下将'z@'标记为再次到达。我站在我的评论中,如果你把任何东西连接到集合中的集合,然后创建一个循环,请把你的答案编辑成是否考虑有向或无向图——我希望这个问题是明确的。
a = [('a', 'b'), ('b', 'c'), ('c', 'd'), ('d', 'a'), ('a', 'e'), ('e', 'a'), ('f', 'e')]

repeated_nodes = set()
result = []

for edge in a:
    if edge[1] not in repeated_nodes:
        result.append(edge)
    else:
        result.append((edge[0], f'@{edge[1]}'))
    repeated_nodes.add(edge[0])

print(result)

[('a', 'b'), ('b', 'c'), ('c', 'd'), ('d', '@a'), ('a', 'e'), ('e', '@a'), ('f', '@e')]
repeated_nodes = set()
result = []

for edge in a:
    result.append(edge if edge[1] not in repeated_nodes else (edge[0], f'@{edge[1]}'))
    repeated_nodes.add(edge[0])

print(result)

[('a', 'b'), ('b', 'c'), ('c', 'd'), ('d', '@a'), ('a', 'e'), ('e', '@a'), ('f', '@e')]