作业题目:
- 你对人工智能定义的理解?
人工智能是智能科学中涉及研究、设计与应用智能机器和智能系统的一个分支,而智能科学是一门与计算机科学并行的学科。
人工智能是智能机器所执行的通常与人类智能有关的智能行为,这些智能行为涉及学习、感知、思考、理解、识别、判断、推理、证明、通信、设计、规划、行动和问题求解等活动。
- 解释什么是图灵测试?
图灵测试(The Turing test)由艾伦·麦席森·图灵发明,指测试者与被测试者(一个人和一台机器)隔开的情况下,通过一些装置(如键盘)向被测试者随意提问。进行多次测试后,如果机器让平均每个参与者做出超过30%的误判,那么这台机器就通过了测试,并被认为具有人类智能。图灵测试一词来源于计算机科学和密码学的先驱艾伦·麦席森·图灵写于1950年的一篇论文《计算机器与智能》,其中30%是图灵对2000年时的机器思考能力的一个预测,目前我们已远远落后于这个预测。
- 你希望人工智能帮你做些什么?
回答问题:回答关于各种领域的问题,对人所不知道的提供信息和解释概念。
写作和编辑:撰写文章、报告、邮件和其他文本。
编程和技术支持:解决编程问题、编写代码或提供技术支持。
语言翻译
根据用户的需求提供帮助和支持,使我们的生活更加便捷和高效。
4、8数码问题,如上图所示的过程,要求:空格按照左、上、右、下的顺序依次扩展。
分别用BFS、DFS(深度为5)、UCS、Greedy (启发函数h自拟) 、A*(启发函数h自拟)算法进行求解。
BFS:通过广度优先搜索从初始状态逐步向目标状态前进,记录每一步的移动操作,直到找到解决方案。
创建一个双端队列(deque),用于进行广度优先搜索。
队列中的元素是一个三元组 (当前状态, 移动序列, 步数),其中:
当前状态 表示当前的数码状态。
移动序列 用于记录达到当前状态的移动序列。
步数 表示从初始状态到当前状态的步数。
from collections import deque # 定义初始状态和目标状态 initial_state = [[2, 8, 3], [1, 0, 4], [7, 6, 5]] goal_state = [[1, 2, 3], [8, 0, 4], [7, 6, 5]] # 定义移动方向:左、上、右、下 directions = [(0, -1), (-1, 0), (0, 1), (1, 0)] direction_names = ['Left', 'Up', 'Right', 'Down'] # 创建一个队列,用于BFS queue = deque([(initial_state, "", 0)]) # (当前状态,移动序列,步数) # BFS算法 while queue: current_state, move_sequence, steps = queue.popleft() # 检查是否达到目标状态 if current_state == goal_state: print("找到解决方案!") print("移动序列:", move_sequence) print("步数:", steps) break # 扩展当前状态 empty_row, empty_col = None, None for row in range(3): for col in range(3): if current_state[row][col] == 0: empty_row, empty_col = row, col for i in range(4): new_row, new_col = empty_row + directions[i][0], empty_col + directions[i][1] if 0 <= new_row < 3 and 0 <= new_col < 3: # 创建新状态 new_state = [list(row) for row in current_state] new_state[empty_row][empty_col], new_state[new_row][new_col] = new_state[new_row][new_col], \ new_state[empty_row][empty_col] new_move_sequence = move_sequence + direction_names[i] # 将新状态加入队列 queue.append((new_state, new_move_sequence, steps + 1))
DFS:通过深度优先搜索从初始状态开始,沿着一个分支一直深入到底,直到找到解决方案或达到深度限制。然后,它会回溯到前一个状态,继续搜索其他分支,直到找到解决方案为止。
创建一个堆栈(deque),用于进行深度优先搜索。
堆栈中的元素是一个三元组 (当前状态, 移动序列, 步数),其中:
当前状态 表示当前的数码状态。
移动序列 用于记录达到当前状态的移动序列。
步数 表示从初始状态到当前状态的步数。
from collections import deque # 定义初始状态和目标状态 initial_state = [[2, 8, 3], [1, 0, 4], [7, 6, 5]] goal_state = [[1, 2, 3], [8, 0, 4], [7, 6, 5]] # 定义移动方向:左、上、右、下 directions = [(0, -1), (-1, 0), (0, 1), (1, 0)] direction_names = ['Left', 'Up', 'Right', 'Down'] # 创建一个堆栈,用于DFS stack = deque([(initial_state, "", 0)]) # (当前状态,移动序列,步数) # DFS算法 while stack: current_state, move_sequence, steps = stack.pop() # 检查是否达到目标状态 if current_state == goal_state: print("找到解决方案!") print("移动序列:", move_sequence) print("步数:", steps) break # 扩展当前状态 empty_row, empty_col = None, None for row in range(3): for col in range(3): if current_state[row][col] == 0: empty_row, empty_col = row, col if steps < 5: # 深度限制为5 for i in range(4): new_row, new_col = empty_row + directions[i][0], empty_col + directions[i][1] if 0 <= new_row < 3 and 0 <= new_col < 3: # 创建新状态 new_state = [list(row) for row in current_state] new_state[empty_row][empty_col], new_state[new_row][new_col] = new_state[new_row][new_col], \ new_state[empty_row][empty_col] new_move_sequence = move_sequence + direction_names[i] # 将新状态加入堆栈 stack.append((new_state, new_move_sequence, steps + 1))
UCS:通过 UCS 算法以最小代价的方式从初始状态开始,逐步扩展状态直到找到目标状态为止。
创建一个优先队列(priority_queue),用于进行UCS搜索。
优先队列中的元素是元组 (total_cost, current_state, move_sequence, steps),按照 total_cost 的大小进行优先级排序。
使用 UCS 算法进行搜索:
不断从优先队列中弹出具有最小 total_cost 的节点。
import heapq # 定义初始状态和目标状态 initial_state = [[2, 8, 3], [1, 0, 4], [7, 6, 5]] goal_state = [[1, 2, 3], [8, 0, 4], [7, 6, 5]] # 定义移动方向:左、上、右、下 directions = [(0, -1), (-1, 0), (0, 1), (1, 0)] direction_names = ['Left', 'Up', 'Right', 'Down'] # 定义初始节点 initial_node = (0, initial_state, "", 0) # (总代价,当前状态,移动序列,步数) # 创建一个优先队列,用于UCS priority_queue = [initial_node] # UCS算法 while priority_queue: total_cost, current_state, move_sequence, steps = heapq.heappop(priority_queue) # 检查是否达到目标状态 if current_state == goal_state: print("找到解决方案!") print("移动序列:", move_sequence) print("步数:", steps) break # 扩展当前状态 empty_row, empty_col = None, None for row in range(3): for col in range(3): if current_state[row][col] == 0: empty_row, empty_col = row, col for i in range(4): new_row, new_col = empty_row + directions[i][0], empty_col + directions[i][1] if 0 <= new_row < 3 and 0 <= new_col < 3: # 创建新状态 new_state = [list(row) for row in current_state] new_state[empty_row][empty_col], new_state[new_row][new_col] = new_state[new_row][new_col], \ new_state[empty_row][empty_col] new_move_sequence = move_sequence + direction_names[i] new_cost = steps + 1 # 计算总代价 new_total_cost = new_cost # 将新状态加入优先队列 heapq.heappush(priority_queue, (new_total_cost, new_state, new_move_sequence, new_cost))
Greedy:Greedy算法是一种启发式搜索算法,它在每一步都选择具有最小启发函数值的状态,因此它可能会陷入局部最优解,但通常运行速度很快。我使用不匹配数量作为启发函数,以贪婪的方式尝试接近目标状态。
定义启发函数(heuristic function):
heuristic(state) 计算每个状态与目标状态的不匹配的数量,用于评估状态的优劣。这里的启发函数采用了"不匹配数量",即当前状态中与目标状态不相同的方格的数量,作为估计的代价。
创建一个优先队列(priority_queue),用于进行Greedy算法搜索。使用Greedy算法进行搜索:
不断从优先队列中弹出具有最小 heuristic_value 的节点。
from collections import deque # 定义初始状态和目标状态 initial_state = [[2, 8, 3], [1, 0, 4], [7, 6, 5]] goal_state = [[1, 2, 3], [8, 0, 4], [7, 6, 5]] # 定义移动方向:左、上、右、下 directions = [(0, -1), (-1, 0), (0, 1), (1, 0)] direction_names = ['Left', 'Up', 'Right', 'Down'] # 定义启发函数:计算每个状态与目标状态的不匹配的数量 def heuristic(state): mismatch_count = 0 for row in range(3): for col in range(3): if state[row][col] != goal_state[row][col]: mismatch_count += 1 return mismatch_count # 创建一个优先队列,用于Greedy 算法 priority_queue = deque([(initial_state, "", 0, 0)]) # (当前状态,移动序列,启发函数值,步数) # Greedy 算法 while priority_queue: current_state, move_sequence, heuristic_value, steps = priority_queue.popleft() # 检查是否达到目标状态 if current_state == goal_state: print("找到解决方案!") print("移动序列:", move_sequence) print("步数:", steps) break # 扩展当前状态 empty_row, empty_col = None, None for row in range(3): for col in range(3): if current_state[row][col] == 0: empty_row, empty_col = row, col for i in range(4): new_row, new_col = empty_row + directions[i][0], empty_col + directions[i][1] if 0 <= new_row < 3 and 0 <= new_col < 3: # 创建新状态 new_state = [list(row) for row in current_state] new_state[empty_row][empty_col], new_state[new_row][new_col] = new_state[new_row][new_col], \ new_state[empty_row][empty_col] new_move_sequence = move_sequence + direction_names[i] new_heuristic_value = heuristic(new_state) new_steps = steps + 1 # 将新状态加入优先队列,按照启发函数值排序 priority_queue.append((new_state, new_move_sequence, new_heuristic_value, new_steps)) priority_queue = deque(sorted(priority_queue, key=lambda x: x[2]))
A*算法:A*算法是一种启发式搜索算法,它综合考虑了每个状态的启发函数值和已经花费的代价(步数),以找到最优的路径。我利用不匹配数量作为启发函数,并按照总代价(启发函数值 + 步数)的大小进行搜索,以确保在估计代价和实际代价之间取得平衡,找到最优的解决方案。
使用A*算法进行搜索:
不断从优先队列中弹出具有最小 total_cost 的节点。
检查当前状态是否达到目标状态,如果是,输出解决方案并结束搜索。
如果当前状态不是目标状态,则扩展当前状态,生成新的可能状态,并将它们加入优先队列中继续搜索。
扩展状态的过程:
找到当前状态中的空格的位置 (empty_row, empty_col)。
针对空格的四个移动方向,尝试生成新的状态,即将空格与相邻方格进行交换,生成一个新的数码状态。
计算新的 heuristic_value,这里使用启发函数计算。
计算新的 steps,即步数。
计算新的 total_cost,即启发函数值与步数之和。
将新状态加入优先队列,并按照 total_cost 的大小重新排序队列,以确保按照总代价的A*策略进行搜索。
from collections import deque # 定义初始状态和目标状态 initial_state = [[2, 8, 3], [1, 0, 4], [7, 6, 5]] goal_state = [[1, 2, 3], [8, 0, 4], [7, 6, 5]] # 定义移动方向:左、上、右、下 directions = [(0, -1), (-1, 0), (0, 1), (1, 0)] direction_names = ['Left', 'Up', 'Right', 'Down'] # 创建一个优先队列,用于A*算法 priority_queue = deque([(initial_state, "", 0, 0)]) # (当前状态,移动序列,启发函数值,步数) # 定义启发函数:计算每个状态与目标状态的不匹配的数量 def heuristic(state): mismatch_count = 0 for row in range(3): for col in range(3): if state[row][col] != goal_state[row][col]: mismatch_count += 1 return mismatch_count # A*算法 while priority_queue: current_state, move_sequence, heuristic_value, steps = priority_queue.popleft() # 检查是否达到目标状态 if current_state == goal_state: print("找到解决方案!") print("移动序列:", move_sequence) print("步数:", steps) break # 扩展当前状态 empty_row, empty_col = None, None for row in range(3): for col in range(3): if current_state[row][col] == 0: empty_row, empty_col = row, col for i in range(4): new_row, new_col = empty_row + directions[i][0], empty_col + directions[i][1] if 0 <= new_row < 3 and 0 <= new_col < 3: # 创建新状态 new_state = [list(row) for row in current_state] new_state[empty_row][empty_col], new_state[new_row][new_col] = new_state[new_row][new_col], \ new_state[empty_row][empty_col] new_move_sequence = move_sequence + direction_names[i] new_heuristic_value = heuristic(new_state) new_steps = steps + 1 # 计算总代价(启发函数值 + 步数) new_total_cost = new_heuristic_value + new_steps # 将新状态加入优先队列,按照总代价排序 priority_queue.append((new_state, new_move_sequence, new_heuristic_value, new_steps)) priority_queue = deque(sorted(priority_queue, key=lambda x: x[2] + x[3]))
- 请分别用极小化极大算法和α-β剪枝算法计算博弈树
极小化极大算法: minimax 函数是算法的核心,它采用了递归的方式来搜索博弈树,同时应用了极小化极大策略。这个函数的参数包括当前轮到哪个玩家(player)和当前博弈树节点(node)。
如果轮到玩家1(player == 1),则选择子节点中的最大值作为当前节点的值;如果轮到玩家0,则选择子节点中的最小值作为当前节点的值。这是博弈树中玩家1追求最大化结果,而玩家0追求最小化结果的基本原则。
递归结束后,将当前节点的值设置为计算出的最大值或最小值,并返回该值。
# 极小化极大算法 def minimax(player, node): if not node["value"]: values = [] for child in node["children"]: values.append(minimax(player ^ 1, child)) if player == 1: node["value"] = max(values) else: node["value"] = min(values) return node["value"] # 构建完全二叉树 tree1 = { "value": None, "children": [ { "value": None, "children": [ { "value": None, "children": [ { "value": 10, "children": [], }, { "value": 6, "children": [], }, ], }, { "value": None, "children": [ { "value": 100, "children": [], }, { "value": 8, "children": [], }, ], }, ], }, { "value": None, "children": [ { "value": None, "children": [ { "value": 1, "children": [], }, { "value": 2, "children": [], }, ], }, { "value": None, "children": [ { "value": 20, "children": [], }, { "value": 4, "children": [], }, ], }, ], }, ], } def main(tree): res = minimax(1, tree) print("最佳值:", res) print(tree) main(tree1)
α-β剪枝算法:在极小化极大算法中应用α-β剪枝,以加速博弈树的搜索过程。这种技术可以显著减少搜索的分支数,提高算法的效率,特别是在博弈树很大的情况下。
根据α-β剪枝的原则来更新 alpha 和 beta。如果在搜索过程中发现某个子节点的值已经超出了当前玩家的上界 beta(对于玩家0)或下界 alpha(对于玩家1),则可以剪枝,停止继续搜索该分支,因为已经找到更好的选择。
# 极小化极大算法 + α-β剪枝 def alpha_beta(player, node, alpha, beta): if not node["value"]: values = [] for child in node["children"]: val = alpha_beta(player ^ 1, child, alpha, beta) values.append(val) if player == 1: # max层 子元素的值大于当前下界的值 更新下界 alpha = max(alpha, val) # max层 下界大于等于上界直接返回下界(返回较大者) if alpha >= beta: return alpha else: # min层 子元素的值小于当前上界的值 更新上界 beta = min(beta, val) # min层 下界大于等于上界直接返回上界(返回较小者) if alpha >= beta: return beta # 返回当前层节点的极值,便于父元素进行剪枝(并不是返回alpha或者beta) if player == 1: node["value"] = max(values) else: node["value"] = min(values) return node["value"]
(二叉树构建部分与上面相同)
- 课程设计作业:井字棋
在3*3格子上进行的连珠游戏,和五子棋比较类似,由于棋盘一般不画边框,格线排成井字故得名。游戏由分别代表O和X的两个游戏者轮流在格子里留下标记(一般来说先手者为X)。由最先在任意一条直线上成功连接三个标记的一方获胜。
使用深度优先搜索、结合极小化极大搜索、结合α-β剪枝算法分别设计出井字棋游戏。
编程语言不限。
输入用鼠标或键盘操作:(三选一)
①可以用鼠标选择人类的着棋位置
②也可以用上下左右键选择位置按Enter键着子
③也可以通过输入坐标(0,1)来输入着棋位置
输出:
①可以通过图形界面实时输出
②也可以通过在控制台打印3*3数组的形式输出没步任何电脑着棋后的状态例如:print(“XOO\nOXX\nOOX\n”)
设计思路:
输入操作:通过输入坐标(0,1)来输入着棋位置
输出:在控制台打印3*3数组的形式输出每步电脑着棋后的状态
check_win(board, player) 函数:检查是否有获胜者。它会检查每一行、每一列以及两个对角线上是否有连续的三个相同符号("X"或"O"),如果有,就返回True。
check_tie(board) 函数:检查游戏是否平局。如果棋盘上没有空格(" "),则返回True。
get_player_move(board) 函数:获取玩家的落子位置。通过输入行数和列数,然后检查该位置是否为空,如果为空,则返回选择的位置。
minimax(board, depth, alpha, beta, maximizing) 函数:在深度优先搜索的基础上实现极小化极大算法(Minimax)结合α-β剪枝。这是一个递归函数,用于模拟玩家和电脑的决策过程,以确定最佳落子位置。如果当前局面有玩家获胜("X"胜利)返回-10,如果电脑获胜("O"胜利)返回10,如果平局返回0。
如果当前是最大化玩家(电脑)的回合(maximizing为True),则尝试所有可能的落子位置,对每个位置递归调用minimax来计算分数。同时,使用α-β剪枝来减少搜索空间,如果发现某个分支已经比β大,就可以剪枝不再搜索,因为这个分支对最终结果不会有影响。最终返回最高分数。
如果当前是最小化玩家(玩家)的回合(maximizing为False),同样尝试所有可能的落子位置,对每个位置递归调用minimax来计算分数。同时,使用α-β剪枝来减少搜索空间,如果发现某个分支已经比α小,就可以剪枝不再搜索,因为这个分支对最终结果不会有影响。最终返回最低分数。
play_game() 函数:这是游戏的主函数,包括玩家选择先后手、游戏主循环。在游戏主循环中,根据玩家选择的先后手,交替进行玩家和电脑的回合。玩家的回合通过输入来获取落子位置,电脑的回合通过minimax函数来计算最佳落子位置。
游戏开始后,玩家可以选择先手("X")或者电脑先手("O"),然后游戏会交替进行,直到有一方获胜或者游戏平局。
设计的关键在于minimax函数,它通过递归搜索棋盘状态树来找到最佳落子位置,同时通过α-β剪枝来减少搜索时间,从而提高了计算效率。我把minimax算法的搜索深度(depth)设置为9,表示电脑将考虑9步后的棋盘状态来做出决策。增加搜索深度会使电脑更聪明,但也会增加计算时间。根据我的测试,9步时的电脑我已经无法战胜,且运行时间较快,可以很好的满足要求。
源码:
import math def print_board(board): # 打印游戏棋盘 for row in board: print(" | ".join(row)) print() def check_win(board, player): # 检查是否有获胜者 for i in range(3): if all(board[i][j] == player for j in range(3)): return True if all(board[j][i] == player for j in range(3)): return True if all(board[i][i] == player for i in range(3)): return True if all(board[i][2 - i] == player for i in range(3)): return True return False def check_tie(board): # 检查是否平局 for row in board: for cell in row: if cell == " ": return False return True def get_player_move(board): # 获取玩家落子位置 while True: try: row = int(input("输入行数 (1-3): ")) - 1 col = int(input("输入列数 (1-3): ")) - 1 if board[row][col] == " ": return row, col else: print("这个位置已经被占,请重新输入.") except ValueError: print("请输入1-3之间的数.") def minimax(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, _ = minimax(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, _ = minimax(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 def play_game(): board = [[" " for j in range(3)] for i in range(3)] print_board(board) # 玩家选择先后手 while True: user_choice = input("选择X或者O(X先手): ") if user_choice.lower() == "x": player = "X" break elif user_choice.lower() == "o": player = "O" break else: print("请输入X或O.") # 游戏主循环 while True: if player == "X": # 玩家先手 while not check_win(board, "X") and not check_tie(board): row, col = get_player_move(board) board[row][col] = "X" print_board(board) if check_win(board, "X"): print("玩家获胜!") return if check_tie(board): print("平局!") return _, (row, col) = minimax(board, 9, -math.inf, math.inf, True) print("电脑思考后落子", (row + 1, col + 1)) board[row][col] = "O" print_board(board) if check_win(board, "O"): print("玩家失败!") return if check_tie(board): print("平局!") return else: # 电脑先手 while not check_win(board, "O") and not check_tie(board): _, (row, col) = minimax(board, 9, -math.inf, math.inf, True) print("电脑思考后落子", (row + 1, col + 1)) board[row][col] = "X" print_board(board) if check_win(board, "X"): print("玩家失败!") return if check_tie(board): print("平局!") return row, col = get_player_move(board) board[row][col] = "O" print_board(board) if check_win(board, "O"): print("玩家获胜!") return if check_tie(board): print("平局!") return if __name__ == '__main__': play_game()
|
|