Python 3.x 使用字典数据结构查找有向图中至少有3个节点的所有循环
上图是使用乳胶绘制的: 上图用Python表示为一个字典Python 3.x 使用字典数据结构查找有向图中至少有3个节点的所有循环,python-3.x,dictionary,graph,depth-first-search,directed-graph,Python 3.x,Dictionary,Graph,Depth First Search,Directed Graph,上图是使用乳胶绘制的: 上图用Python表示为一个字典 图形={ 'A':['B','D','C'], “B”:[C'], “C”:[…], “D”:[E'], “E”:[G'], “F”:[A”,“I'], 'G':['A','K'], 'H':['F','G'], “I”:[H'], 'J':['A'], 'K':[] } 我有一个大约3378546个节点的大图 给出上面的有向图,我试图找到至少有3个和少于5个不同节点的圆,并输出前3个圆 我花了一天半的时间解决这个问题。我查看了Stac
图形={
'A':['B','D','C'],
“B”:[C'],
“C”:[…],
“D”:[E'],
“E”:[G'],
“F”:[A”,“I'],
'G':['A','K'],
'H':['F','G'],
“I”:[H'],
'J':['A'],
'K':[]
}
我有一个大约3378546个节点的大图
给出上面的有向图,我试图找到至少有3个和少于5个不同节点的圆,并输出前3个圆
我花了一天半的时间解决这个问题。我查看了Stackoverflow,甚至尝试按照本教程进行操作,但没有找到解决方案
在本例中,输出是一个以制表符分隔的文本文件,其中每行都有一个循环
0a、D、E、G
1 F,I,H
0
和1
是索引。
此外,图形节点的字母表中没有顺序
我尝试了以下表单教程:
visted=set()
def dfs(已访问、图形、节点):
如果未访问节点:
打印(节点)
已访问。添加(节点)
对于图[node]中的邻居:
dfs(访问、图表、邻居)
dfs(已访问,图表“A”)
但这没用。我还尝试了这个这里有一个注释代码,它将打印包含找到的循环的数组。我认为,将返回值调整为所需格式(我认为在您的情况下是CSV)不需要更多 可能是因为有了3M个节点,这会变得很慢。然后我建议采用动态编程的方式,缓存/记忆一些递归的结果,以避免重复它们 我希望这能解决你的问题,或者至少对你有所帮助
def cycles_rec(root, current_node, graph, depth, visited, min_depth, max_depth):
depth += 1
# First part our stop conditions
if current_node in visited or current_node not in graph.keys():
return ''
if depth >= max_depth:
return ''
visited.append(current_node)
if root in graph[current_node] and depth >= min_depth:
return current_node
# The recursive part
# for each connection we try to find recursively one that would cycle back to our root
for connections in graph[current_node]:
for connection in connections:
result = cycles_rec(root, connection, graph, depth, visited, min_depth, max_depth)
# If a match was found, it would "bubble up" here, we can return it along with the
# current connection that "found it"
if result != '':
return current_node + ' ' + result
# If we are here we found no cycle
return ''
def cycles(graph, min_depth = 3, max_depth = 5):
cycles = {}
for node, connections in graph.items():
for connection in connections:
visited = []
# Let the recursion begin here
result = cycles_rec(node, connection, graph, 1, visited, min_depth, max_depth)
if result == '':
continue
# Here we found a cycle.
# Fingerprint is only necessary in order to not repeat the cycles found in the results
# It could be ignored if repeating them is not important
# It's based on the fact that nodes are all represented as letters here
# It could be it's own function returning a hash for example if nodes have a more
# complex representation
fingerprint = ''.join(sorted(list(node + ' ' + result)))
if fingerprint not in cycles.keys():
cycles[fingerprint] = node + ' ' + result
return list(cycles.values())
因此,假设您在示例中声明的图形变量:
print(cycles(graph, 3, 5))
会打印出来吗
['A D E G', 'F I H']
注意:此解决方案是所述解决方案的扩展解决方案。我扩展到原始图,有大约300万个节点,我查找所有至少3个节点小于40个节点的循环,并将前3个循环存储到一个文件中
我提出了以下解决方案 Johnson循环查找算法的实现 #原稿:唐纳德·约翰逊。“寻找有向图的所有基本回路”,《暹罗计算杂志》。1975 从集合导入defaultdict 将networkx导入为nx 从networkx.utils导入未实现,成对 @未实施(“未定向”) def findCycles(G): “”“查找有向图的简单圈。 “简单循环”是一个闭合路径,其中没有节点出现两次。 如果两个基本电路不是彼此的循环置换,则它们是不同的。 这是Johnson算法[1]的迭代器/生成器版本。 在某些情况下,可能会有更好的算法[2]\u3]\u3。 参数 ---------- G:NetworkX有向图 有向图 退换商品 ------- 循环发电机:发电机 生成图形基本循环的生成器。 每个循环由沿循环的节点列表表示。 例子 -------- >>>图={'A':['B','D','C'], “B”:[C'], “C”:[…], “D”:[E'], “E”:[G'], “F”:[A”,“I'], 'G':['A','K'], 'H':['F','G'], “I”:[H'], 'J':['A'], 'K':[] } >>>G=nx.DiGraph() >>>G.add_nodes_from(graph.keys()) >>>对于键,graph.items()中的值: G.add_edges_from(([(键,节点)用于值中的节点]) >>>列表(nx.findCycles(G)) [F',I',H',[G',A',D',E']] 笔记 ----- 实施遵循[1]第79-80页。 对于$n$节点、$e$边和$c,时间复杂度为$O((n+e)(c+1))$$ 基本电路。 工具书类 ---------- ..[1]求有向图的所有基本回路。 D.B.Johnson,《暹罗计算机杂志》第4期,第1期,1975年,第77-84页。 https://doi.org/10.1137/0204007 ..[2]枚举有向图的循环:一种新的预处理策略。 G.Loizou和P.Thanish,《信息科学》,第27卷,第163-182页,1982年。 ..[3]有向图的基本圈的搜索策略。 J.L.Szwarcfiter和P.E.Lauer,BIT数值数学, v、 1976年12月16日,第2期,192-204页。 -------- """ def_解除阻止(此节点,已阻止,B): 堆栈={thisnode} 堆栈时: node=stack.pop() 如果节点处于阻止状态: 已阻止。删除(节点) stack.update(B[节点]) B[node].clear() #约翰逊的算法需要对节点进行某种排序。 #我们指定强连通comps给出的任意顺序 #无需跟踪订单,因为每个节点都是在处理过程中删除的。 #我们还保存了实际的图形,以便对其进行变异。我们只坐火车 #边,因为我们不希望在此处复制边和节点属性。 subG=类型(G)(G.边() sccs=[nx中scc的scc。如果列表中的len(scc)(范围(3,41)),则强连接的组件(subG)] #Johnson算法排除自循环边,如(v,v) #为了向后兼容,我们提前记录这些周期 #然后从subG中删除 对于subG中的v: 如果子对象具有_边(v,v): 收益率[v] 子G.移除边缘(v,v) 而SCC: scc=sccs.pop() sccG=子g.子图(scc) #scc的顺序决定了节点的顺序 startnode=scc.pop() #处理节点从递归版本运行“电路”例程 路径=[startnode] blocked=set()#顶点:阻止搜索? closed=set()#循环中涉及的节点 已阻止。添加(startnode)