Сделать неориентированный граф из списка смежности

Я пытаюсь сделать неориентированный граф из списка смежности, чтобы попрактиковаться в алгоритме Min Cut Каргера. Ниже приведен мой код

class Vertex(object):
    '''Represents a vertex, with the indices of edges
       incident on it'''
    def __init__(self,name,edgeIndices=[]):
        self.name = name
        self.edgeIndices = edgeIndices
    def getName(self):
        return self.name
    def addEdge(self,ind):
        self.edgeIndices.append(ind)
    def getEdges(self):
        return self.edgeIndices
    def __eq__(self,other):
        return self.name == other.name

class Edge(object):
    '''Represents an edge with the indices of its endpoints''' 
    def __init__(self,ends):
        self.ends = ends
    def getEnds(self):
        return self.ends
    def __eq__(self,other):
        return (self.ends == other.ends)\
               or ((self.ends[1],self.ends[0]) == other.ends)

class Graph(object):
    def __init__(self,vertices,edges):
        self.edges = edges
        self.vertices = vertices

def createGraph(filename):
    '''Input: Adjacency list
       Output: Graph object'''
    vertices = []
    edges = []
    with open(filename) as f:
        for line in f:
            elements = line.split()
            newVert = Vertex(elements[0])
            if newVert not in vertices:
                vertices.append(newVert)

            for verts in elements[1:]:
                otherVert = Vertex(verts)
                if otherVert not in vertices:
                    vertices.append(otherVert)
                end1 = vertices.index(newVert)
                end2 = vertices.index(otherVert)
                newEdge = Edge((end1,end2))
                if newEdge not in edges:
                    edges.append(newEdge)
                newVert.addEdge(edges.index(newEdge))
    return Graph(vertices,edges)

Предположим, что список смежности выглядит следующим образом с вершинами, представленными целыми числами.

1 -> 2,3,4
2 -> 1,3
3 -> 1,2,4
4 -> 1,3

Всего в этом графе будет пять ребер, поэтому длина списка, содержащего индексы ребер, с которыми связана вершина, не может превышать 5.

Например, я ожидаю, что вершина «2» будет иметь индексы только двух ребер, то есть ребер с вершинами 1 и 3. Вместо этого я получаю [0, 1, 2, 3, 0, 2, 1, 3]. Мне нужна помощь, чтобы понять, что происходит не так.


person Effective_cellist    schedule 29.07.2016    source источник


Ответы (2)


Первая ошибка исходит от Vertex init. При передаче списка в качестве аргумента по умолчанию Python создает его экземпляр один раз и делится этим экземпляром со всеми будущими экземплярами Vertex. Передайте None и используйте локальный список, если список не указан.

class Vertex(object):
    def __init__(self,name,edgeIndices=None):
        self.name = name
        self.edgeIndices = edgeIndices if edgeIndices else []

В методе createGraph, когда вершина уже существует в графе, вам нужно ее использовать. См. добавленный else: newVert = ... У вас также, кажется, есть проблема с разделением линий. См. итерацию над elements[2].split(',').

def createGraph(filename):
    '''Input: Adjacency list
       Output: Graph object'''
    vertices = []
    edges = []
    with open(filename) as f:
        for line in f:
            elements = line.split()
            newVert = Vertex(elements[0])
            if newVert not in vertices:
                vertices.append(newVert)
            else:
                newVert = vertices[vertices.index(newVert)]

            for verts in elements[2].split(','):
                otherVert = Vertex(verts)
                if otherVert not in vertices:
                    vertices.append(otherVert)
                end1 = vertices.index(newVert)
                end2 = vertices.index(otherVert)
                newEdge = Edge((end1,end2))
                if newEdge not in edges:
                    edges.append(newEdge)
                newVert.addEdge(edges.index(newEdge))
    return Graph(vertices,edges)

В качестве примечания я бы попытался использовать dict для хранения вершин (и ребер) и выполнения поиска. List.index используется много, и вы можете создать много объектов просто так.

person guillaume.deslandes    schedule 29.07.2016
comment
Я сохраняю вершины и ребра в виде массивов в объекте графа, а затем ссылаюсь на эти два массива. Можно ли сделать то же самое с диктовками, так как доступ к ним осуществляется по ключам, более того, записи в диктовке могут быть не в том порядке, в каком мы ввели. - person Effective_cellist; 29.07.2016
comment
Ну, смысл dict был действительно в том, чтобы ускорить загрузку графа и упростить код. Dict и ключи пригодятся, если у вас есть несмежные метки для ваших вершин и вам нужно сохранить какое-то сопоставление. - person guillaume.deslandes; 01.08.2016
comment
Если вы планируете выполнять тяжелые вычисления со своим графом, массивы дадут вам более быстрый доступ и в конечном итоге будут лучше. Если ваши вершины помечены от 1 до n, как в файле, я бы заполнил массив (n+1) вершинами в отсортированном порядке, а затем добавил ребра. Или пометьте вершины от 0 до (n-1). Поскольку вы используете индекс, а не фактический объект, для создания ребер не нужны фактические вершины, а только их индекс, вычитаемый из его метки. - person guillaume.deslandes; 01.08.2016

Я бы порекомендовал взглянуть на реализации графов на основе Dict, OrderedDict, Linked List. Они гораздо эффективнее, чем на основе списков и индексов. Чтобы ваш код работал, вы можете сделать следующее:

Измените вершину, чтобы избежать проблемы, описанной в предыдущем ответе:

class Vertex(object):
    def __init__(self,name, edgeIndices=None):
        self.name = name
        self.edgeIndices = edgeIndices or []     

Пусть график выполняет некоторую работу:

class Graph(object):
    def __init__(self):
        self.edges = []
        self.vertices = []

    def add_vertex(self, name):
        vertex = Vertex(name)
        if vertex not in self.vertices:
            self.vertices.append(vertex)

    def add_edge(self, *names):
        self._add_vertices(names)
        edge = self._add_edge(names)
        self._update_vertices_links(edge, names)

    def get_vertex_index(self, name):
        vertex = Vertex(name)
        return self.vertices.index(vertex)

    def get_vertex(self, name):
        return self.vertices[self.get_vertex_index(name)]

    def _update_vertices_links(self, edge, names):
        for name in names:
            vertex = self.get_vertex(name)
            vertex.addEdge(self.edges.index(edge))

    def _add_edge(self, names):
        edge = Edge((self.get_vertex_index(names[0]), self.get_vertex_index(names[1])))
        if edge not in self.edges:
            self.edges.append(edge)
        return edge

    def _add_vertices(self, names):
        for name in names:
            self.add_vertex(name)

    def __repr__(self):
        return "Vertices: %s\nEdges: %s" % (self.vertices, self.edges)

Создать график:

def createGraph(filename):
    with open(filename) as f:
        graph = Graph()
        for line in f:
            elements = line.strip().split()
            graph.add_vertex(elements[0])
            for element in elements[2].split(","):
                graph.add_edge(elements[0], element)
    return graph

Запустить его:

graph = createGraph('input.txt')
print graph

Вывод для вашего ввода:

Vertices: [<Name:1 Edges:[0, 1, 2]>, <Name:2 Edges:[0, 3]>, <Name:3 Edges:[1, 3, 4]>, <Name:4 Edges:[2, 4]>]
Edges: [(0, 1), (0, 2), (0, 3), (1, 2), (2, 3)]
person Andrei Kozlovskiy    schedule 29.07.2016