我正在使用复杂的网络。我想找到一组节点,它们在给定的图形中形成3个节点(或三角形)的循环。由于我的图形包含大约百万个边缘,因此使用简单的迭代解决方案(多个“for”循环)效率不高。
我正在使用python进行编程,如果这些是用于处理这些问题的内置模块,请告诉我。
如果有人知道任何可用于在图表中查找三角形的算法,请回复。
答案 0 :(得分:4)
一百万个边缘非常小。除非你做了数千次,否则只需使用一个天真的实现。
我假设你有一个node_ids字典,它指向一个邻居的序列,并且图表是定向的。
例如:
nodes = {}
nodes[0] = 1,2
nodes[1] = tuple() # empty tuple
nodes[2] = 1
我的解决方案:
def generate_triangles(nodes):
"""Generate triangles. Weed out duplicates."""
visited_ids = set() # remember the nodes that we have tested already
for node_a_id in nodes:
for node_b_id in nodes[node_a_id]:
if nod_b_id == node_a_id:
raise ValueError # nodes shouldn't point to themselves
if node_b_id in visited_ids:
continue # we should have already found b->a->??->b
for node_c_id in nodes[node_b_id]:
if node_c_id in visited_ids:
continue # we should have already found c->a->b->c
if node_a_id in nodes[node_c_id]:
yield(node_a_id, node_b_id, node_c_id)
visited_ids.add(node_a_id) # don't search a - we already have all those cycles
检查表现:
from random import randint
n = 1000000
node_list = range(n)
nodes = {}
for node_id in node_list:
node = tuple()
for i in range(randint(0,10)): # add up to 10 neighbors
try:
neighbor_id = node_list[node_id+randint(-5,5)] # pick a nearby node
except:
continue
if not neighbor_id in node:
node = node + (neighbor_id,)
nodes[node_id] = node
cycles = list(generate_triangles(nodes))
print len(cycles)
当我尝试它时,构建随机图需要更长的时间而不是计算周期。
你可能想测试它;)我不保证它是正确的。
您还可以查看networkx,这是一个很大的python图库。
答案 1 :(得分:2)
我不想听起来很苛刻,但你有没有试过谷歌呢?第一个链接是一个非常快速的算法: http://www.mail-archive.com/algogeeks@googlegroups.com/msg05642.html
然后有关于ACM的文章(您可以访问): http://portal.acm.org/citation.cfm?id=244866 (如果你没有访问权限,我相信如果你好好问一下写这篇文章的女士,你会得到一份副本。)
另外,我可以想象一个基于clique-decomposition的三角形枚举方法,但我不知道它是否在某处被描述过。
答案 2 :(得分:2)
非常简单明了的方法是使用Networkx:
使用Networkx,您可以通过nx.cycle_basis(G)获取无向图的循环,然后选择具有3个节点的循环
cycls_3 = [c for c in nx.cycle_basis(G) if len(c)==3]
或者你可以通过find_cliques(G)找到所有派系,然后选择你想要的派系(有3个节点)。 cliques是图的一部分,其中所有节点彼此连接,这发生在具有3个节点的循环/循环中。
答案 3 :(得分:2)
假设它是一个无向图,答案就在于python的networkx库。 如果您只需要计算三角形,请使用:
import networkx as nx
tri=nx.triangles(g)
但是如果您需要知道具有三角形(三元组)关系的边列表,请使用
all_cliques= nx.enumerate_all_cliques(g)
这将为你提供所有派系(k = 1,2,3 ...... max degree - 1)
所以,只过滤三角形,即k = 3,
triad_cliques=[x for x in all_cliques if len(x)==3 ]
triad_cliques将给出仅包含三角形的边列表。
答案 4 :(得分:1)
即使效率不高,您也可能希望实现解决方案,因此请使用循环。写一个测试,这样你就可以知道需要多长时间。
然后,当您尝试新方法时,您可以做两件事: 1)确保答案保持不变。 2)看看改进是什么。
拥有更快的算法可能会错过某些东西可能比慢速算法更差。
进行慢速测试后,您可以看到是否可以并行执行此操作并查看性能提升情况。
然后,您可以看到是否可以标记少于3个顶点的所有节点。
理想情况下,您可能希望首先将其缩小到100左右,这样您就可以绘制它,并以图形方式查看正在发生的事情。
在查看算法时,有时候你的大脑会看到一种不那么明显的模式。
答案 5 :(得分:1)
我正在研究在无向图表上计算三角形数量的相同问题,wisty的解决方案在我的情况下非常有效。我对它进行了一些修改,因此只计算了无向三角形。
#### function for counting undirected cycles
def generate_triangles(nodes):
visited_ids = set() # mark visited node
for node_a_id in nodes:
temp_visited = set() # to get undirected triangles
for node_b_id in nodes[node_a_id]:
if node_b_id == node_a_id:
raise ValueError # to prevent self-loops, if your graph allows self-loops then you don't need this condition
if node_b_id in visited_ids:
continue
for node_c_id in nodes[node_b_id]:
if node_c_id in visited_ids:
continue
if node_c_id in temp_visited:
continue
if node_a_id in nodes[node_c_id]:
yield(node_a_id, node_b_id, node_c_id)
else:
continue
temp_visited.add(node_b_id)
visited_ids.add(node_a_id)
当然,您需要使用字典
#### Test cycles ####
nodes = {}
nodes[0] = [1, 2, 3]
nodes[1] = [0, 2]
nodes[2] = [0, 1, 3]
nodes[3] = [1]
cycles = list(generate_triangles(nodes))
print cycles
使用Wisty的代码,找到的三角形将是 [(0,1,2),(0,2,1),(0,3,1),(1,2,3)]
将三角形(0,1,2)和(0,2,1)计为两个不同的三角形。使用我修改的代码,这些代码只计为一个三角形。
我使用了一个相对较小的100字以内的字典,每个键平均有50个值。
答案 6 :(得分:0)
你需要找到'所有''三角',还是只找'某些'/'任何'? 或者您可能只需要测试特定节点是否是三角形的一部分?
测试很简单 - 给定节点A,是否存在任何两个连接的节点B& C也是直接连接的。
如果你需要找到所有的三角形 - 具体来说,每个节点都连接到另外两个节点的3个节点的所有组 - 那么你需要在很长的'每个'循环'中检查每个可能的组。
唯一的优化是确保您不会两次检查相同的“群组”,例如如果你已经测试了B& C不在A组中,那么不检查是否A& C与B组合在一起。
答案 7 :(得分:0)
很惊讶没有提到Networkx三角形功能。我知道它不一定会返回形成三角形的节点组,但应该与发现在这个页面上的许多人非常相关。
nx.triangles(G) # list of how many triangles each node is part of
sum(nx.triangles(G).values())/3 # total number of triangles
返回节点块的另一种方法是......
for u,v,d in G.edges(data=True):
u_array = adj_m.getrow(u).nonzero()[1] # get lists of all adjacent nodes
v_array = adj_m.getrow(v).nonzero()[1]
# find the intersection of the two sets - these are the third node of the triangle
np.intersect1d(v_array,u_array)
答案 8 :(得分:0)
如果你不关心不同顺序的同一个三角形的多个副本,那么3元组列表可以工作:
from itertools import combinations as combos
[(n,nbr,nbr2) for n in G for nbr, nbr2 in combos(G[n],2) if nbr in G[nbr2]]
这里的逻辑是检查每个节点的每对邻居以查看它们是否已连接。 G[n]
是一种快速迭代或查找邻居的方式。
如果您想摆脱重新排序,请将每个三元组转换为冻结集并制作一组frozensets:
set(frozenset([n,nbr,nbr2]) for n in G for nbr, nbr2 in combos(G[n]) if nbr in G[nbr2])
如果你不喜欢冷冻套装,并想要一套套装,那么:
triple_iter = ((n, nbr, nbr2) for n in G for nbr, nbr2 in combos(G[n],2) if nbr in G[nbr2])
triangles = set(frozenset(tri) for tri in triple_iter)
nice_triangles = [set(tri) for tri in triangles]
答案 9 :(得分:0)
这是Ajay M answer的更有效版本(我会评论它,但我没有足够的声誉)。
enumerate_all_cliques
networkx
方法确实会在图表中返回所有派系,无论其长度如何;因此循环可能需要很长时间(特别是对于非常密集的图形)。
此外,一旦为三角形定义,只需要参数化来为每个团队长度推广该方法,所以这是一个函数:
import networkx as nx
def get_cliques_by_length(G, length_clique):
""" Return the list of all cliques in an undirected graph G with length
equal to length_clique. """
cliques = []
for c in nx.enumerate_all_cliques(G) :
if len(c) <= length_clique:
if len(c) == length_clique:
cliques.append(c)
else:
return cliques
# return empty list if nothing is found
return cliques
要获取三角形,只需使用get_cliques_by_length(G, 3)
即可。
警告:此方法仅适用于无向图。 networkx
答案 10 :(得分:0)
我刚刚发现nx.edge_disjoint_paths
可以计算包含某些边的三角形。比nx.enumerate_all_cliques
和nx.cycle_basis
更快。
它返回源和目标之间的边缘不相交路径。边缘不相交路径是不共享任何边缘的路径。
结果1是包含某些边或源节点与目标节点之间的三角形的数量。
edge_triangle_dict = {}
for i in g.edges:
edge_triangle_dict[i] = len(list(nx.edge_disjoint_paths(g, i[0], i[1]))-1)