目录
前言
文中涉及的关于最优路径以及相关算法的讨论,仅限静态下以距离作为优先考量的有向图、无向图或混合图的最优路径求解,不考虑时序相关性。时序相关或状态转移模型可参考图概率模型,诸如马尔可夫链、隐马尔科夫模型、条件随机场等。两者的主要区别在于后者求解的是随机过程中发生概率最大的时序转移路径。
概念
图 (Graph) 是一种表示对象之间关系的数据结构,一个图由多个顶点,以及顶点之间的边构成。如果图中所有的边都是无向的 (即双向),则这个图称为无向图;如果所有的边都是有向的 (可以理解为单向),则这个图称为有向图;如果既有无向也有有向的边,则称为混合图。路径是交替的顶点和边构成的序列,表示从一个顶点转移至另一个顶点所经过的顶点和边。以下是有向图的示例:
准备工作
为运行算法,我们统一构建一套有向图案例,一套无向图案例,以及一套混合图案例。考虑到代码简洁性的需求,不构建图和边的对象。案例中,每个顶点包含ID、邻接点、邻接边长三种信息。
import re
class Vertex(object):
def __init__(self,ID):
self.id = ID #顶点ID
self.neighbors = [] #邻点对象
self.edges = [] #与邻点相连的边长度
def gen_graphs():
sample = "(ID,Neighbors,Edge)(0,[1],[4])(1,[2,4,8],[12,10,8])(2,[3],[10])(3,[2,4,7],[10,12,6])(4,[7],[10])(5,[6],[12])(6,[11],[20])(7,[11],[10])(8,[1,4,10],[18,12,6])(9,[6,14],[10,10])(10,[5],[16])(11,[7,12],[10,4])(12,[9,14],[12,4])(13,[12,5],[6,22])(14,[10,15],[10,6])(15,[None],[None])"
directed = [Vertex(i) for i in range(16)] #构建有向图
undirected = [Vertex(i) for i in range(16)] #构建无向图
mixed = [Vertex(i) for i in range(16)] #构建混合图
vertices_info = re.findall(r'([\w\[\],]+)',sample)[1:]
for vertex_info in vertices_info: #遍历每一个顶点
ID = int(re.findall(r'[\w]+',vertex_info)[0]) #读取ID
neighbors = eval(re.findall(r'\[[\w,]+\]',vertex_info)[0]) #读取邻点
directed[ID].neighbors = [directed[i] for i in neighbors if i is not None]
undirected[ID].neighbors = [undirected[i] for i in neighbors if i is not None]
mixed[ID].neighbors = [mixed[i] for i in neighbors if i is not None]
edges = eval(re.findall(r'\[[\w,]+\]',vertex_info)[1]) #读取边
directed[ID].edges = [i for i in edges if i is not None]
undirected[ID].edges = [i for i in edges if i is not None]
mixed[ID].edges = [i for i in edges if i is not None]
for vertex in directed: #构建有向非循环图
for i in range(len(vertex.neighbors)-1,-1,-1):
neighbor = vertex.neighbors[i]
if neighbor.id < vertex.id:
del vertex.neighbors[i]
del vertex.edges[i]
for i in range(len(mixed)): #补充无向图对象缺失的边
vertex = mixed[i]
for j in range(len(vertex.neighbors)):
neighbor = vertex.neighbors[j]
if vertex not in neighbor.neighbors:
undirected[i].neighbors[j].neighbors.append(undirected[i])
undirected[i].neighbors[j].edges.append(undirected[i].edges[j])
return directed, undirected, mixed
directed, undirected, mixed = gen_graphs()
构建好案例后,使用代码对无向图案例进行简单的可视化,生成图像和代码如下:
import math
import matplotlib.pyplot as plt
#import seaborn as sns
#sns.set(color_codes=True)
plt.figure(figsize=(7,7))
Graph = undirected
for i in range(len(Graph)):
x, y = math.cos(i/16*2*math.pi),math.sin(i/16*2*math.pi)
plt.scatter([x],[y],s=18,c='black',label=str(i))
plt.annotate(str(i),
xy=(x, y),
xycoords='data',
xytext=(+5, +5),
textcoords='offset points',
fontsize=12)
for j in range(len(Graph[i].neighbors)):
if Graph[i].neighbors[j].id < Graph[i].id: continue
x2, y2 = math.cos(Graph[i].neighbors[j].id/16*2*math.pi),math.sin(Graph[i].neighbors[j].id/16*2*math.pi)
plt.plot([x,x2],
[y,y2],
linewidth=1,color='gray')
plt.annotate('(%d)'%Graph[i].edges[j],
xy=((x+x2)/2, (y+y2)/2</