迷宫问题 深度优先搜索 广度优先搜索 宽度优先搜索【python】


一、实验内容

  1. 下载mymaze.py文件maze1.txt,maze2.txt,maze3.txt以及requirements.txt几个文件,将它们放于同一个文件夹中,安装相应的环境定义。
  2. 读懂文件中所给的类和函数,请按照mymaze.py中的提示,补全代码,执行深度优先搜索和广度优先搜索的代码并对结果进行记录。

二、深度优先搜索和广度优先搜索总结

1.深度优先搜索算法

  • 总是先扩展frontier表中最深的结点
  • Frontier是一个栈结构——后进先出
    请添加图片描述

2.广度优先搜索算法

  • 总是先扩展frontier表中最浅的结点
  • Frontier是一个队列结构——先进先出
    请添加图片描述

三、实验代码和用于测试的迷宫

1.实验代码

#!/usr/bin/python
# -*- coding:utf-8 -*-

class Node():
    def __init__(self, state, parent, action):
        self.state = state
        self.parent = parent
        self.action = action


class StackFrontier():
    def __init__(self):
        self.frontier = []

    def add(self, node):
        self.frontier.append(node)

    def contains_state(self, state):
        return any(node.state == state for node in self.frontier)

    def empty(self):
        return len(self.frontier) == 0

    def remove(self):
        if self.empty():
            raise Exception("empty frontier")
        else:
            #please add your code here to get a node from the frontier, according to FIBO
            #请在下面添加你的代码,从frontier表中按照后进先出的顺序移除一个结点,并将移除的结点给node
            node = self.frontier[-1]
            #after remove a node ,your frontier should adaptive accordingly
            #在移除了一个结点后,你的frontier表应该相应的更新
            del self.frontier[-1]
            
            return node


class QueueFrontier(StackFrontier):

    def remove(self):
        if self.empty():
            raise Exception("empty frontier")
        else:
            #请添加你的代码,按照先进先出的顺序移除结点
            node = self.frontier[0]
            #请更新frontier表,使得frontier表为移除了一个结点剩下的结点组成
            del self.frontier[0]
            return node

