From c21dfa63ef7755029e2006ae2f4b454ad88bce1a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BE=A1=E5=9D=82=E6=98=B4?= Date: Thu, 3 Apr 2025 18:28:09 +0800 Subject: [PATCH] done: homework3 --- .../.idea/MarsCodeWorkspaceAppSettings.xml | 6 + homework3/.idea/homework3.iml | 8 + .../inspectionProfiles/profiles_settings.xml | 6 + homework3/.idea/misc.xml | 6 + homework3/.idea/modules.xml | 8 + homework3/.idea/vcs.xml | 4 +- homework3/main.py | 183 ++++++++++++++++++ 7 files changed, 220 insertions(+), 1 deletion(-) create mode 100644 homework3/.idea/MarsCodeWorkspaceAppSettings.xml create mode 100644 homework3/.idea/homework3.iml create mode 100644 homework3/.idea/inspectionProfiles/profiles_settings.xml create mode 100644 homework3/.idea/misc.xml create mode 100644 homework3/.idea/modules.xml diff --git a/homework3/.idea/MarsCodeWorkspaceAppSettings.xml b/homework3/.idea/MarsCodeWorkspaceAppSettings.xml new file mode 100644 index 0000000..05ed8ba --- /dev/null +++ b/homework3/.idea/MarsCodeWorkspaceAppSettings.xml @@ -0,0 +1,6 @@ + + + + + \ No newline at end of file diff --git a/homework3/.idea/homework3.iml b/homework3/.idea/homework3.iml new file mode 100644 index 0000000..2051088 --- /dev/null +++ b/homework3/.idea/homework3.iml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/homework3/.idea/inspectionProfiles/profiles_settings.xml b/homework3/.idea/inspectionProfiles/profiles_settings.xml new file mode 100644 index 0000000..105ce2d --- /dev/null +++ b/homework3/.idea/inspectionProfiles/profiles_settings.xml @@ -0,0 +1,6 @@ + + + + \ No newline at end of file diff --git a/homework3/.idea/misc.xml b/homework3/.idea/misc.xml new file mode 100644 index 0000000..6de7068 --- /dev/null +++ b/homework3/.idea/misc.xml @@ -0,0 +1,6 @@ + + + + + \ No newline at end of file diff --git a/homework3/.idea/modules.xml b/homework3/.idea/modules.xml new file mode 100644 index 0000000..886212e --- /dev/null +++ b/homework3/.idea/modules.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/homework3/.idea/vcs.xml b/homework3/.idea/vcs.xml index d843f34..6c0b863 100644 --- a/homework3/.idea/vcs.xml +++ b/homework3/.idea/vcs.xml @@ -1,4 +1,6 @@ - + + + \ No newline at end of file diff --git a/homework3/main.py b/homework3/main.py index e69de29..e22c21c 100644 --- a/homework3/main.py +++ b/homework3/main.py @@ -0,0 +1,183 @@ +import numpy as np + +class State: + def __init__(self, state, directionFlag=None, parent=None, f=0): + """八数码问题状态类 + Args: + state: 3x3 numpy数组,表示当前状态 + directionFlag: 禁止的移动方向(用于防止走回头路) + parent: 父状态节点 + f: 评估函数值(f = g + h) + """ + self.state = state # 当前状态矩阵 + self.direction = ['up', 'down', 'right', 'left'] # 可移动方向 + if directionFlag: # 移除来源方向防止回退 + self.direction.remove(directionFlag) + self.parent = parent # 父节点指针 + self.f = f # 评估函数值 + + def getDirection(self): + """获取当前允许的移动方向列表""" + return self.direction + + def setF(self, f): + """设置评估函数值""" + self.f = f + return + + # 打印结果 + def showInfo(self): + """打印当前状态矩阵""" + for i in range(len(self.state)): + for j in range(len(self.state)): + print(self.state[i, j], end=' ') + print("\n") + print('->') + return + + # 获取0点 + def getZeroPos(self): + """获取空白格(0)的位置坐标 + Returns: + tuple: 包含空白格的行列坐标(x, y) + """ + postion = np.where(self.state == 0) + return postion + + # 曼哈顿距离 f = g + h,g=1,如果用宽度优先的评估函数可以不调用该函数 + def getFunctionValue(self): + """计算曼哈顿距离评估值 + Returns: + int: 当前状态到目标状态的评估值(f = 移动步数 + 启发函数值) + """ + cur_node = self.state.copy() + fin_node = self.answer.copy() + dist = 0 # 曼哈顿距离总和 + N = len(cur_node) # 矩阵维度(3) + + # 遍历每个格子计算距离 + for i in range(N): + for j in range(N): + if cur_node[i][j] != fin_node[i][j]: + index = np.argwhere(fin_node == cur_node[i][j]) # 找到对应数字在目标状态的位置 + x = index[0][0] # 目标x坐标 + y = index[0][1] # 目标y坐标 + dist += (abs(x - i) + abs(y - j)) # 累加曼哈顿距离 + return dist + 1 # 总距离+当前步数(g值固定为1) + + def nextStep(self): + """生成所有可能的下一个合法状态 + Returns: + State: 评估函数值最小的下一个状态 + """ + if not self.direction: # 无可用移动方向 + return [] + subStates = [] # 子状态列表 + boarder = len(self.state) - 1 # 矩阵边界索引 + + # 获取0点位置 + x, y = self.getZeroPos() + x, y = x[0], y[0] # 转换为标量值 + + # 向左移动 + if 'left' in self.direction and y > 0: + s = self.state.copy() + tmp = s[x, y - 1] + s[x, y - 1] = s[x, y] + s[x, y] = tmp + news = State(s, directionFlag='right', parent=self) + news.setF(news.getFunctionValue()) + subStates.append(news) + + # 向上移动 + if 'up' in self.direction and x > 0: + s = self.state.copy() + tmp = s[x - 1, y] + s[x - 1, y] = s[x, y] + s[x, y] = tmp + news = State(s, directionFlag='down', parent=self) + news.setF(news.getFunctionValue()) + subStates.append(news) + + # 向下移动 + if 'down' in self.direction and x < boarder: + s = self.state.copy() + tmp = s[x + 1, y] + s[x + 1, y] = s[x, y] + s[x, y] = tmp + news = State(s, directionFlag='up', parent=self) + news.setF(news.getFunctionValue()) + subStates.append(news) + + # 向右移动 + if 'right' in self.direction and y < boarder: + s = self.state.copy() + tmp = s[x, y + 1] + s[x, y + 1] = s[x, y] + s[x, y] = tmp + news = State(s, directionFlag='left', parent=self) + news.setF(news.getFunctionValue()) + subStates.append(news) + + # 返回F值最小的下一个点 + subStates.sort(key=compareNum) + return subStates[0] + + # A* 迭代 + def solve(self): + """执行A*算法求解路径 + Returns: + list: 从初始状态到目标状态的路径节点列表 + """ + # openList(待访问节点列表) + openTable = [] + # closeList(已访问节点列表) + closeTable = [] + openTable.append(self) # 初始节点加入open表 + + while len(openTable) > 0: # 当存在待访问节点时 + # 取出评估值最小的节点 + n = openTable.pop(0) + # 加入已访问列表 + closeTable.append(n) + # 生成子节点 + subStates = n.nextStep() + path = [] + + # 判断是否到达目标状态 + if (subStates.state == subStates.answer).all(): + # 回溯路径 + while subStates.parent and subStates.parent != originState: + path.append(subStates.parent) + subStates = subStates.parent + path.reverse() # 反转得到正序路径 + return path + + # 将子节点加入open表 + openTable.append(subStates) + else: + return None, None + +def compareNum(state): + """状态比较函数(用于排序)""" + return state.f + +if __name__ == '__main__': + # 初始化起始状态(0代表空格) + originState = State(np.array([[1, 5, 3], [2, 4, 6], [7, 0, 8]])) + # 设置类属性:目标状态 + State.answer = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 0]]) + + # 创建初始状态对象 + s1 = State(state=originState.state) + # 求解路径 + path = s1.solve() + + # 输出结果 + if path: + print("解决方案路径:") + for node in path: + node.showInfo() + print("目标状态:") + print(State.answer) + print("总步数:%d" % len(path))