繁体   English   中英

Python-检查图形是否已连接-意外的循环行为

[英]Python - checking if graph is connected - unexpected loop behavior

我正在用Python编码图形对象(顶点,边,图)。 我有一个random_graph方法,它获取一个顶点列表,对于每对顶点,它以概率p在它们之间放置一条边。 我有一个is_connected方法,该方法返回是否连接图形。

我想知道一个图将被连接的概率,因为它具有n个顶点并且使用了概率p进行随机生成。 这可以在没有代码的情况下找到,但我还想通过代码通过生成一堆随机图并查看有多少个随机图来通过实验来计算。

一次执行所有操作似乎一切正常:创建一个随机图,然后检查它是否已连接。 例如,我以0.95的概率一遍又一遍地运行文件,每次它告诉我图形都已连接时,它的概率很高。 问题在于, 当我将相同的代码放入循环中多次运行时,行为会发生变化 处于循环中时,(0索引)第3和第9迭代打印出该图形未连接。

以下是我的所有代码,包括两种形式的play.py (非循环有效,循环产生怪异行为):

*****
play.py (non-loop, works)
*****

from vertex import SimpleVertex
from edge import SimpleEdge
from graph import SimpleGraph


vertices = []
for i in xrange(10):
    vertices.append(SimpleVertex('v'+str(i), i))

g = SimpleGraph.random_graph(vertices, p=1)

res = g.breadth_first_search(vertices[0])

print 'g is connected:'
print g.is_connected()


*****
play.py (loop, weird behavior)
*****

from vertex import SimpleVertex
from edge import SimpleEdge
from graph import SimpleGraph


for j in range(10):

    vertices = []
    for i in xrange(10):
        vertices.append(SimpleVertex('v'+str(i), i))

    g = SimpleGraph.random_graph(vertices, p=1)

    res = g.breadth_first_search(vertices[0])

    print 'g is connected:'
    print g.is_connected()


*****
edge.py
*****

class SimpleEdge(object):
    """
    Base class for edges of graphs with no concept of direction
    """
    def __init__(self, v1, v2):
        if v1 == v2:
            raise Exception("Cannot make edge from " + str(v1) + " to itself.")
        self.vertices = {v1, v2}

    def __str__(self):
        vertices = list(self.vertices)
        return "Edge(%s, %s)" % (vertices[0], vertices[1])

    def __eq__(self, other):
        return self.vertices == other.vertices


*****
vertex.py
*****

from edge import SimpleEdge


class SimpleVertex(object):
    """
    Base class for vertices of graphs with no concept of direction
    """
    def __init__(self, name='', vid=None):
        self.name = name
        self.vid = vid if vid is not None else id(self)
        self.edges = []

    def __str__(self):
        display = self.name if self.name else self.vid
        return "Vertex(%s)" % display

    def __eq__(self, other):
        return self.vid == other.vid

    def has_edge(self, edge):
        """ Checks if a certain edge already exists on this vertex """
        return edge in self.edges

    def add_edge(self, other):
        """ Adds an edge from this vertex to another vertex """
        edge = SimpleEdge(self, other)

        if self.has_edge(edge) or other.has_edge(edge):
            raise Exception(str(edge) + " already exists.")

        self.edges.append(edge)
        other.edges.append(edge)
        return edge

    def neighbors(self):
        """ List of vertices adjacent to this vertex """
        neighbors = []
        for edge in self.edges:
            vertices = edge.vertices
            for vertex in vertices:
                if vertex != self:
                    neighbors.append(vertex)
        return neighbors

    def degree(self):
        """ Number of neighbors this vertex has """
        return len(self.neighbors())

    def breadth_first_search(self, goal=None):
        """ Beginning at this vertex, perform breadth-first search to find the
            distance, in edges, to either some goal vertex or all vertices
            reachable from this vertex """
        vertex_queue = [(self, 0)]
        seen_so_far = set([self])
        dists = {}

        # handle each vertex until there are no vertices left to check
        while vertex_queue:
            next_vertex, current_dist = vertex_queue.pop(0)

            # if searching for a specific vertex, check if this is it
            if goal is not None and next_vertex == goal:
                return current_dist

            # if this is the first time seeing this vertex, store its dist
            if next_vertex not in dists:
                dists[next_vertex] = current_dist

            # put this vertex's neighbors onto the back of the queue
            new_dist = current_dist + 1
            for n in next_vertex.neighbors():
                if n not in seen_so_far:
                    vertex_queue.append((n, new_dist))
                    seen_so_far.add(n)

        # if searching for a specific vertex, it was not reachable
        if goal is not None:
            return -1
        return dists


*****
graph.py
*****

from edge import SimpleEdge, DirectedEdge

import random


class SimpleGraph(object):
    """ Base class for graphs with no concept of direction """
    def __init__(self):
        self.vertices = set()
        self.edges = set()

    def __str__(self):
        vertices_str = ", ".join([str(vertex) for vertex in self.vertices])
        edges_str = ", ".join([str(edge) for edge in self.edges])
        return "Vertices: %s\nEdges: %s" % (vertices_str, edges_str)

    def num_vertices(self):
        """ Number of vertices in this graph """
        return len(self.vertices)

    def num_edges(self):
        """ Number of edges in this graph """
        return len(self.edges)

    def has_vertex(self, vertex):
        """ Checks if a certain vertex already exists in this graph """
        return vertex in self.vertices

    def has_edge(self, edge):
        """ Checks if a certain edge already exists in this graph """
        return edge in self.edges

    def add_vertex(self, vertex):
        """ Adds a vertex to this graph """
        if vertex.degree():
            raise Exception(str(vertex) + " has neighbors.")

        if self.has_vertex(vertex):
            raise Exception(str(vertex) + " already exists.")

        self.vertices.add(vertex)
        return vertex

    def add_edge(self, v1, v2):
        """ Adds an edge between two vertices in this graph """
        edge = SimpleEdge(v1, v2)
        if self.has_edge(edge):
            raise Exception(str(edge) + " already exists.")

        self.edges.add(v1.add_edge(v2))
        return edge

    def average_degree(self):
        """ Average number of neighbors vertices in this graph have """
        if not self.num_vertices:
            return 0
        return 2.0 * self.num_edges() / self.num_vertices()

    @classmethod
    def random_graph(cls, vertices, p=0.5):
        """ Generate a graph using a set of vertices where each pair of vertices
            has some probability of having an edge between them """
        graph = cls()
        for v1 in vertices:
            graph.add_vertex(v1)
            for v2 in graph.vertices:
                if v1 > v2 and random.random() < p:
                    graph.add_edge(v1, v2)
        return graph

    @classmethod
    def complete_graph(cls, vertices):
        """ Generate a graph with all possible edges using a set of vertices """
        return cls.random_graph(vertices, p=1.0)

    def breadth_first_search(self, start, goal=None):
        """ Beginning at some vertex, perform breadth-first search to find the
            distance, in edges, to either some goal vertex or all vertices """
        # perform breadth-frist search starting from the specified vertex
        result = start.breadth_first_search(goal)
        if goal is not None:
            return result
        # for all the unreachable vertices, add them to the result
        for vertex in self.vertices:
            if vertex not in result:
                result[vertex] = -1
        return result

    def is_connected(self):
        """ Checks if this graph has a path from any vertex to any other
            vertex """
        dists = self.breadth_first_search(list(self.vertices)[0])
        return all([dists[dist] >= 0 for dist in dists])

您在SimpleGraph.random_graph中对v1和v2的比较是根据类的ID进行比较,这可能不是您想要的。 在SimpleVertex中添加以下方法可以解决此问题:

def __lt__(self, other):
    return self.vid < other.vid

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM