在 Python 中查找可以开始旅行的起始点数量的程序
旅游季节快要到来了,我们翘首期盼着即将到来的假期。如果你计划要外出旅行,你很可能需要一个程序来帮助你确定可以选择的起始点的数量。本文将介绍如何使用 Python 编写一个简单的程序,用来计算可以开始旅行的起始点的数量。
什么是旅行起点?
在旅行中,我们通常从一个城市出发,而此城市被称为旅游城市的起点。为了使旅行更加方便和轻松,在选择旅游起点时,我们通常希望它满足以下要求:
- 所有其他城市都可以通过某种交通方式到达
- 从起点出发,能够到达所有其他城市
因此寻找可用的起点变得有些棘手。幸运的是,在 Python 中能方便地处理这个问题。
获取数据
首先我们需要获取城市交通信息数据,我们这里使用图论中的邻接表来表示。我们使用以下四个城市作为样例数据。
城市 | 与该城市连通的城市 |
---|---|
北京 | 上海, 天津, 沈阳 |
上海 | 北京, 深圳, 广州 |
天津 | 北京, 沈阳 |
沈阳 | 北京, 天津 |
对于上面的数据,我们可以用一个可以大致预览的 CSV 文件保存。
# Python
import csv
FILEPATH = 'adjacent_cities.csv'
cities = {}
with open(FILEPATH) as data_file:
reader = csv.reader(data_file)
for row in reader:
city, adjacent_cities = row[0], row[1:]
cities[city] = adjacent_cities
这个代码使用了 Python 内置的 csv 模块 来读取包含城市与其相邻城市的 CSV 文件。首先,定义 adjacent_cities.csv
文件路径和一个我们的数据将保存到的字典 cities
。接着,打开 CSV 文件,使用 csv.reader()
来读取文件中的每一行。迭代器返回的行是一个由这列中的字符串组成的列表。
因为我们处理的是行,因此我们仅需简单地分割这个列表。把城市的名字它们的相邻城市存放到 cities
字典,其中城市名是键,相邻城市是值。
这里的 Python 代码主要是为了建立我们要使用的城市连接。在实际生产中,这种数据通常可以从数据库中获取。
寻找起点
如果我们使用图论,寻找起点可以使用深度搜索或广度搜索算法。但是,我们依然不太清楚在这样做时会遇到什么问题,那么我们可以使用 Floyd-Warshall 算法利用矩阵 A 来解决这个问题。
Floyd-Warshall 算法是经典的动态规划算法,用于解决有权图中所有点对之间的最短路径问题。然而,如果我们不关心路径长度,则可以利用它来实现我们所需的起点计数。
使用 Floyd-Warshall 算法的关键是理解路径的基本概念。除了本文中使用的基于矩阵的算法之外,路径的概念还可以导致许多通用算法。
用于 Floyd-Warshall 算法的矩阵 A 的每个元素 a_ij 表示从 i 到 j 的最短路径中存在的中间节点数目。在我们的情况下,如果 a_ij 的值是正无穷的,说明从 i 到 j 之间没有路径。如果 a_ij 的值不是正无穷的,则我们认为城市 j 可以由城市 i 可达。这个算法执行以下三个嵌套循环,以填充矩阵 A:
# Python
INF = float('inf')
num_cities = len(cities) # number of cities in data
# initialize matrix A with 0s for diagonal and INF for non-adjacent cities
A = [[INF] * num_cities for _ in range(num_cities)]
for i in range(num_cities):
A[i][i] = 0 # set cost of i to i as 0
for neighbor in cities[str(i)]:
j = int(neighbor)
A[i][j] = 1 # adjacent cities have cost of 1
for k in range(num_cities):
for i in range(num_cities):
for j in range(num_cities):
A[i][j] = min(A[i][j], A[i][k] + A[k][j])
初始化的矩阵 A 设置将斜对角线(即从每个城市到其他自身的距离)置为零,将非相邻城市之间的路径长度设置为正无穷。接下来,我们将所有连接城市的路径设置为 1,这只表示它们是相邻城市。最后,使用三个嵌套循环来使用 Floyd-Warshall 算法计算矩阵的最短路径。
计算起点数量
现在,我们已经使用 Floyd-Warshall 算法计算了所有城市之间的最短路径。我们将使用这些信息来计算可以选择的起点的数量。
我们需要查找符合以下两个条件的每个城市:
- 它没有任何向外的路径(即没有边出它)。
- 所有其他城市至少要有一个路径可以到达。
根据条件 1,当出现完全由其他城市连通的城市时,这个城市就成为了一个起点。我们可以使用以下代码找到这些城市:
# Python
starting_points = [i for i in range(num_cities) if all(A[i][j] == INF for j in range(num_cities) if i != j) and any(A[j][i] == INF for j in range(num_cities) if i != j)]
print(starting_points)
我们简单地迭代所有城市并检查是否存在向外的边,即 A[i][j] 的值是否为正无穷。如果是,则城市 i 没有与其他任何城市直接相连。此外,我们还要检查是否任何城市可以到达城市 i。如果不存在,它就被添加到起点列表中。
结论
通过将最短路径算法和起点计算组合在一起,我们可以快速地找到我们可以选择的起点的数量,以便我们可以轻松地计划旅行路线。同时,通过使用图论中的一些知识,我们可以在 Python 中实现一个有效的程序,这有助于我们更好地了解建立路径相关应用程序的一些基本编程技术。
完整代码如下:
# Python
import csv
FILEPATH = 'adjacent_cities.csv'
cities = {}
with open(FILEPATH) as data_file:
reader = csv.reader(data_file)
for row in reader:
city, adjacent_cities = row[0], row[1:]
cities[city] = adjacent_cities
INF = float('inf')
num_cities = len(cities) # number of cities in data
# initialize matrix A with 0s for diagonal and INF for non-adjacent cities
A = [[INF] * num_cities for _ in range(num_cities)]
for i in range(num_cities):
A[i][i] = 0 # set cost of i to i as 0
forneighbor in cities[str(i)]:
j = int(neighbor)
A[i][j] = 1 # adjacent cities have cost of 1
for k in range(num_cities):
for i in range(num_cities):
for j in range(num_cities):
A[i][j] = min(A[i][j], A[i][k] + A[k][j])
starting_points = [i for i in range(num_cities) if all(A[i][j] == INF for j in range(num_cities) if i != j) and any(A[j][i] == INF for j in range(num_cities) if i != j)]
print(starting_points)
最后,我们根据环境和需求来定期使用这种类型的程序。因为城市连通性的变化,我们需要频繁地更新数据和运行这个程序,这将确保我们最终选择的旅游起点是最合适的。
希望这篇文章对那些计划旅行的 Python 编程者有所帮助。如果您还有其他建议或有更好的解决方案,请在评论区中分享。