使用Astar(A星)算法解决八数码问题(Python代码)

使用Astar(A星)算法解决八数码问题(附Python代码)

参考文章:A星算法详解(个人认为最详细,最通俗易懂的一个版本)
A*算法作为一种最佳图搜索算法,据说被用于游戏红色警戒的寻路算法。
在这里插入图片描述

八数码问题的重述:

在这里插入图片描述

Astar算法概述

Astar算法本质上是一种图搜索算法。图搜索算法就是将问题空间看作一个有向图空间,这个图空间由一个个图节点构成,问题的求解就转化为在问题空间中从初始节点出发,寻找一条通向目标节点的路径。在八数码问题中,棋局的每一个不同的状态都是一个节点,节点之间的转换是通过移动空格实现的。这样,通过选定初始节点和确定图转换规则(本问题中就是移动空格),我们就可以生成一个状态图,如:
来自算法入门到进阶(三)——搜索技术(八数码问题和状态图搜索)
既然问题已经转化为图搜索问题了,那么我们可以确定一下搜索策略了。Astar算法其实和广度优先搜索很像,它与后者不同的地方就是Astar是一种启发式(heuristic)搜索。关于启发式搜索,简单来说就是在每次广搜选择节点时,从中选出一个最有希望出现在最佳路径上的点进行拓展。
在Astar算法中,我们用一个估价函数f(x)评价节点x距离最优解的距离,f(x) = g(x) + h(x)。
其中g(x)为代价函数,用来评价从初始节点到节点x的已付出的搜索代价(通常由搜索步数决定),g(x)越小代表节点x距离初始节点距离越小,提高了搜索的完备性(鼓励机器去尝试不同路径);而h(x)启发函数用来评价节点x距离目标节点的距离,h(x)越小代表节点x距离目标节点距离越小,提高了搜索的效率(鼓励机器去更快接近目标)。
Astar算法额外要求对所有的节点x均有h(x) <= h*(x),这里h*(x)是从节点x到目标节点的最小启发函数值。这句话理解起来有点困难,笔者在此谈一下自己的看法,有不足处欢迎在评论区指出,欢迎讨论。
我的理解是:要找一个最适合描述当前节点到目标节点距离的h(x)函数
如在八数码问题中,空格只能横向、纵向移动,那么这个h(x)可以是曼哈顿距离。同理,在若在直角坐标系中,这个距离可描述为欧几里得距离。
算法的实现过程如下:
◆ 把起点加入 open list 。

◆ 重复如下过程:

◆ 遍历 open list ,查找 F 值最小的节点,把它作为当前要处理的节点。

◆ 把这个节点移到 close list 。

◆ 对当前方格的 8 个相邻方格的每一个方格?

◆ 如果它是不可抵达的或者它在 close list 中,忽略它。否则,做如下操作。

◆ 如果它不在 open list 中,把它加入 open list ,并且把当前方格设置为它的父亲,记录该方格的 F , G 和 H 值。

◆ 如果它已经在 open list 中,检查这条路径 ( 即经由当前方格到达它那里 ) 是否更好,用 G 值作参考。更小的 G 值表示这是更好的路径。如果是这样,把它的父亲设置为当前方格,并重新计算它的 G 和 F 值。如果你的 open list 是按 F 值排序的话,改变后你可能需要重新排序。

◆ 停止,当你把终点加入到了 open list 中,此时路径已经找到了,或者

◆ 查找终点失败,并且 open list 是空的,此时没有路径。

◆ 保存路径。从终点开始,每个方格沿着父节点移动直至起点,这就是你的路径。
最后附上代码,如果有错拜托评论区指出,转载请注明出处,感谢

import numpy as np


# 定义open表与close表
open_list = []
close_list = []
start_state = np.zeros((3, 3), dtype=int)
target_state = np.zeros((3, 3), dtype=int)