class Maze():

    def __init__(self, filename):

        # Read file and set height and width of maze
        with open(filename) as f:
            contents = f.read()

        # Validate start and goal
        if contents.count("A") != 1:
            raise Exception("maze must have exactly one start point")
        if contents.count("B") != 1:
            raise Exception("maze must have exactly one goal")

        # Determine height and width of maze
        contents = contents.splitlines()
        self.height = len(contents)
        self.width = max(len(line) for line in contents)

        # Keep track of walls
        self.walls = []
        for i in range(self.height):
            row = []
            for j in range(self.width):
                try:
                    if contents[i][j] == "A":
                        self.start = (i, j)
                        row.append(False)
                    elif contents[i][j] == "B":
                        self.goal = (i, j)
                        row.append(False)
                    elif contents[i][j] == " ":
                        row.append(False)
                    else:
                        row.append(True)
                except IndexError:
                    row.append(False)
            self.walls.append(row)
        self.solution = None


    def print(self):
        solution = self.solution[1] if self.solution is not None else None
        print()
        for i, row in enumerate(self.walls):
            for j, col in enumerate(row):
                if col:
                    print("#", end="")
                elif (i, j) == self.start:
                    print("A", end="")
                elif (i, j) == self.goal:
                    print("B", end="")
                elif solution is not None and (i, j) in solution:
                    print("*", end="")
                else:
                    print(" ", end="")
            print()
        print()


    def neighbors(self, state):
        row, col = state
        candidates = [
            ("up", (row - 1, col)),
            ("down", (row + 1, col)),
            ("left", (row, col - 1)),
            ("right", (row, col + 1))
        ]

        result = []
        for action, (r, c) in candidates:
            if 0 <= r < self.height and 0 <= c < self.width and not self.walls[r][c]:
                result.append((action, (r, c)))
        return result


    def solve(self):
        """Finds a solution to maze, if one exists."""

        # Keep track of number of states explored
        self.num_explored = 0

        # Initialize frontier to just the starting position
        start = Node(state=self.start, parent=None, action=None)
        
        #please add the frontier of stackfroniter or queuefrontier
        #请声明栈结构或者队列的frontier实例
        """
        每一次都改的地方!!!
        """
        frontier = QueueFrontier()
        #frontier = StackFrontier()
        frontier.add(start)

        # Initialize an empty explored set
        self.explored = set()

        # Keep looping until solution found
        while True:

            # If nothing left in frontier, then no path
            if frontier.empty():
                raise Exception("no solution")

            # Choose a node from the frontier,please finish the sentence
            node = frontier.remove()
            self.num_explored += 1

            # If node is the goal, then we have a solution
            if node.state == self.goal:
                actions = []
                cells = []
                while node.parent is not None:
                    actions.append(node.action)
                    cells.append(node.state)
                    node = node.parent
                actions.reverse()
                cells.reverse()
                self.solution = (actions, cells)
                return

            # Mark node as explored
            self.explored.add(node.state)

            # Add neighbors to frontier
            for action, state in self.neighbors(node.state):
                if not frontier.contains_state(state) and state not in self.explored:
                    child = Node(state=state, parent=node, action=action)
                    frontier.add(child)


    def output_image(self, filename, show_solution=True, show_explored=False):
        from PIL import Image, ImageDraw
        cell_size = 50
        cell_border = 2

        # Create a blank canvas
        img = Image.new(
            "RGBA",
            (self.width * cell_size, self.height * cell_size),
            "black"
        )
        draw = ImageDraw.Draw(img)

        solution = self.solution[1] if self.solution is not None else None
        for i, row in enumerate(self.walls):
            for j, col in enumerate(row):

                # Walls
                if col:
                    fill = (40, 40, 40)

                # Start
                elif (i, j) == self.start:
                    fill = (255, 0, 0)

                # Goal
                elif (i, j) == self.goal:
                    fill = (0, 171, 28)

                # Solution
                elif solution is not None and show_solution and (i, j) in solution:
                    fill = (220, 235, 113)

                # Explored
                elif solution is not None and show_explored and (i, j) in self.explored:
                    fill = (212, 97, 85)

                # Empty cell
                else:
                    fill = (237, 240, 252)

                # Draw cell
                draw.rectangle(
                    ([(j * cell_size + cell_border, i * cell_size + cell_border),
                      ((j + 1) * cell_size - cell_border, (i + 1) * cell_size - cell_border)]),
                    fill=fill
                )

        img.save(filename)


"""if len(sys.argv) != 2:
    sys.exit("Usage: python maze.py maze.txt")"""

m = Maze("maze3.txt")
print("Maze:")
m.print()
print("Solving...")
m.solve()
print("States Explored:", m.num_explored)
print("Solution:")
m.print()
m.output_image("maze3_广度优先.png", show_explored=True)

2.测试迷宫

2.1 maze1.txt

#####B#
##### #
####  #
#### ##
     ##
A######

2.2 maze2.txt

###                 #########
#   ###################   # #
# ####                # # # #
# ################### # # # #
#                     # # # #
##################### # # # #
#   ##                # # # #
# # ## ### ## ######### # # #
# #    #   ##B#         # # #
# # ## ################ # # #
### ##             #### # # #
### ############## ## # # # #
###             ##    # # # #
###### ######## ####### # # #
###### ####             #   #
A      ######################

2.3 maze3.txt

##    #
## ## #
#B #  #
# ## ##
     ##
A######

四、实验结果

下图中黑色格子代表迷宫中的墙,深红色代表起点,深绿色代表终点,浅红色代表走过的错路,浅绿色代表走到正确的路线。

1. maze1.txt

  • 深度优先算法结果
    请添加图片描述
  • 广度优先算法结果
    请添加图片描述

2. maze2.txt

  • 深度优先算法结果
    请添加图片描述
  • 广度优先算法结果
    请添加图片描述

3. maze3.txt

  • 深度优先算法结果
    请添加图片描述
  • 广度优先算法结果
    请添加图片描述

五、结果分析

  • 完备性角度上分析:深度优先搜索和广度优先搜索都可以找到解。
  • 最优性角度上分析:深度优先搜索不一定能找到最优解,而广度优先搜索可以找到最优解。
  • 时间复杂度角度上分析:深度优先搜索跑了更多的结点所以它的时间复杂度比广度优先搜索高。
  • 空间复杂度角度上分析:广度优先搜索在找到最终结点之前需要记录所有已经走过的结点,所以空间复杂度要高一些。

