当前位置: 首页 > news >正文

实验8 搜索技术

实验8 搜索技术

一、实验目的
(1)掌握搜索技术的相关理论,能根据实际情况选取合适的搜索方法;
(2)进一步熟悉盲目搜索技术,掌握其在搜索过程中的优缺点;
(3)掌握启发式搜索的思想,能针对实际问题选取合适的评价函数;
(4)掌握问题归约的解决问题的思想,掌握与或图的搜索技术并能应用;
(5)深入理解博弈树搜索方法,并能应用于对弈类问题;
(6)根据自身情况,能选择合适的编程语言,实现启发式搜索、博弈树搜索方法、α-β剪枝算法,并能应用于实际AI问题。

二、实验内容
选择一种编程语言(最好为python或java),编程实现下面题目要求。
1、八数码难题
在3×3的棋盘上,摆有八个棋子,每个棋子上标有1至8的某一数字。棋盘中留有一个空格,空格可用0来表示。空格周围的棋子可以移到空格中。要求解的问题是:给出一种初始布局(初始状态)和目标布局,找到一种启发式移动方法,实现从初始布局到目标布局的转变,并与实验7 的盲目移动方法对比。
在这里插入图片描述

1、需求分析
在一个3×3的九宫格棋盘上,摆有8个正方形方块,每一个方块都标有1~8中的某一个数字。棋盘中留有一个空格,要求按照每次只能将与空格相邻的方块与空格交换的原则,将任意摆放的数码盘(初始状态)逐步摆成某种给定的数码盘的排列方式(目标状态)。

2、数据结构、功能模块设计与说明
运用启发式搜索,利用问题拥有启发信息引导搜索,以达到减小搜索范围、降低问题复杂度的目的。在启发式搜索过程中,要对Open表进行排序,这就要有一种方法来计算待扩展结点有希望通向目标结点的不同程度,人们总是希望找到最有可能通向目标结点的待扩展结点优先扩展。一种最常用的方法是定义一个评价函数对各个结点进行计算,其目的就是用来估算出“有希望”的结点。用f来标记评价函数,用f(n)表示结点n的评价函数值,并用f来排列等待扩展的结点,然后选择具有最小f值的结点作为下一个要扩展的结点。
A*算法是一种有序搜索算法,其特点在于对评价函数的定义上。这个评估函数f使得在任意结点上其函数值f(n)能估算出结点S到结点n的最小代价路径的代价与从节点n到某一目标节点的最小代价路径的代价的总和,也就是说f(n)是约束通过结点n的一条最小代价路径的代价的估计。

3、核心代码(不要将全部代码贴在报告上)与测试结果说明
核心代码:
定义A*算法

def A_start(start, end, distance_fn, generate_child_fn, time_limit=10):
    # 创建起始状态结点和目标状态结点对象,并分别计算其哈希值
    root = State(0, 0, start, hash(str(BLOCK)), None)
    end_state = State(0, 0, end, hash(str(GOAL)), None)
    # 检查起始状态是否就是目标状态,如果是,则直接输出提示信息
    if root == end_state:
        print("start == end !")
    # 将起始状态结点加入到OPEN表中,并对OPEN表进行堆化操作
    OPEN.append(root)
    heapq.heapify(OPEN)
    # 创建一个哈希集合,用于存储已经生成的状态结点的哈希值,并将起始状态结点的哈希值添加到集合中
    node_hash_set = set()
    node_hash_set.add(root.hash_value)
    # 记录算法开始的时间
    start_time = datetime.datetime.now()
    # 进入主循环,直到OPEN表为空(搜索完成)或达到时间限制
    while len(OPEN) != 0:
        top = heapq.heappop(OPEN)
        # 如果当前结点就是目标状态结点,则直接输出路径
        if top == end_state:
            return print_path(top)
        # 产生孩子节点,孩子节点加入OPEN表
        generate_child_fn(cur_node=top, end_node=end_state, hash_set=node_hash_set,
                          open_table=OPEN, dis_fn=distance_fn)
        # 记录当前时间
        cur_time = datetime.datetime.now()
        # 超时处理,如果运行时间超过了设定的时间限制,则输出超时提示信息并返回
        if (cur_time - start_time).seconds > time_limit:
            print("Time running out, break !")
            print("Number of nodes:", SUM_NODE_NUM)
            return -1
    # 如果循环结束时OPEN表为空,则表示没有找到路径,输出提示信息并返回-1
    print("No road !")  # 没有路径
    return -1

