NetworkX g.neighbors (n) сложность времени

1

Какова временная сложность получения соседей узла в сетиx?

>>> G = nx.Graph()
>>> G.add_edges_from([('a', 'b'), ('a', 'c')])
>>> G.neighbors('a')
Теги:
graph
networkx

1 ответ

1
Лучший ответ

Короткий ответ: получение соседей принимает линейное время O (m) (с m число соседей или в маловероятных сценариях O (m + n) с n числом узлов). Добавление ребер к сети обычно также приводит к линейному времени, а также O (n) в количестве добавляемых ребер, поэтому постоянным для одного края, но в (очень) маловероятном сценарии может принимать квадратичное время O (n 2). Вы можете получить доступ к словарю соседей в постоянное время (ну и в худшем случае - линейное время) с помощью 'G [' a '].

Мы можем проверить исходный код и посмотреть:

def neighbors(self, n):
    # ...
    try:
        return list(self.adj[n])
    except KeyError:
        raise NetworkXError("The node %s is not in the graph." % (n,))

Теперь, если проверить исходный код, мы видим, что (по умолчанию) adj является словарем, действительно:

node_dict_factory = dict
adjlist_dict_factory = dict
edge_attr_dict_factory = dict

def __init__(self, data=None, **attr):
    # ...
    self.node_dict_factory = ndf = self.node_dict_factory
    # ...
    self.node = ndf()  # empty node attribute dict
    # ...

Таким образом, даже если поиск в словаре происходит в худшем случае, но очень маловероятен - линейное время O (n) в количестве узлов, тогда у нас есть только одно линейное время для преобразования списка ключей в список.

С другой стороны, метод add_edges_from реализуется как:

def add_edges_from(self, ebunch, attr_dict=None, **attr):
    # ...
    for e in ebunch:
        ne = len(e)
        if ne == 3:
            u, v, dd = e
        elif ne == 2:
            u, v = e
            dd = {}  # doesnt need edge_attr_dict_factory
        else:
            raise NetworkXError(
                "Edge tuple %s must be a 2-tuple or 3-tuple." % (e,))
        if u not in self.node:
            self.adj[u] = self.adjlist_dict_factory()
            self.node[u] = {}
        if v not in self.node:
            self.adj[v] = self.adjlist_dict_factory()
            self.node[v] = {}
        datadict = self.adj[u].get(v, self.edge_attr_dict_factory())
        datadict.update(attr_dict)
        datadict.update(dd)
        self.adj[u][v] = datadict
        self.adj[v][u] = datadict

Таким образом, мы в основном итерации каждый элемент в списке, сделать некоторые распаковки, а затем добавить словарь данных дважды в adj (один раз в одном направлении, один раз в другом). Если поиск словаря выполняется быстро (обычно это постоянное время), то этот алгоритм имеет линейное время (в количестве добавляемых кортежей). Наихудший случай (но очень маловероятный) поиск в словаре может занять линейное время, поэтому вставка может масштабироваться до O (n 2).

Однако класс Graph позволяет получить доступ к подзадаче с помощью G['a']:

>>> G['a']
AtlasView({'b': {}, 'c': {}})

AtlasView представляет собой представление по словарю, чтобы предотвратить редактирование базового словаря.

  • 0
    Спасибо! поэтому, если я хочу сделать много быстрых поисков соседей в неизменном графе, я полагаю, что было бы лучше предварительно обработать и сохранить соседей каждого узла в словаре {узел: {соседей}}?
  • 0
    @ CarlosG.Oliver: вы можете использовать G['a'] , это вернет вам подчиненное значение этого узла, и ключи, таким образом, содержат соседей. Таким образом, это (при нормальных обстоятельствах) даст вам словарь в постоянном времени. Однако обратите внимание, что тогда вы получили доступ к «данным» графика, и изменение словаря окажет влияние на ваш график G
Показать ещё 1 комментарий

Ещё вопросы

Сообщество Overcoder
Наверх
Меню