总结

算法引自——人工智能 一种现代的方法(第3版)
代码源自网络,仅用于教学实验
本文来源于实验报告

A*算法是一种启发式搜索算法,常用于求解路径问题。下面以8数码和迷宫问题为例,来介绍A*算法的过程。 1. 8数码问题 1.1 状态表示 8数码问题是指在3×3的九宫格上,摆放了1-8这8个数字,其中有一个格子是空的,求解将这8个数字移动到目标状态的过程。我们可以用一个3×3的矩阵来表示当前状态,其中空格用0表示。 1.2 操作集 对于8数码问题,每次只能将空格与上下左右四个方向之一的数字进行交换,因此操作集可以表示为{Up, Down, Left, Right}。 1.3 估价函数 估价函数用于评估当前状态到达目标状态的距离,一般情况下,估价函数越小,搜索的效率越高。在8数码问题中,我们可以使用曼哈顿距离作为估价函数。曼哈顿距离是指对于一个点(x,y),到目标点(x',y')的距离为|x-x'| + |y-y'|。因此,对于当前状态s,到达目标状态的估价函数值可以表示为: h(s) = Σi=1^8 ManhattanDistance(s[i], goal[i]) 其中,s[i]表示当前状态中数字i的位置,goal[i]表示目标状态中数字i的位置。 1.4 OPEN表和CLOSED表 OPEN表是用来存储待扩展的状态的集合,CLOSED表是用来存储已经扩展过的状态的集合。在每次扩展状态时,如果该状态已经存在于CLOSED表中,则可以直接跳过;否则,将该状态加入OPEN表中。 1.5 A*算法步骤 1. 初始化OPEN表和CLOSED表,将初始状态加入OPEN表中。 2. 从OPEN表中选择f值最小的状态s,如果该状态为目标状态,则搜索结束;否则,将该状态从OPEN表中移除,并将其加入CLOSED表中。 3. 对于状态s,枚举所有可能的操作,得到所有可能的后继状态。 4. 对于每个后继状态s',计算其估价函数值f(s') = g(s) + h(s'),其中g(s)表示从初始状态到状态s的实际距离。如果状态s'已经存在于OPEN表或CLOSED表中,则比较其f值和原来的f值,选择较小的一个;否则,将状态s'加入OPEN表中。 5. 重复步骤2-4,直到找到目标状态或OPEN表为空。 2. 迷宫问题 2.1 状态表示 迷宫问题是指在一个矩形网格中,从起点出发,到达终点的最短路径。我们可以用一个二维数组来表示迷宫,其中0表示可通过的空格,1表示不可通过的障碍物。 2.2 操作集 对于迷宫问题,每次只能向上下左右四个方向之一移动一格,因此操作集可以表示为{Up, Down, Left, Right}。 2.3 估价函数 估价函数用于评估当前状态到达目标状态的距离,一般情况下,估价函数越小,搜索的效率越高。在迷宫问题中,我们可以使用欧式距离作为估价函数。欧式距离是指对于两个点(x1,y1)和(x2,y2),它们之间的距离为√((x1-x2)^2 + (y1-y2)^2)。因此,对于当前状态s,到达目标状态的估价函数值可以表示为: h(s) = EuclideanDistance(s, goal) 其中,s表示当前状态的位置,goal表示目标状态的位置。 2.4 OPEN表和CLOSED表 OPEN表和CLOSED表的作用同8数码问题中的作用相同。 2.5 A*算法步骤 1. 初始化OPEN表和CLOSED表,将起点加入OPEN表中。 2. 从OPEN表中选择f值最小的状态s,如果该状态为终点,则搜索结束;否则,将该状态从OPEN表中移除,并将其加入CLOSED表中。 3. 对于状态s,枚举所有可能的操作,得到所有可能的后继状态。 4. 对于每个后继状态s',计算其估价函数值f(s') = g(s) + h(s'),其中g(s)表示从起点到状态s的实际距离。如果状态s'已经存在于OPEN表或CLOSED表中,则比较其f值和原来的f值,选择较小的一个;否则,将状态s'加入OPEN表中。 5. 重复步骤2-4,直到找到终点或OPEN表为空。 以上就是A*算法求解8数码、迷宫问题的过程,希望能对你有所帮助。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

秋千的千秋

希望你喜欢

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值