NetworkX - генерация случайного связного двудольного графа - PullRequest
0 голосов
/ 22 февраля 2019

Я использую 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_graph(5,6,0.6) и наличие p>0.5 избавляет от ошибки.Точно так же bipartite.gnmk_random_graph(5,6,11), где k>n+m.Я не осознавал, что это так, так как предполагал, что если число ребер будет меньше, чем требуется для соединения каждой вершины, то будет просто несколько плавающих вершин.

Спасибо за помощь!

Ответы [ 2 ]

0 голосов
/ 23 февраля 2019

КОРОТКИЙ ОТВЕТ

Вы хотите сделать

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]

Объяснение

Так, когда вы генерируетеэтот двудольный граф, скорее всего, будет отключен.Допустим, у него есть 2 отдельных компонента X и Y.Оба эти компонента являются двудольными.

bipartite.sets(B) должен определить, какие наборы являются двумя разделами B.Но это может привести к неприятностям.

Почему?

X можно разбить на два раздела X_1 и X_2 и Y можно разбить на Y_1 иY_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 (следующие 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]
0 голосов
/ 22 февраля 2019

Учитывая, что у вас есть {5, 6} двудольный граф только с 10 ребрами, очень вероятно, что ваш граф будет отключен (он настолько мал, что у вас даже есть высокая вероятность наличия изолированных узлов).

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 параметровКонечно).

Редактировать Я тупой, это не может быть хорошо, так как новый граф не имеет правильного количества узлов / ребер.Но должно быть лучшее решение, чем просто повторить попытку, пока вы не получите хороший график.Хм, это интересно ...

2-е редактирование Возможно, этот ответ может помочь найти хорошее решение этой проблемы.

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)

Но я повторяю, этот граф не выбран случайным образом равномерно в наборе всех возможных двудольных графов (с ограничениямимы определили на n, m и k).Как я уже говорил в другом посте, этот граф будет иметь некоторые узлы с более высокой степенью, чем другие.Это связано с тем, что мы соединяем изолированные узлы с подключенным компонентом один за другим, поэтому узлы, которые были добавлены раньше в процессе, будут иметь тенденцию привлекать больше узлов (преимущественное присоединение).Я задал вопрос по cstheory , чтобы посмотреть, появятся ли какие-нибудь яркие идеи.

edit Я добавил другое решение, чем представленное здесь, оно немного лучше, но все жене очень хороший

...