A*


A*算法

A* 算法维护一个优先级队列,结点的优先级由$f(n)$的值决定,$f(n)$值越小,结点的优先级越高。$f(n) = g(n)+h(n)$,其中$g(n)$为结点从起始状态到当前状态所花费的代价,$h(n)$为当前状态到终止状态距离的估计代价,为启发式函数。当$h(n)$小于等于结点n到终止状态的实际代价,则A* 算法一定能找到最优路径。

以下为 A* 的伪代码:

初始化优先级队列OPEN,CLOSE(OPEN里为待展开的结点,CLOSE中表示已经展开的结点)
将初始状态加入OPEN
while OPEN:
	n⬅OPEN中弹出优先级最高的结点
	将n加入CLOSE
	如果n为终止结点:
		找n的parent,直到起始状态
		return
	遍历n的邻近结点m:
		如果m在CLOSE中:
			continue
		如果m也不在OPEN中:
			设置m的parent为n
			将m加入OPEN中

Q: 森林宝石的秘密通道

题目描述:

在一个神秘的森林里,有一块由9个小方格组成的魔法石板。石板上有8个宝石,每个宝石上刻有1-8中的一个数字(每个数字都不重复)。石板上还有一个空位,用0表示。通过移动空位周围的宝石,你可以改变宝石的位置。传说中,当宝石按照某个特定的顺序排列时(本题设为135702684),魔法石板将会显露出通往一个宝藏的秘密通道。
现在,你站在这块魔法石板前,需要找到一种最少步骤的移动方法,将宝石排列成目标顺序。为了解开这个谜题,请使用A*算法来设计一个程序,帮助你从初始状态成功解锁秘密通道。

要求:要求只能用A*算法。

输入格式:一行介个数字,空格用0表示,除0之外,分别表示从左到右从上到下的对应宝石上的数字。

输入格式:只有一行,该行只有一个数字,表示从初始状态到目标状态需要的最少移动次数(测试数据中无特殊无法到达目标状态数据)。

输入输出样例:

输入:150732684

输出:2

# 该函数返回启发式函数的值,current_node为当前结点矩阵,goal_node为目标结点矩阵
def heuristic_f(current_node, goal_node):
    h = 0
    rows = current_node.shape[0]
    cols = current_node.shape[1]
    for i in range(rows):
        for j in range(cols):
            if current_node[i][j] != goal_node[i][j] and current_node[i][j]!=0:
                goal_x = np.argwhere(goal_node==current_node[i][j])[0][0]
                goal_y = np.argwhere(goal_node==current_node[i][j])[0][1]
                h += (abs(i-goal_x) + abs(j-goal_y))
    return h

将open_list定义为一个按照f升序排列的列表,其中为待展开的结点,close_list中存储已经展开的结点。当open_list不为空时,每次弹出第一个结点。

def main():
    # 每个结点用一个字典表示
    start = {}
    input_order = list(input())
    # 初始化3*3的输入矩阵
    input_order = [int(x) for x in input_order]
    start_node = np.array(input_order).reshape(3, 3)
    start['node'] = start_node
    start['parent'] = {}
    start['steps'] = get_steps(start['node'])
    start['g'] = 0
    start['h'] = heuristic_f(start['node'], goal['node'])
    start['f'] = start['g'] + start['h']
    # openlist中存储待展开的结点,closelist中存储已经展开的节点
    openlist = []
    closelist = []
    openlist.append(start)
    while openlist:
        current = openlist.pop(0)
        closelist.append(current)
        # 如果当前展开的结点与目标结点一致,则返回
        if (current['node'] == goal['node']).all():
            print(current['g'])
            p_list = []
            p = current
            while p:
                p_list.append(p.get('node'))
                p = p['parent']
            p_list.reverse()
            # print(p_list)
            return
        children = get_children(current)
        for child in children:
            # 判断该结点是否出现和待展开的结点列表和已经展开的结点列表
            exist_open = False
            exist_close = False
            for i in range(len(openlist)):
                if (child['node'] == openlist[i]['node']).all():
                    exist_open = True
                    # 如果该孩子结点的f小于与它展开相等的在openlist结点的值,则用该孩子结点替代
                    if child['f'] < openlist[i]['f']:
                        del openlist[i]
                        openlist.append(child)
                        break
            for j in range(len(closelist)):
                if (child['node'] == closelist[j]['node']).all():
                    exist_close = True
                    break
            if exist_open == False and exist_close == False:
                openlist.append(child)
        openlist = sorted(openlist, key=itemgetter('f'))
  • 版权声明: 本博客所有文章除特别声明外,著作权归作者所有。转载请注明出处!
  • Copyrights © 2023 Yuqing He

请我喝杯奶茶吧~

支付宝
微信