Python NetworkX-生成随机连通二部图

Python NetworkX-生成随机连通二部图,python,networkx,bipartite,Python,Networkx,Bipartite,我正在使用NetworkX使用nx.bipartite.random_graph或nx.bipartite.gnmk_random_graph生成一个二部图,如下所示: B = bipartite.gnmk_random_graph(5,6,10) bottom_nodes, top_nodes = bipartite.sets(B) 但是,我得到一个错误: networkx.exception.AmbiguousSolution: Disconnected graph: Ambiguous

我正在使用NetworkX使用
nx.bipartite.random_graph
nx.bipartite.gnmk_random_graph
生成一个二部图,如下所示:

B = bipartite.gnmk_random_graph(5,6,10)
bottom_nodes, top_nodes = bipartite.sets(B)
但是,我得到一个错误:

networkx.exception.AmbiguousSolution: Disconnected graph: Ambiguous solution for bipartite sets.
这只是一行,所以我不确定我怎么会做错,为什么他们的包会返回(我假设是)一个无效的二分图

谢谢

编辑:我刚刚意识到我需要为第三个参数指定最小边数/概率

例如,
bipartite.random_图(5,6,0.6)
p>0.5
可以消除错误。类似地,
bipartite.gnmk_random_图(5,6,11)
其中
k>n+m
。我没有意识到这种情况,因为我假设如果边的数量低于连接每个顶点所需的数量,那么只会有一些浮动顶点


谢谢你的帮助

考虑到一个只有10条边的{5,6}二部图,很可能图是断开连接的(它非常稀疏,甚至很可能有孤立节点)

将向您显示id为1的节点是隔离的。要使图形连接,您可以只保留其最大的连接组件:

import networkx as nx
import random

random.seed(0)

B = nx.bipartite.gnmk_random_graph(5,6,11)
components = sorted(nx.connected_components(B), key=len, reverse=True)
largest_component = components[0]
C = B.subgraph(largest_component)
这将仅删除节点1(孤立节点)

现在唯一的问题是“这个新图表有多随机”。换言之,它是否在具有5-6个节点和10条边且概率相等的随机连通二部图集中选择任何图。目前我还不确定,但我认为这看起来不错

当然,您的建议(选择一个图形直到其连接)是可以的,但它可能会很昂贵(当然取决于3个参数)

编辑我很笨,这不可能正常,因为新图形没有正确数量的节点/边。但是应该有一个更好的解决方案,而不是在得到一个好的图之前重试。嗯,那很有趣

第二次编辑也许这有助于找到解决此问题的好方法

第三次编辑和建议

正如您在我链接的问题中所注意到的,所接受的答案并不真正正确,因为生成的图不是在预期图集中随机均匀地选择的。我们可以做一些类似的事情来获得第一个像样的解决方案。其思想是首先通过迭代拾取孤立节点并将其连接到二部图的另一侧,创建一个边数最少的连通二部图。为此,我们将创建两组
N
M
,创建从
N
M
的第一条边。然后,我们将选择一个随机隔离节点(从
N
M
)并从另一侧将其连接到一个随机非隔离节点。一旦我们不再有任何孤立节点,我们将有n+m-1条边,因此我们需要向图中添加k-(n+m-1)条额外的边以匹配原始约束

下面是与该算法对应的代码

import networkx as nx
import random

random.seed(0)

def biased_random_connected_bipartite(n, m, k):
  G = nx.Graph()

  # These will be the two components of the bipartite graph
  N = set(range(n)) 
  M = set(range(n, n+m))
  G.add_nodes_from(N)
  G.add_nodes_from(M)

  # Create a first random edge 
  u = random.choice(tuple(N))
  v = random.choice(tuple(M))
  G.add_edge(u, v)

  isolated_N = set(N-{u})
  isolated_M = set(M-{v})
  while isolated_N and isolated_M:
    # Pick any isolated node:
    isolated_nodes = isolated_N|isolated_M
    u = random.choice(tuple(isolated_nodes))

    # And connected it to the existing connected graph:
    if u in isolated_N:
      v = random.choice(tuple(M-isolated_M))
      G.add_edge(u, v)
      isolated_N.remove(u)
    else:
      v = random.choice(tuple(N-isolated_N))
      G.add_edge(u, v)
      isolated_M.remove(u)

  # Add missing edges
  for i in range(k-len(G.edges())):
    u = random.choice(tuple(N))
    v = random.choice(tuple(M))
    G.add_edge(u, v)

  return G

B = biased_random_connected_bipartite(5, 6, 11)
但是我重复一遍,这个图不是在所有可能的二部图集合中随机选择的。正如我在另一篇文章中所说的,这个图将倾向于有一些节点的阶数高于其他节点。这是因为我们将孤立节点一个接一个地连接到连接的组件,因此在该过程中添加得更快的节点将倾向于吸引更多节点(优先连接)。我问他是否有什么好主意


编辑我添加了一个比这里介绍的解决方案更好的解决方案,但仍然不是一个好的解决方案

简短回答

你想做什么

B = bipartite.gnmk_random_graph(5,6,10)
top = [node for node in B.nodes() if B.node[node]['bipartite']==0]
bottom = [node for node in B.nodes() if B.node[node]['bipartite']==1]

解释

因此,当您生成这个二部图时,它很可能是断开连接的。假设它有两个独立的组件
X
Y
。这两个组件都是二部的

bipartite.set(B)
应该确定哪些集合是
B
的两个分区。但它会遇到麻烦的

为什么?

X
可分为两个分区
X\u 1
X\u 2
Y
可分为
Y\u 1
Y\u 2
。那
B
呢?让
top=X_1+Y_1
bottom=X_2+Y_2
。这是完全合法的分割。但是
top=X_1+Y_2
bottom=X_2+Y_1
也是一个完全合法的分区。它应该退回哪一个?这是模棱两可的。算法明确拒绝做出选择。相反,它会给你一个错误


怎么办?如果断开连接,您可以抛出
B
,然后重试。但是你在用
B
做点什么,对吗?将注意力限制在连通图上是否合理?也许,也许不是。这是你需要弄清楚的。但是,如果原因是断开连接的图不方便,那么将注意力限制在连通图上是不合理的。你似乎经常碰到这个错误,所以大部分的图表都是断开连接的——你扔掉了大部分的案例。这似乎可能会影响你所做的一切的最终结果。(类似地,如果您采取步骤连接网络,您将不再从原始分发中获取随机图,因为您已经确保它们没有断开连接,更糟糕的是,您可能无法从连接的图中进行统一采样)

那么有什么更好的选择呢?在查看源代码之后,我发现这个方法没有得到应有的文档化。事实证明,这是因为

B = bipartite.gnmk_random_graph(5,6,10)
节点
0
最多
4
(前五个)位于顶部,节点
5
最多
10
(第五个)
B = bipartite.gnmk_random_graph(5,6,10)
B = bipartite.gnmk_random_graph(5,6,10)
B.nodes(data=True)
> NodeDataView({0: {'bipartite': 0}, 1: {'bipartite': 0}, 2: {'bipartite': 0}, 3: {'bipartite': 0}, 4: {'bipartite': 0}, 5: {'bipartite': 1}, 6: {'bipartite': 1}, 7: {'bipartite': 1}, 8: {'bipartite': 1}, 9: {'bipartite': 1}, 10: {'bipartite': 1}})
top = [node for node in B.nodes() if B.node[node]['bipartite']==0]
bottom = [node for node in B.nodes() if B.node[node]['bipartite']==1]