定义曼哈顿距离计算函数

def manhattan_dis(cur_node, end_node):  # 定义一个名为manhattan_dis的函数,接受两个参数cur_node(当前结点)和end_node(目标结点)
    # 获取当前结点和目标结点的状态矩阵
    cur_state = cur_node.state
    end_state = end_node.state
    dist = 0
    N = len(cur_state)  # 获取状态矩阵的大小,假设为N
    # 遍历状态矩阵中的每个位置
    for i in range(N):
        for j in range(N):
            # 如果当前结点的值与目标结点的值相等,则跳过当前位置,因为这个位置已经在目标状态中
            if cur_state[i][j] == end_state[i][j]:
                continue
            num = cur_state[i][j]  # 获取当前结点在状态矩阵中的值
            # 如果当前结点的值为0(空白格),则将目标位置设置为状态矩阵的右下角
            if num == 0:
                x = N - 1
                y = N - 1
            # 如果当前结点的值不为0,则根据当前结点的值计算其目标位置,假设目标位置为(x,y)
            else:
                x = num / N
                y = num - N * x - 1
            # 计算当前结点与目标位置之间的曼哈顿距离,并累加到总距离中
            dist += (abs(x - i) + abs(y - j))
    # 返回计算得到的曼哈顿距离作为当前结点到目标结点的估计代价
return dist

生成子结点函数

def generate_child(cur_node, end_node, hash_set, open_table, dis_fn):
    # 如果当前结点就是目标结点,则直接将目标结点假如OPEN表,并返回,表示已经找到了解
    if cur_node == end_node:
        heapq.heappush(open_table, end_node)
        return
    # 获取当前结点状态矩阵的大小
    num = len(cur_node.state)
    # 遍历当前结点状态矩阵的每一个位置
    for i in range(0, num):
        for j in range(0, num):
            # 如果当前位置不是空格,则跳过,因为空格是可以移动的位置
            if cur_node.state[i][j] != 0:
                continue
            # 遍历当前位置的四个邻居位置,即上下左右四个方向
            for d in direction:
                x = i + d[0]
                y = j + d[1]
                if x < 0 or x >= num or y < 0 or y >=num:
                    continue
                # 记录生成的结点数量
                global SUM_NODE_NUM
                SUM_NODE_NUM += 1
                # 交换空格和邻居位置的数字,生成一个新的状态矩阵
                state = copy.deepcopy(cur_node.state)
                state[i][j], state[x][y] = state[x][y], state[i][j]
                # 计算新状态矩阵的哈希值,并检查是否已经在哈希集合中存在,如果存在则表示已经生成过相同的状态,跳过
                h = hash(str(state))
                if h in hash_set:
                    continue
                # 将新状态的哈希值添加到哈希集合中,计算新状态结点的gn(从起始结点到当前结点的代价)和hn(当前结点到目标结点的估计代价)
                hash_set.add(h)
                gn = cur_node.gn + 1
                hn = dis_fn(cur_node, end_node)
                # 创建新的状态结点对象,并将其加入到当前结点的子结点列表中,并将其加入到OPEN表中。
                node = State(gn, hn, state, h, cur_node)
                cur_node.child.append(node)
                heapq.heappush(open_table, node)

结果:
在这里插入图片描述

4、实验存在的问题与体会
学会了用A算法解决搜索问题,在搜索的时候比之前的盲目搜索用时更少,在其他搜索问题上用A算法也可以比盲目搜索更有效率,但前提是找到合适的估计函数。

2、编程实现一字棋游戏人机对弈,在九宫格棋盘上人机轮流在棋盘上摆各自的棋子,每次一枚,谁先取得三子一线结果的一方获胜。 (请用极小极大值搜索算法或α-β剪枝算法实现)
在这里插入图片描述

1、需求分析
用极小极大值搜索算法或α-β剪枝算法编程实现一字棋游戏人机对弈,在九宫格棋盘上人机轮流在棋盘上摆各自的棋子,每次一枚,谁先取得三子一线结果的一方获胜。
2、数据结构、功能模块设计与说明
关于核心判断部分 maxmin()解释:

该函数根据当前游戏状态的得分,计算出下一步应该走的位置。该函数的参数如下:

