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函数
网友评论