# 定义节点类
class Node:
    G = 0
    H = 0
    F = 0
    state = np.zeros((3, 3), dtype=int)
    parent = []

    # 找num在state状态中的位置x,y
    def find_pos(self, state, num):
        for i in range(len(state)):
            for j in range(len(state[i])):
                if state[i][j] == num:
                    return i, j

    def __init__(self, state, prt=[]):
        self.state = state
        if prt:
            self.parent = prt
            self.G = prt.G + 1
        for i in range(len(state)):
            for j in range(len(state[i])):
                x, y = self.find_pos(target_state, state[i][j])
                self.H = self.H + abs(x - i) + abs(y - j)
        self.F = self.G * 1 + self.H * 10

    def moveto(self, x, y):
        x0, y0 = self.find_pos(self.state, 0)
        # new_state = self.state 是错的
        newstate = (self.state).copy()
        tmp = newstate[x0][y0]
        newstate[x0][y0] = newstate[x][y]
        newstate[x][y] = tmp
        return newstate

# 得到逆序数,用于判断解的存在性
def get_reverse_num(state):
    ans = 0
    s = ""
    for i in range(len(state)):
        for j in range(len(state[i])):
            # 0即空格,不在考虑范围内
            if not state[i][j] == 0:
                s += str(state[i][j])

    for i in range(len(s)):
        for j in range(i):
            if s[j] > s[i]:
                ans += 1
    return ans


# 输出状态及深度
def display(cur_node):
    alist = []
    tmp = cur_node
    while tmp:
        alist.append(tmp)
        tmp = tmp.parent
    alist.reverse()
    for node in alist:
        print("搜索深度%d" % node.G)
        print(node.state)
        print()


# 检查state状态是否在list中(可能是open或close表)
def is_in_list(alist, state):
    for stat in alist:
        if (stat.state == state).all():
            return stat
    return -1


# 排序的权值函数
def delta(node):
    return node.F


# # 输入初始与目标状态
# for i in range(len(start_state)):
#     for j in range(len(start_state[i])):
#         start_state[i][j] = input("start_state"+"("+str(i+1)+","+str(j+1)+"):")
#
# print("the start state is:")
# print(start_state)
#
# for i in range(len(target_state)):
#     for j in range(len(target_state)):
#         target_state[i][j] = input("target_state"+"("+str(i+1)+","+str(j+1)+"):")
#
# print("the target state is:")
# print(target_state)

# 调试
start_state = np.array([[2, 8, 3],
                        [1, 6, 4],
                        [7, 0, 5]])
target_state = np.array([[1, 2, 3],
                         [8, 0, 4],
                         [7, 6, 5]])


# 可行解判断
if get_reverse_num(target_state) % 2 != get_reverse_num(start_state) % 2:
    print(get_reverse_num(target_state))
    print(get_reverse_num(start_state))
    print("找不到可行解!")
    exit(-1)

# 可行解存在时,开始启发搜索
open_list.append(Node(start_state))
while open_list:
    current_node = open_list.pop(0)
    close_list.append(current_node)
    # 当open表中取出的恰好为目标状态时
    if (current_node.state == target_state).all():
        print("可行解已找到!")
        display(current_node)
        exit(0)
    # 否则对当前节点进行拓展
    x, y = current_node.find_pos(current_node.state, 0)
    for [x_, y_] in [[x+1, y], [x-1, y], [x, y+1], [x, y-1]]:
        if 0 <= x_ < len(start_state) and 0 <= y_ < len(start_state):
            new_state = current_node.moveto(x_, y_)
            # 判断新状态是否在close表
            if is_in_list(close_list, new_state) == -1:
                # 如果不在close表
                if is_in_list(open_list, new_state) == -1:
                    # 如果也不在open表
                    open_list.append(Node(new_state, current_node))
                else:
                    # 如果open表中已存在这种状态,则选取G值较小的
                    index = is_in_list(open_list, new_state)
                    if current_node.G + 1 < open_list[index].G:
                        # 如果新路线更好,则放弃旧路线而选择新路线
                        open_list.pop(index)
                        open_list.append(Node(new_state, current_node))
                    # 否则忽略
    # 对open表按F值从小到大进行排序
    open_list.sort(key=delta)

# bug1:if current_node.state == target_state:
# ValueError: The truth value of an array with more than one element is ambiguous. Use a.any() or a.all()

# bug2:start state与new state同步变化
#     def moveto(self, x, y):
#         x0, y0 = self.find_pos(self.state, 0)
#         new_state = self.state
#         tmp = new_state[x0][y0]
#         new_state[x0][y0] = new_state[x][y]
#         new_state[x][y] = tmp
#         return new_state

  • 15
    点赞
  • 80
    收藏
    觉得还不错? 一键收藏
  • 7
    评论
### 回答1: A算法是一种用于解决寻路问题算法,下面是一个使用Python实现避障的示例代码:import queue # 定义坐标点 class Point: def __init__(self, x, y): self.x = x self.y = y # 定义节点 class Node: def __init__(self, point, endPoint, g=0): self.point = point self.father = None self.g = g self.h = (abs(endPoint.x - point.x) + abs(endPoint.y - point.y)) * 10 # 定义A算法 class AStar: def __init__(self, openList, closeList, maze): self.openList = openList self.closeList = closeList self.maze = maze def findMinNode(self): currentNode = self.openList[0] for node in self.openList: if node.g + node.h < currentNode.g + currentNode.h: currentNode = node return currentNode def pointInCloseList(self, point): for node in self.closeList: if node.point.x == point.x and node.point.y == point.y: return True return False def point_is_valid(self, point): if point.x < 0 or point.x >= len(self.maze): return False elif point.y < 0 or point.y >= len(self.maze[0]): return False elif self.maze[point.x][point.y] == 1: return False elif self.pointInCloseList(point): return False return True def findNeighbors(self, minF, endPoint): topPoint = Point(minF.point.x, minF.point.y - 1) leftPoint = Point(minF.point.x - 1, minF.point.y) rightPoint = Point(minF.point.x + 1, minF.point.y) downPoint = Point(minF.point.x, minF.point.y + 1) points = [topPoint, leftPoint, rightPoint, downPoint] resPoints = [] for point in points: if self.point_is_valid(point): resPoints.append(Node(point, endPoint, g=minF.g + 10)) return resPoints def start(self, startPoint, endPoint): startNode = Node(startPoint, endPoint) self.openList.append(startNode) while True: minF = self.findMinNode() self.closeList.append(minF) self.openList.remove(minF) if minF.point.x == endPoint.x and minF.point.y == endPoint.y: return minF neighborList = self.findNeighbors(minF, endPoint) for node in neighborList: node.father = minF self.openList.append(node) # 初始化地图 def initMaze(): maze = [ [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [1, 1, 1, 1, 1, 1, 1, 1, 1, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 1, 1, 1, 1, 1, 1, 1, 1, 1], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [1, 1, 1, 1, 1, 1, 1, 1, 1, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 1, 1, 1, 1, 1, 1, 1, 1, 1], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [1, 1, 1, 1, 1, 1, 1, 1, 1, 0]] return maze # 运行A算法 def run(): openList = [] closeList = [] maze = initMaze() astar = AStar(openList, closeList, maze) startPoint = Point(0, 0) endPoint = Point(9, 9) result = astar.start(startPoint, endPoint) while result: print(result.point.x, result.point.y) result = result.father print('end') if __name__ == '__main__': run() ### 回答2: 下面是一个使用A*算法实现避障的Python代码示例: ```python import heapq # 创建一个节点类表示地图中的每个位置 class Node: def __init__(self, x, y): self.x = x self.y = y self.g = 0 self.h = 0 self.f = 0 self.parent = None # 定义节点之间的比较函数 def __lt__(self, other): return self.f < other.f def Astar(start, end, obstacles): # 定义一个open集合存储待访问的节点 open_set = [] # 定义一个close集合存储已访问过的节点 close_set = set() # 将起点加入open集合 heapq.heappush(open_set, start) while open_set: # 取出open集合中f值最小的节点 current = heapq.heappop(open_set) close_set.add(current) if current == end: # 找到终点,返回路径 path = [] while current: path.append((current.x, current.y)) current = current.parent return path[::-1] # 定义四个邻居节点的偏移量 neighbors = [(-1, 0), (1, 0), (0, -1), (0, 1)] for dx, dy in neighbors: neighbor = Node(current.x + dx, current.y + dy) if neighbor.x < 0 or neighbor.y < 0: continue if neighbor in obstacles or neighbor in close_set: continue neighbor.g = current.g + 1 neighbor.h = abs(neighbor.x - end.x) + abs(neighbor.y - end.y) neighbor.f = neighbor.g + neighbor.h neighbor.parent = current if neighbor in open_set: # 如果邻居节点已经在open集合中,更新其g值和父节点 for node in open_set: if node == neighbor and node.g > neighbor.g: node.g = neighbor.g node.parent = current else: # 如果邻居节点不在open集合中,将其加入open集合 heapq.heappush(open_set, neighbor) # open集合为空,无法找到路径 return None # 示例调用: # 创建起点、终点、障碍物 start = Node(0, 0) end = Node(5, 5) obstacles = {Node(1, 1), Node(2, 2), Node(3, 3)} # 障碍物为坐标(1,1),(2,2),(3,3) # 调用A*算法得到路径 path = Astar(start, end, obstacles) # 输出路径 if path: print("找到路径:") for point in path: print(point) else: print("无法找到路径") ``` 注意:这只是一个简单的示例代码,实际使用时,可能需要根据具体情况进行适当修改。 ### 回答3: A*算法是一种常用于路径搜索和避障规划的算法。它基于图论中的图搜索算法,通过利用启发式函数来评估搜索节点的优先级,以找到最短路径。 下面是一个基于A*算法Python避障代码示例: ```python import heapq # 定义节点类 class Node: def __init__(self, x, y): self.x = x self.y = y self.g_cost = float('inf') self.h_cost = float('inf') self.f_cost = float('inf') self.parent = None def __lt__(self, other): return self.f_cost < other.f_cost # 定义A*算法函数 def a_star(start, end, obstacles): open_list = [] closed_list = [] heapq.heappush(open_list, start) while open_list: current_node = heapq.heappop(open_list) closed_list.append(current_node) if current_node == end: return True for neighbor in get_neighbors(current_node): if neighbor in closed_list or neighbor in obstacles: continue g_cost = current_node.g_cost + distance(current_node, neighbor) # 计算节点的实际代价 if g_cost < neighbor.g_cost: neighbor.g_cost = g_cost neighbor.h_cost = heuristic(neighbor, end) # 估计节点的启发代价 neighbor.f_cost = g_cost + neighbor.h_cost neighbor.parent = current_node if neighbor not in open_list: heapq.heappush(open_list, neighbor) return False # 获取节点相邻的节点 def get_neighbors(node): x, y = node.x, node.y neighbors = [] # 添加上、下、左、右四个相邻节点 for dx, dy in [(0, 1), (0, -1), (1, 0), (-1, 0)]: new_x, new_y = x + dx, y + dy neighbors.append(Node(new_x, new_y)) return neighbors # 计算两个节点之间的距离 def distance(node1, node2): return abs(node1.x - node2.x) + abs(node1.y - node2.y) # 启发式函数,用于估计代价 def heuristic(node, end): return abs(node.x - end.x) + abs(node.y - end.y) # 避障示例 start = Node(0, 0) end = Node(5, 5) obstacles = [Node(2, 2), Node(3, 3), Node(4, 4)] if a_star(start, end, obstacles): path = [] node = end while node: path.append((node.x, node.y)) node = node.parent path.reverse() print("避障路径:", path) else: print("无法到达终点") ``` 在这个示例中,我们定义了一个Node类作为节点,包含了节点的坐标、实际代价(g_cost)、启发代价(h_cost)、总代价(f_cost)以及父节点(parent)。在A*算法函数中,我们使用开放列表(open_list)和关闭列表(closed_list)来存储节点,根据代价和启发代价计算总代价,并通过堆的数据结构来维护开放列表的优先级。 使用get_neighbors函数获取节点相邻的节点,并计算两个节点之间的距离。启发式函数heuristic用于估计节点的启发代价。 避障示例中,我们定义了起点(start)和终点(end),并给出一些障碍物节点,通过调用a_star函数来找到避障路径。如果存在路径,我们根据父节点递归构建路径,并打印出路径。如果无法到达终点,则输出无法到达终点的信息。 以上就是一个基于A*算法Python避障代码示例。注意,实际应用中可能需要根据具体情况对代码进行适当的修改和扩展。
评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值