python 图形优化问题

1,369 阅读6分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第24天,点击查看活动详情

图形优化问题

让我们考虑另一种优化问题。假设您有一个美国每对城市之间所有航空公司航班的价格列表。还假设对于所有城市,A、B 和 c,通过 B 从 A 飞往 c 的成本是从 A 飞往 B 的成本加上从 B 飞往 c 的成本。您可能想问的几个问题是:

·某对城市之间的停靠点最少是多少?

·一对城市之间最便宜的机票价格是多少?

·一对不超过两站的城市之间最便宜的机票价格是多少?

·访问一些城市集合的最便宜的方式是什么?所有这些问题(以及许多其他问题)都可以很容易地形式化为图形问题。

graph92 是一组称为节点(或顶点)的对象,由一组边(或弧)连接。如果边是单向的,则该图形称为有向图或二分图。在有向图中,如果有从 n1 到 n2 的边,我们将 n1 称为源节点或父节点,将 n2 称为目标节点或子节点。

图(或二分图)被称为包含两个节点n1和n2之间的路径,如果有一系列边<eo,...,en>所以e的源。是 n1,en 的目的地是 n2,对于所有边 e,在序列中 en,e 的源;是 ei-1 的目的地。从节点到节点自身的路径称为循环。包含循环的图形称为循环,不包含循环的图形称为非循环。

图形通常用于表示各部分之间存在有趣关系的情况。图在数学中首次被记录使用是在1735年,当时瑞士数学家莱昂哈德·欧拉(Leonhard Euler)使用后来被称为图论的东西来制定和解决柯尼斯堡桥梁问题。

柯尼斯堡,当时的东普鲁士首都,建在两条河流的交汇处,其中包含许多岛屿。这些岛屿通过七座桥梁相互连接并与大陆相连,如图14-6左侧的地图所示。出于某种原因,该市的居民痴迷于是否有可能步行穿过每座桥一次的问题。

image.png

图14-6 柯尼斯堡的桥梁(左)和欧拉的简化地图(右) 欧拉的伟大见解是,通过将每个单独的陆地视为一个点(想想“节点”),并将每座桥梁视为连接其中两个点的一条线(想想“边”),问题可以大大简化。然后,城镇的地图可以用图 14-6 中地图右侧的无向图来表示。欧拉然后理由是,如果步行要遍历每个边一次,那么步行中间的每个节点(即在步行过程中进入和退出的任何岛屿)必须由偶数条边连接。由于此图中没有一个节点具有偶数条边,因此欧拉得出结论,不可能将每个桥仅遍历一次。

比柯尼斯堡桥问题,甚至欧拉定理(将他的解推广到柯尼斯堡桥问题)更有趣的是使用图论来帮助理解问题的整个想法。

例如,只需要对欧拉使用的图形类型进行一个小的扩展,就可以对一个国家的高速公路系统进行建模。如果权重与图形(或双图)中的每个边相关联,则称为加权图。使用加权图,高速公路系统可以表示为一个图形,其中城市由节点表示,连接它们的高速公路表示为边,其中每条边都标有两个节点之间的距离。更一般地说,我们可以通过加权双图来表示任何路线图(包括那些有单行道的路线图)。

类似地,万维网的结构可以表示为一个双连字符,其中节点是从节点A到节点B的边缘的网页,当且仅当A页面上有指向页面B的链接时。

图也有许多不太明显的用途。生物学家使用图形来模拟事物,从蛋白质相互作用的方式到基因表达网络。物理学家使用图形来描述相变。流行病学家使用图表来模拟疾病轨迹。等等。

Eigure 14-7 包含实现与节点、加权边和边相对应的抽象类型的类。

image.png

为节点创建一个类可能看起来有些矫枉过正。毕竟,Node 类中没有一个方法执行任何有趣的计算。我们引入该类只是为了让我们能够灵活地决定(也许在以后的某个时刻)引入具有其他属性的 Node 子类。

图 14-8 包含类“双图”和“图形”类的实现。

image.png

一个重要的决定是选择用于表示双图的数据结构。一种常见的表示形式是 n x n 邻接矩阵,其中 n 是图中的节点数。矩阵的每个单元格都包含有关连接节点对的边的信息(例如,权重)<i,j>。如果边未加权,则每个条目都是 True,当且仅当存在从 i 到 j 的边。

另一种常见的表示形式是邻接列表,我们在这里使用它。类二图有两个实例变量。变量节点是一个 Python 列表,其中包含 Digraph 中节点的名称。节点的连接性使用作为字典实现的邻接列表来表示。变量边缘是一个字典,用于将 Digraph 中的每个节点映射到该节点的子节点列表。类图是双图的子类。它继承了 Digraph 的所有方法,但add_edge除外,它覆盖了该方法。(这不是实现 Graph 的最节省空间的方法,因为它将每个边存储两次,在 Digraph 中的每个方向存储一次。但它具有简单的优点。

你可能想停下来想一想为什么图形是Digraph的子类,而不是相反。在我们看过的许多子类的例子中,子类向超类添加属性。例如,类weighted_edge向类 Edge 添加了权重属性。

在这里,双图和图形具有相同的属性。唯一的区别是添加边方法的实现。通过从另一个继承方法,任何一个都可以很容易地实现,但是选择哪一个来制作超类并不是任意的。在第 10 章中,我们强调了遵守替换原则的重要性:如果客户端代码使用超类型的实例可以正常工作,那么当子类型的实例替换为超类型的实例时,它也应该正常工作。

事实上,如果客户端代码使用 Digraph 实例正常工作,那么如果将图形实例替换为 Digraph 实例,它将正常工作。反之亦然。有许多算法在有向图上工作(通过利用边缘的对称性),而这些算法在有向图上不起作用。