人工智能实验:A*算法解决15Puzzle&SuperQueenPuzzle

A*算法

感觉A *和bfs区别不大,关键就在于H值,当我们在写bfs时,从可用列表决定当前拓展哪个节点是按照fifo原则,即取出列表第一个元素,有些bfs会根据该节点的历史成本来决定(选择历史成本最低的节点进行拓展)
A * 的不同就在于取出节点时应用了启发式策略,即H值。就是这个函数⬇️
F = G + H
G:以往的成本,确定值
H:对未来成本的估计
取出当前可用列表中F最小的节点进行拓展。
这个H由于是估计,因此就根据具体问题自己个人设计,没有标准的答案。

伪代码

# 1. Create an empty fringe and add your root node (you can use lists, sets, heaps, ... )
# 2. While the container is not empty:
# 3.      Pop the best? node (Use the attribute `node.f` in comparison)
# 4.      If that's a goal node, return node.get_path()
# 5.      Otherwise, add the children of the node to the fringe
# 6. Return None

实验

老师已经给了框架代码,只需要填几个重要函数和实现A*算法就好了。
框架代码写的特别特别好!简单易读,很适合第一次写python的小白快速入门。

The layout of the helper code:
• node.py - The implementation of the Node class. (Do not modify this file!)
• problems.py - The partial implementation of the FifteensNode and SuperqueensNode classes.
• search.py - The skeleton of the A* algorithm.
• test.py - A helper script that runs some tests.

需要补充的函数:
You need to code up the function Astar in search.py, and the following methods of the FifteensNode and SuperqueensNode classes:
• is_goal:根据具体问题判断是否目标节点
• generate_children:生成孩子节点列表(从当前状态移动到下一状态的所有可能)
• evaluate_heuristic:计算H值

15puzzle(十五数码)

The first problem that you will solve using A* is the classic fifteens puzzle (the four-by-four version of the eights puzzle studied in class) where you can move any horizontally or vertically adjacent tile into the empty slot.
node :
board: 棋盘列表,二维数组

• is_goal:
判断当前board和goal board是否相同即可

• generate_children:生成孩子节点列表
上下左右移动,要判断边界条件!

 if x > 0:
            # up
            s1 = copy.deepcopy(self.board)
            s1[x][y] = self.board[x - 1][y]
            s1[x - 1][y] = self.board[x][y]
            child_1 = FifteensNode(g=child_g, parent=self, board=s1)
            res.append(child_1)

• evaluate_heuristic:计算H值
我设计的比较简单,就是根据当前棋盘和目标棋盘的错位数。

  diff = 0
        for i in range(4):
            for j in range(4):
                if self.board[i][j] == 0:
                    continue
                if self.board[i][j] != goalBoard[i][j]:
                    diff += 1
        return diff

SuperQueen Puzzle

Consider a modified chess piece that can move like a queen, but also like a knight. We will call such a piece a “superqueen” (it is also known as an “amazon”). This leads to a new “superqueens” puzzle. We formulate the puzzle as a constraint optimization problem: each row and each column can have at most one superqueen (that’s a hard constraint), and we try to minimize the number of pairs of superqueens that attack each other (either diagonally or with a knight’s move).
node :
queen_positions:[皇后的放置坐标] e.g. [(x1,y1), (x2,y2), ....]
• is_goal:根据具体问题判断是否目标节点
所有皇后都放置好了
• generate_children:生成孩子节点列表(从当前状态移动到下一状态的所有可能)
由于硬约束是同一行和同一列只能放一个皇后,我们每次拓展的时候就是向下一行放皇后,因此同一行只放一个皇后必然是满足的。因此在本函数中,我将已经放过皇后的列号collect起来,然后在没有放过的列放皇后。

  if self.is_goal():
            return
        col = []
        res = []
        length = len(self.queen_positions)
        for i in range(length):
            col.append(self.queen_positions[i][1])
        for i in range(self.n):
            if  i not in col :
                queen_positions = copy.deepcopy(self.queen_positions)
                queen_positions.append((length, i))
                child = SuperqueensNode(parent=self, g=self.g+self.evaluate_g(), queen_positions=queen_positions,n=self.n)
                res.append(child)
        return res

• evaluate_heuristic:计算H值
//optional

• 计算g值:
g:当前产生攻击的皇后数,从题目中我们可知攻击有两种:对角线&马走日

    def evaluate_g(self):
        g = 0
        length = len(self.queen_positions)
        for i in range(length):
            for j in range(length - i - 1):
                left = self.queen_positions[i]
                right = self.queen_positions[j]
                # 对角线
                if abs(left[0] - right[0]) == abs(left[1] - right[1]):
                    g += 1
                # 马走日
                if abs(left[0] - right[0]) == 2 * abs(left[1] - right[1]):
                    g += 1
                if 2 * abs(left[0] - right[0]) == abs(left[1] - right[1]):
                    g += 1
        return g

A星算法

伪代码已经写的非常详细了

    open = []
    open.append(root)
    while open:
        open.sort(key=lambda node: node.f)
        cur = open[0]
        open.remove(cur)
        if cur.is_goal():
            return cur.get_path()
        open.extend(cur.generate_children())

小坑

open.append(cur.generate_children()):generate_children()返回一个list,如果用append是将这个list整体作为一个元素加入open
如果想实现将list中的元素取出放到open中,应该使用extend函数

你可能感兴趣的:(人工智能实验:A*算法解决15Puzzle&SuperQueenPuzzle)