我有一组节点和一个函数foo(u,v)
,可以确定两个节点是否相等。 “相等”是指及物对等:
If 1==2
和2==3
然后1==3
,还有:If 1==2
和1!=4
然后2!=4
给定一组节点后,我可以通过将节点的每种可能的组合传递给foo(u,v)
(返回预定结果)来找到图中的所有已连接组件仅用于演示目的-这不是真正的功能!)功能并构建所需的边缘。像这样:
import networkx as nx
import itertools
from matplotlib import pyplot as plt
def foo(u, v):
# this function is simplified, in reality it will do a complex
# calculation to determine whether nodes are equal.
EQUAL_EDGES = {(1, 2), (2, 3), (1, 3), (4, 5)}
return (u, v) in EQUAL_EDGES
def main():
g = nx.Graph()
g.add_nodes_from(range(1, 5 + 1))
for u, v in itertools.combinations(g.nodes, 2):
are_equal = foo(u, v)
print '{u}{sign}{v}'.format(u=u, v=v, sign='==' if are_equal else '!=')
if are_equal:
g.add_edge(u, v)
conn_comps = nx.connected_components(g)
nx.draw(g, with_labels=True)
plt.show()
return conn_comps
if __name__ == '__main__':
main()
这种方法的问题在于,我要避免很多冗余检查:
1==2 # ok
1==3 # ok
1!=4 # ok
1!=5 # ok
2==3 # redundant check, if 1==2 and 1==3 then 2==3
2!=4 # redundant check, if 1!=4 and 1==2 then 2!=4
2!=5 # redundant check, if 1!=5 and 1==2 then 2!=5
3!=4 # redundant check, if 1!=4 and 1==3 then 3!=4
3!=5 # redundant check, if 1!=5 and 1==3 then 3!=5
4==5 # ok
我想避免以O(n ^ 2)的时间复杂度运行。
通过自定义foo(u,v)
函数有效地找到所有连接的组件的正确方法是什么(或者可能是任何python库中的现有函数)?
答案 0 :(得分:1)
尚不清楚您真正要做什么,但是这是一个仅检查每个等效组中一个元素的解决方案:
nodes2place = range(1, 6)
cclist = []
for u in nodes2place:
node_was_placed=False
for icc in range(len(cclist)):
if foo(u, cclist[icc][0]):
cclist[icc].append(u)
node_was_placed=True
break
# node doesn't fit into existing cc so make a new one
if not node_was_placed:
cclist.append([u])
答案 1 :(得分:1)
您可以跟踪两个字典中哪些边在传递上相等或不相等。对于每种边缘组合,您都可以在O(1)时间内进行一些简单的检查,以查看计算是否多余。否则,您将根据第一原理进行计算,然后根据边的相等或不相等,使用必要的信息更新上述字典。您仍然必须进行C(n,2)个相等性检查,因为这是要迭代的组合数量,但是对于其中的许多组合,可以立即做出决定。
equal_edges
字典更易于解释,因此让我们开始吧。 1-2个边缘对相等,但是由于1或2都不作为键(字典现在为空),我们创建了{1, 2}
集并将其附加到equal_edges[1]
和{{1 }}。然后,我们遇到等边线对1-3。由于equal_edges[2]
现在存在,因此我们在其传递相等的节点上加3。但是,由于此设置在边1和边2之间共享,因此在两个地方都进行了更新。现在,我们还必须将同一集合附加到equal_edges[1]
。所有这三个边都引用内存中的同一集合,即equal_edges[3]
,因此我们不复制任何数据。现在,当要检查等边线对2-3时,{1, 2, 3}
或3 in equal_edges[2]
允许我们绕过任何繁重的计算。
对于2 in equal_edges[3]
,其逻辑有些相似,但是对于传递不等式的边,我们还必须参考unequal_edges
字典。例如,边缘对1-4是不相等的。但是由于1在传递上等于2和3,所以我们必须有equal_edges
。设置unequal_edges[4] = equal_edges[1]
或unequal_edges[1] = {4}
等将是多余的。这是因为可以从unequal_edges[2] = {4}
获得此信息。这只是意味着,对于传递不等式的a-b对,我们需要仔细检查,即unequal_edges[4]
。
a in unequal_edges[b] or b in unequal_edges[a]
此处的打印语句仅用于演示目的。如果您运行
from itertools import combinations
equal_edges = {}
unequal_edges = {}
def update_equal_edges(a, b):
def update_one(a, b):
equal_edges[a].add(b)
equal_edges[b] = equal_edges[a]
exists_a = a in equal_edges
exists_b = b in equal_edges
if not (exists_a or exists_b):
s = set((a, b))
equal_edges[a] = s
equal_edges[b] = s
elif exists_a and not exists_b:
update_one(a, b)
elif exists_b and not exists_a:
update_one(b, a)
def update_unequal_edges(a, b):
exists_a = a in equal_edges
exists_b = b in equal_edges
if not (exists_a or exists_b):
s = set((a, b))
unequal_edges[a] = s
unequal_edges[b] = s
elif exists_a and not exists_b:
unequal_edges[b] = equal_edges[a]
elif exists_b and not exists_a:
unequal_edges[a] = equal_edges[b]
def are_equal_edges(a, b):
if a in equal_edges.get(b, []):
print('{}=={} # redundant'.format(a, b))
return True
if (a in unequal_edges.get(b, [])) or (b in unequal_edges.get(a, [])):
print('{}!={} # redundant'.format(a, b))
return False
# hardcoded equal edges which are the result
# of some complex computations
are_equal = (a, b) in {(1, 2), (1, 3), (4, 5)}
if are_equal:
update_equal_edges(a, b)
else:
update_unequal_edges(a, b)
print('{}{}{} # ok'.format(a, '==' if are_equal else '!=', b))
return are_equal
您得到以下结果
for a, b in combinations(range(1, 6), 2):
are_equal_edges(a, b)
答案 2 :(得分:0)
您可以使用0
表示相等性,使用math.inf
表示不平等性作为边缘权重。然后,对于每个节点对u, v
,您可以计算从u
到v
的路径长度,并根据结果确定是否需要调用(重)节点检查:>
g = nx.Graph()
g.add_nodes_from(range(1, 6))
for u, v in it.combinations(g.nodes, 2):
try:
path = nx.shortest_path(g, u, v)
except nx.NetworkXNoPath:
new_weight = 0 if func(u, v) else math.inf
else:
weights = list(x['weight'] for x in it.starmap(g.get_edge_data, zip(path[:-1], path[1:])))
if min(weights) == math.inf:
new_weight = 0 if func(u, v) else math.inf
elif max(weights) == math.inf:
new_weight = math.inf
else:
new_weight = 0
g.add_edge(u, v, weight=new_weight)
如果您不喜欢图形中的这些无限边,则可以: