[英]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.