【问题标题】:NetworkX - generating a random connected bipartite graphNetworkX - 生成一个随机连接的二分图
【发布时间】:2019-02-22 15:08:44
【问题描述】:

我正在使用 NetworkX 使用 nx.bipartite.random_graphnx.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_graph(5,6,0.6)p>0.5 可以消除错误。同样,bipartite.gnmk_random_graph(5,6,11) 其中k>n+m。我没有意识到是这种情况,因为我假设如果边数低于连接每个顶点所需的边数,那么只会有一些浮动顶点。

感谢您的帮助!

【问题讨论】:

  • 你能打印图表看看它是什么样子吗,也许你有孤立的节点?
  • 您是否系统地收到此错误?您使用的是哪个版本?它是 Python 2 还是 3?刚刚使用 Python 2 和 networkx 1.11 尝试了您的代码,它多次运行良好。从算法的角度来看,断开的二分图不应该是计算二分的问题。
  • 我还没试过,但我可以。我只是在猜测:p
  • 嗯,the documentation states that the input graph should be connected,你的随机生成的图表没有理由应该是。一个快速(愚蠢)的解决方法是重复生成,直到得到一个连通图。
  • 我想粘贴代码,所以我不得不写一个答案,但你的解决方案也可以。

标签: python networkx bipartite


【解决方案1】:

简短回答

你想做的

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]

说明

所以当你生成这个二分图时,它很可能是断开的。假设它有两个独立的组件XY。这两个组件都是二分的。

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

为什么?

X可以分为两个分区X_1X_2Y可以分为Y_1Y_2B 呢?让top = X_1 + Y_1bottom = X_2 + Y_2。这是一个完全合法的分区。但是top = X_1+Y_2bottom = X_2+Y_1 也是一个完全合法的分区。它应该返回哪一个?这是模棱两可的。该算法明确拒绝做出选择。相反,它会给你一个错误。

怎么办?如果它已断开连接,您可以丢弃B,然后重试。但是您正在使用B 做某事,对吗?只关注连通图是否合理?也许,也许不是。这是你需要弄清楚的事情。但是,如果原因是断开连接的图不方便,那么将您的注意力仅限于连接的图是不合理的。您似乎经常遇到此错误,因此大部分图表已断开连接——您丢弃了大部分案例。似乎这可能会影响您所做的任何事情的最终结果。 (同样,如果您采取措施连接您的网络,您将不再从原始分布中获得随机图,因为您已经确保它们没有断开连接,现在更糟糕的是 - 您可能不会从连接图)。

那么有什么更好的选择呢?查看源代码后,我发现此方法没有按应有的方式记录。事实证明,对于

B = bipartite.gnmk_random_graph(5,6,10)

节点 04(前 5 个)位于顶部,节点 510(后 6 个)位于底部。

或者,您可以直接从图形B 中编码的数据中获取它(文档中未提及)。试试看

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]

【讨论】:

  • “你可能没有从连通图中统一采样”是的,我认为这样做并不容易。您在这里所做的只是将top/bottom 放入您要求位于第一/第二个独立集合中的节点。我想这不是代码的结尾,这只是导致代码崩溃的那一行。另一方面,如果 top 和 bot 是这里的最终目标,那么只需执行:top = set(range(n))bottom = set(range(n, n+m))。但我不明白这一点,所以我猜这不是最终目标。
【解决方案2】:

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

import networkx as nx
import random

random.seed(0)

B = nx.bipartite.gnmk_random_graph(5,6,10)
isolated_nodes = set(B.nodes())
for (u, v) in B.edges():
  isolated_nodes -= {u}
  isolated_nodes -= {v}
print(isolated_nodes)

将显示 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 个参数)。

编辑我很笨,这不行,因为新图没有正确数量的节点/边。但是应该有一个更好的解决方案,而不是重试直到你得到一个好的图表。嗯,这很有趣......

第二次编辑也许这个answer可以帮助找到解决这个问题的好方法。

第三次修改和建议

正如您在我链接的问题中注意到的那样,接受的答案并不是真正正确的,因为生成的图表不是在预期图表集中随机均匀选择的。我们可以在这里做一些类似的事情来获得第一个体面的解决方案。这个想法是首先通过迭代挑选孤立节点并将它们连接到二分图的另一侧来创建具有最少边数的连通二分图。为此,我们将创建两个集合NM,创建从NM 的第一条边。然后我们将选择一个随机隔离节点(来自NM)并将其连接到另一侧的随机非隔离节点。一旦我们不再有任何孤立节点,我们将恰好有 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)

但我再说一遍,这个图并不是在所有可能的二分图的集合中随机均匀选择的(我们在 n、m 和 k 上定义了约束)。正如我在另一篇文章中所说的那样,这个图表往往会有一些节点的度数高于其他节点。这是因为我们将孤立的节点与连接的组件一一连接,因此在此过程中较早添加的节点将倾向于吸引更多的节点(优先附加)。我问了question on cstheory,看看有没有什么好主意。

edit 我添加了另一种解决方案,而不是这里介绍的解决方案,它稍微好一点,但仍然不是一个好的解决方案。

【讨论】:

  • 您可以在随机二分图的连接组件之间重复添加边,直到它们合并为一个,在每一步随机选择要连接的组件和顶点。您链接到的答案也可以提供一个起点,除了每当您向跨越子图添加边时,您需要检查它是否不会创建奇数循环。
  • 是的,但与我的回答类似,如果添加边,则不满足初始约束({n, m} 具有 k 个边的二分图)。
  • 越想越觉得这个问题其实是一个很有趣的谜题:D。
猜你喜欢
  • 2016-12-30
  • 2022-11-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-12-11
  • 2023-01-03
  • 1970-01-01
相关资源
最近更新 更多