CSCI 1100 - 作业5 - 嵌套列表、网格和路径规划
作业概述
本次作业总分值 100 分,将计入你的总作业成绩。截止日期为 2024 年 2 月 29 日星期四晚上 11:59:59。和往常一样,作业评分由自动评分、讲师测试用例和助教评分三部分组成。作业分为两个部分,需分别提交。两部分都应在截止日期前提交,否则将被视为逾期。
关于评分标准和过度合作的定义,请参阅提交指南和合作政策。这些规则将在本学期剩余时间内执行。
你将需要我们在 hw5_files.zip
中提供的数据文件,请务必从 Submitty 的课程资料部分下载并解压到你的作业 5 目录。压缩包内包含实用代码、数据文件以及程序的示例输入/输出。
问题描述
计算机科学和工程领域的许多问题都是在二维数值网格上求解的,常用技术包括"梯度上升(或下降)"、贪心搜索或爬山算法。我们将使用手工生成的高程数据来研究其简化版本。
我们主要使用的表示方法是"高度"(也称"海拔",这里简称"高度")的嵌套列表。例如:
|
|
这个网格包含四个长度为六的整数列表。每个元素表示一个高度值,如海拔高度,以固定间隔测量,间隔可小至厘米,也可大至公里。美国地质调查局(USGS)和一些私营公司都在维护和分发此类高程数据。这些数据在评估径流、确定风力涡轮机位置以及规划道路和建设等方面都很重要。我们将以规划徒步路线为例来研究这个问题。
对于这些数据,我们可能会问以下问题:
- 最高点(最大高度)在哪里?这也称为"全局最大值",因为它是数据中的最大值。在上例中,最大高度为 23,出现在第 1 行第 2 列(简写为 (1, 2),假设第一个值表示行,第二个值表示列)。我们称 (1, 2) 为一个"位置"。
- 数据中是否存在"局部最大值"?即高度大于周围值但小于全局最大值的点。在上例中,位置 (2, 4) 处的 22 就是一个局部最大值。
- 从给定位置出发,到达全局最大值的最佳路径是什么?这是一个棘手的问题,因为我们需要定义"最佳路径"。一个简单的定义是:能否从给定位置出发,只走最陡的路线到达全局最大值(是否能徒步到"山顶")?例如,从 (3, 0) 出发,经过 (3, 0)、(3, 1)、(3, 2)、(2, 2)、(1, 2) 的路径是最陡的,可以到达顶峰。这是一种"梯度上升"方法。但如果从 (3, 5) 出发,会生成路径 (3, 5)、(3, 4)、(2, 4),之后就无法继续向上到达全局最大值。
我们还可以提出并回答更多问题。有些问题很容易解决,有些则需要复杂的算法和大量计算。
在开始实际作业之前,我们需要定义网格中"相邻"位置的概念,即从给定位置允许走到的位置。在本作业中,从位置 (r, c) 出发,相邻位置包括 (r-1, c)、(r, c-1)、(r, c+1) 和 (r+1, c)。
换句话说,相邻位置必须与当前位置在同一行或同一列。此外,相邻位置不能超出网格边界。例如,对于位置 (r, 0),只有 (r-1, 0)、(r, 1)、(r+1, 0) 是允许的相邻位置。
入门指南
请下载 hw5_files.zip
并将所有文件放在你要编写解决方案的同一目录下。hw5_util.py
和 hw5_grids.txt
这两个文件非常重要:hw5_util.py
包含从 hw5_grids.txt
读取网格和起始位置的实用函数。具体来说:
hw5_util.num_grids()
返回数据中不同网格的数量。hw5_util.get_grid(n)
返回第 n 个网格,其中 n==1 表示第一个网格,n ==hw5_util.num_grids()
表示最后一个网格。hw5_util.get_start_locations(n)
返回一个元组列表,给出第 n 个网格要考虑的一个或多个起始位置。hw5_util.get_path(n)
返回一个元组列表,给出第 n 个网格的一条可能路径。
建议你先试用这些函数并打印结果,以确保你理解它们的功能。
你可以对数据做如下假设:
- 网格至少有两行。
- 每行至少有两个元素(列),且每行的列数相同。
- 所有高度值都是正整数。
- 起始位置都在网格的行列范围内。
- 路径上的位置都在网格的行列范围内。
第一部分
编写一个 Python 程序 hw5_part1.py
,完成以下任务:
-
询问用户要使用的网格编号,并循环询问直到得到正确范围内的编号。将网格编号记为 n。
-
获取第 n 个网格。
-
询问用户是否要打印网格。如果用户输入单个字符 ‘Y’ 或 ‘y’,则打印网格,否则不打印。打印时可假设高度值小于 1000 米。参考示例输出。
-
获取与第 n 个网格关联的起始位置,对于每个起始位置,打印其在网格边界内的相邻位置。例如,如果第 n 个网格有 8 行 10 列,起始位置列表为
[(4, 6), (0, 3), (7, 9)]
,则输出应为:1 2 3
Neighbors of (4, 6): (3, 6) (4, 5) (4, 7) (5, 6) Neighbors of (0, 3): (0, 2) (0, 4) (1, 3) Neighbors of (7, 9): (6, 9) (7, 8)
非常重要:我们强烈建议你编写一个名为
get_nbrs
的函数,它接受行、列位置以及网格的行数和列数作为参数,返回一个元组列表,包含给定位置在网格边界内的所有相邻位置。你会经常用到这个函数。 -
获取建议路径,判断它是否有效(每个位置都与下一个位置相邻),然后计算总下降高度和总上升高度。例如,对于上面的网格,如果路径为
(3, 1), (3, 0), (2, 0), (1, 0), (1, 1), (0, 1), (0, 2), (1, 2)
,则下降高度为从 (3, 1) 到 (3, 0)(变化 4)和从 (1, 1) 到 (0, 1)(变化 3),总计 7;上升高度为从 (3, 0) 到 (2, 0)、从 (2, 0) 到 (1, 0)、从 (1, 0) 到 (1, 1)、从 (0, 1) 到 (0, 2) 以及从 (0, 2) 到 (1, 2),总计 (2 + 1 + 6 + 2 + 5) = 16。输出应为:
|
|
如果路径无效,代码应输出 路径:从 point1 到 point2 的无效步骤。
其中 point1 和 point2 是表示无效步骤起点和终点的元组。
只提交文件 hw5_part1.py
,不要提交其他任何内容。
第二部分
修改第一部分的解决方案,并将其提交为 hw5_part2.py
。程序应再次要求用户输入网格编号,但不应打印网格。接下来,它应找到并输出全局最大高度的位置和高度值。例如,对于上面的简单网格,输出应为 全局最大值:(1, 2) 23
。你可以不加验证地假设全局最大值是唯一的。
第二部分的主要任务是为网格的每个起始位置找到并输出两条路径。第一条是最陡峭的上升路径,第二条是最平缓的上升路径。每条路径上的步骤必须在相邻位置之间,与第一部分相同。此外,在每条路径上,不允许走到高度相同或更低的位置,并且步长(新位置与当前位置的高度差)不能超过最大步高(由用户输入)。
接下来,确定每条路径是到达了网格中全局最大高度的位置、局部最大值还是都不是。如果当前位置的唯一上升步骤相对于当前高度太高,就可能出现后一种情况。当然,真正的徒步路径可以上下起伏,但在这种更复杂的情况下寻找"最优路径"需要我们尚未准备好开发的更复杂算法。
以上面的网格为例:
|
|
从位置 (3, 0) 出发,最大步高为 4,最陡峭的路径是 (3, 0)、(3, 1)、(3, 2)、(2, 2)、(2, 3)、(1, 3)、(1, 2),最平缓的路径是 (3, 0)、(2, 0)、(1, 0)、(0, 0)、(0, 1)、(0, 2)、(0, 3)、(1, 3)、(1, 2)。两条路径都到达了全局最大值,并且在第一次接近时都避免了直接走到全局最大值,因为步高太大。注意,从位置 (3, 5) 出发的最陡峭和最平缓路径都将在局部最大值 (2, 4) 处结束。最陡峭的路径将在 4 步后结束(路径上有 5 个位置),最平缓的路径将在 6 步后结束(路径上有 7 个位置)。如果最大步高只有 3,那么从 (3, 5) 出发的两条路径都将在到达任何最大值之前的位置 (3, 4) 处停止。
路径输出时每行 5 个位置,例如:
|
|
更多细节请参考示例输出。
最后,如果用户请求,输出一个"路径网格",在每个位置给出包含该位置的路径数量。这可以通过创建一个新的嵌套列表来处理,其中每个元素表示一个计数,初始化为 0。对于每条路径和路径上的每个位置 (i, j),应增加嵌套列表中相应的计数。最后,在生成所有路径并将其添加到计数后,输出网格。在此输出中,对于不在任何路径上的位置,不要输出 0,而是输出 ‘.’,这将使输出更清晰。请参阅示例输出。
注意事项
- 在选择路径的下一个位置时,如果有多个选择,则选择
get_nbrs
函数生成的列表中较早出现的位置。例如,在上面的示例网格中,从高度为 11 的位置 (0, 5) 出发,(0, 4) 和 (1, 5) 的高度都是 12。在这种情况下,(0, 4) 将在get_nbrs
列表中较早出现,因此被选为路径上的下一个位置。 - 在确保其他所有内容都正常工作之前,请不要处理路径网格输出(最后一步)。
- 最平缓和最陡峭的路径都是贪心算法的示例,其中在每一步都做出当前最优选择,而不重新考虑之前的决定。更复杂的算法会考虑某种形式的回溯,即撤销决策并重新考虑替代方案。
支持文件
HW5.zip参考答案
hw5_part1.py
|
|
hw5_part2.py
|
|
相关内容
- CSCI 1100 - 作业 4 - 循环和列表;密码和隔离
- CSCI 1100 - 作业 3 - 循环、元组、列表和条件语句
- CSCI 1100 - 作业2 - 字符串和函数
- CSCI 1100 - 作业 1 - 计算与字符串处理
- 使用 ChatGPT 轻松创建选择题