board:表示当前的游戏状态,是一个 3x3 的二维数组。
depth:表示搜索的深度,即预测未来多少步。
alpha:表示当前最好的评分(对于 maximizing 玩家来说),初始化为负无穷。
beta:表示当前最好的评分(对于 minimizing 玩家来说),初始化为正无穷。
maximizing:表示当前是轮到哪个玩家走棋,True 表示 maximizing 玩家,False 表示 minimizing 玩家。
该函数的返回值包括两个元素:

best_score:表示当前的最优得分。
best_move:表示当前应该走的位置。
在函数内部,首先使用 check_win 和 check_tie 函数判断当前游戏状态,如果存在胜负或平局,则返回对应的得分。如果还没有到达搜索的深度,则进入下一步的搜索。

如果当前是 maximizing 玩家,则使用 for 循环遍历棋盘中所有空位,对于每个空位,都假设 maximizing 玩家会走这个位置,并计算下一步的得分。如果得分比当前最优得分更好,则更新最优得分和最优位置。同时,更新 alpha 的值为当前最优得分和 alpha 中的最大值。如果 beta 的值小于或等于 alpha,则可以停止搜索并返回结果。

如果当前是 minimizing 玩家,则与 maximizing 玩家的情况类似,只是更新的是 beta 的值。
最终返回当前的最优得分和最优位置。

3、核心代码(不要将全部代码贴在报告上)与测试结果说明
核心代码:
minmax函数

# 极大极小/α-β剪枝算法
def minmax(board, depth, alpha, beta, maximizing):
    if check_win(board, "X"):
        return -10, None
    if check_win(board, "O"):
        return 10, None
    if check_tie(board):
        return 0, None
    if maximizing:
        best_score = -math.inf
        best_move = None
        for row in range(3):
            for col in range(3):
                if board[row][col] == " ":
                    board[row][col] = "O"
                    score, _ = minmax(board, depth - 1, alpha, beta, False)
                    board[row][col] = " "
                    if score > best_score:
                        best_score = score
                        best_move = (row, col)
                    alpha = max(alpha, best_score)
                    if beta <= alpha:
                        break
        return best_score, best_move
    else:
        best_score = math.inf
        best_move = None
        for row in range(3):
            for col in range(3):
                if board[row][col] == " ":
                    board[row][col] = "X"
                    score, _ = minmax(board, depth - 1, alpha, beta, True)
                    board[row][col] = " "
                    if score < best_score:
                        best_score = score
                        best_move = (row, col)
                    beta = min(beta, best_score)
                    if beta <= alpha:
                        break
        return best_score, best_move

结果:
在这里插入图片描述

4、实验存在的问题与体会
掌握了极大极小值搜索算法和α-β算法的算法思想。

相关文章:

  • 《论分布式系统架构设计及其应用》架构师论文
  • C#中通过Response.Headers设置自定义参数
  • 【设计模式】原型模式
  • OpenCV图像加权函数:addWeighted
  • TSN CB:恢复算法与潜在错误检测
  • 动态规划----完全平方数(3种写法,逐步简化)
  • cursor中使用prettier-code formatter插件方法
  • 六十天前端强化训练之第十七天React Hooks 入门:useState 深度解析
  • 基于 GEE 利用 Sentinel-1 双极化数据计算 SDWI 指数实现逐月提取水域面积
  • CFD交易与传统股票交易在交易机制上存在哪些显著差异
  • 矩阵交换行(信息学奥赛一本通-1119)
  • Compose笔记(九)--Checkbox
  • 【eNSP实战】使用高级ACL实现单向Ping
  • 基于UniApp + Vue3开发的智能汉字转拼音工具
  • 【前端三剑客】万字总结JavaScript
  • 【6*】差分约束系统学习笔记
  • nerfstudio以及相关使用记录(长期更新)
  • 【STM32】NVIC(嵌套向量中断控制器)
  • 【蓝桥】-动态规划-倒水
  • AI Agent席卷B端:解锁部门效率新玩法,挑战企业软件的智能革命
  • 外媒称北斗挑战GPS地位,外交部:中国的北斗也是世界的北斗
  • 凤阳县鼓楼四周连夜搭起围挡,安徽省文物局已介入调查
  • 张永宁任福建宁德市委书记
  • 交通运输局男子与两名女子办婚礼?官方通报:未登记结婚,开除该男子
  • 俄乌直接谈判勉强收场,特朗普再次“电话外交”能否有用?|907编辑部
  • 吴双评《发展法学》|穷国致富的钥匙:制度,还是产业活动?