请用python语言、A*算法求解八数码问题,要求启发函数使用不在为数、将牌“不在位”的距离和两种方法,输出初始状态、目标状态、最优解、扩展节点数(不包括叶子节点)、生成节点数(包含叶子节点)、迭代次数。其中要求最优解输出完整的状态变换过程,同时state不能出现不可hash的错误。
时间: 2023-05-31 12:04:44 浏览: 125
由于题目中要求使用A*算法,我们需要定义一个估价函数(启发函数)来衡量当前节点到达目标节点的距离。在八数码问题中,我们可以使用不在位的牌数或不在位的牌的距离作为启发函数。
我们可以定义一个Node类来表示每个搜索节点,包括当前状态、父节点、深度、估价函数值等信息。我们使用一个set类型的closedList来记录已经搜索过的节点,使用一个优先队列openList(使用heapq实现)来记录待扩展的节点,按照节点的估价函数值从小到大排序。
以下是代码实现:
```python
import heapq
import copy
class Node:
def __init__(self, state, parent, depth, f, heuristic):
self.state = state
self.parent = parent
self.depth = depth
self.f = f
self.heuristic = heuristic
def __lt__(self, other): # 用于heapq比较大小
return self.f < other.f
def print_path(node):
path = []
while node:
path.append(node.state)
node = node.parent
path.reverse()
for state in path:
print_state(state)
def print_state(state):
print(state[0], state[1], state[2])
print(state[3], state[4], state[5])
print(state[6], state[7], state[8])
print()
def get_zero_index(state):
return state.index(0)
def swap(state, i, j):
new_state = copy.deepcopy(state)
new_state[i], new_state[j] = new_state[j], new_state[i]
return new_state
def get_successors(state):
successors = []
zero_index = get_zero_index(state)
if zero_index != 0 and zero_index != 3 and zero_index != 6: # 交换上面的牌
successors.append(swap(state, zero_index, zero_index - 1))
if zero_index != 2 and zero_index != 5 and zero_index != 8: # 交换下面的牌
successors.append(swap(state, zero_index, zero_index + 1))
if zero_index >= 3: # 交换上面的牌
successors.append(swap(state, zero_index, zero_index - 3))
if zero_index <= 5: # 交换下面的牌
successors.append(swap(state, zero_index, zero_index + 3))
return successors
def count_misplaced_tiles(state, target):
count = 0
for i in range(9):
if state[i] != target[i]:
count += 1
return count
def manhattan_distance(state, target):
distance = 0
for i in range(9):
if state[i] != 0:
distance += abs(i % 3 - target.index(state[i]) % 3) + abs(i // 3 - target.index(state[i]) // 3)
return distance
def astar(start, target, heuristic):
start_node = Node(start, None, 0, 0, heuristic(start, target))
open_list = [start_node]
closed_list = set()
generated = 1
expanded = 0
iterations = 0
while open_list:
iterations += 1
current_node = heapq.heappop(open_list)
if current_node.state == target:
print_path(current_node)
print("Expanded nodes:", expanded)
print("Generated nodes:", generated)
print("Iterations:", iterations)
return
closed_list.add(tuple(current_node.state))
successors = get_successors(current_node.state)
expanded += 1
for successor in successors:
if tuple(successor) not in closed_list:
successor_node = Node(successor, current_node, current_node.depth + 1, 0, heuristic(successor, target))
successor_node.f = successor_node.depth + successor_node.heuristic
heapq.heappush(open_list, successor_node)
generated += 1
print("No solution found")
if __name__ == '__main__':
start_state = [1, 2, 3, 4, 5, 6, 0, 7, 8]
target_state = [1, 2, 3, 4, 5, 6, 7, 8, 0]
print("Start state:")
print_state(start_state)
print("Target state:")
print_state(target_state)
print("Misplaced tiles heuristic:")
astar(start_state, target_state, count_misplaced_tiles)
print("Manhattan distance heuristic:")
astar(start_state, target_state, manhattan_distance)
```
我们可以先定义一个print_state函数来输出状态,然后定义一个get_zero_index函数来获取0的位置,以及一个swap函数用于交换两个位置的数。
接着定义一个get_successors函数来获取当前状态的所有后继节点,即所有可以通过一次交换0和相邻的数得到的状态。我们可以先获取0的位置,然后判断0在哪一行哪一列,然后分别判断是否可以交换上下左右的数。
接下来定义两个启发函数:count_misplaced_tiles和manhattan_distance。其中count_misplaced_tiles返回当前状态和目标状态中不在位的牌的数量,manhattan_distance返回当前状态到目标状态的曼哈顿距离。
然后定义一个Node类,用于表示每个搜索节点。其中state表示当前状态,parent表示父节点,depth表示深度,f表示f值,heuristic表示估价函数值。
最后定义一个astar函数来实现A*算法。我们使用一个set类型的closed_list来记录已经搜索过的节点,使用一个优先队列open_list(使用heapq实现)来记录待扩展的节点,按照节点的估价函数值从小到大排序。在每次循环中,我们从open_list中取出f值最小的节点进行扩展,将其加入closed_list中,并获取其所有后继节点。对于每个后继节点,如果它没有被搜索过,则将其加入open_list中,并更新其f值和估价函数值。最后如果open_list为空,则搜索失败,否则搜索成功。
最后我们在main函数中定义初始状态和目标状态,然后依次使用两个启发函数进行搜索,输出结果。
阅读全文