墨小兰的玩具之八数码

 BFS版

普通的广度优先,简单的玩具。

from collections import deque

# 八数码问题求解函数
def solve_puzzle(start, target):
    # 定义移动方向
    directions = [(0, 1), (0, -1), (1, 0), (-1, 0)]
    
    # 转换初始状态和目标状态为字符串
    start_str = ''.join(str(num) for num in start)
    target_str = ''.join(str(num) for num in target)
    
    # 创建队列并加入初始状态
    queue = deque([(start_str, 0)])
    
    # 创建集合记录已访问的状态
    visited = set([start_str])
    
    while queue:
        # 出队
        curr_state, steps = queue.popleft()
        
        # 检查是否达到目标状态
        if curr_state == target_str:
            return steps
        
        # 找到空格位置
        zero_pos = curr_state.index('0')
        zero_row, zero_col = zero_pos // 3, zero_pos % 3
        
        # 遍历四个方向的移动
        for direction in directions:
            new_row, new_col = zero_row + direction[0], zero_col + direction[1]
            
            # 检查移动是否合法
            if 0 <= new_row < 3 and 0 <= new_col < 3:
                # 创建新状态
                new_pos = new_row * 3 + new_col
                new_state = list(curr_state)
                new_state[zero_pos], new_state[new_pos] = new_state[new_pos], new_state[zero_pos]
                new_state_str = ''.join(new_state)
                
                # 判断是否已访问过该状态
                if new_state_str not in visited:
                    # 加入队列并标记为已访问
                    queue.append((new_state_str, steps + 1))
                    visited.add(new_state_str)
    
    # 如果无解,则返回-1
    return -1

启发式之A*版

玩具原理:曼哈顿距离是欧几里得距离的一种变体,也称为城市街区距离或L1距离。它定义为在网格状结构上沿着垂直和水平路径从一个点到另一个点所需的步数之和。

在算法中定义了一个启发式函数heuristi,它遍历了当前状态的每一个元素,表示为state[i]。如果元素不是空格('0'),则计算该元素在当前状态中的行坐标curr_row和列坐标curr_col。同时,计算该元素在目标状态中的目标行坐标target_row和目标列坐标target_col。通过将当前元素转换为整数减去1,可以得到目标元素在目标状态中的坐标。然后,使用曼哈顿距离的定义来计算当前元素与目标元素之间的距离,即当前行与目标行的绝对值之差加上当前列与目标列的绝对值之差。将所有元素的距离累加到manhattan_distance中。最后,函数返回累加得到的曼哈顿距离作为对当前状态和目标状态之间距离的估计值。

玩具的合理性:曼哈顿距离可以衡量每个数字所在位置与其目标位置之间的距离,越接近目标位置的数字,其曼哈顿距离越小。因此,将曼哈顿距离作为启发函数可以帮助A*算法在搜索过程中选择更接近目标状态的路径,提高求解效率。

from queue import PriorityQueue

# 八数码问题求解函数
def solve_puzzle(start, target):
    # 定义移动方向
    directions = [(0, 1), (0, -1), (1, 0), (-1, 0)]
    
    # 转换初始状态和目标状态为字符串
    start_str = ''.join(str(num) for num in start)
    target_str = ''.join(str(num) for num in target)
    
    # 定义启发函数:曼哈顿距离
    def heuristic(state):
        manhattan_distance = 0
        for i in range(9):
            if state[i] != '0':
                curr_row, curr_col = i // 3, i % 3
                target_row, target_col = (int(state[i])-1) // 3, (int(state[i])-1) % 3
                manhattan_distance += abs(curr_row - target_row) + abs(curr_col - target_col)
        return manhattan_distance
    
    # 创建优先队列并加入初始状态
    queue = PriorityQueue()
    queue.put((heuristic(start_str), start_str, 0))
    
    # 创建集合记录已访问的状态
    visited = set([start_str])
    
    while not queue.empty():
        # 出队
        _, curr_state, steps = queue.get()
        
        # 检查是否达到目标状态
        if curr_state == target_str:
            return steps
        
        # 找到空格位置
        zero_pos = curr_state.index('0')
        zero_row, zero_col = zero_pos // 3, zero_pos % 3
        
        # 遍历四个方向的移动
        for direction in directions:
            new_row, new_col = zero_row + direction[0], zero_col + direction[1]
            
            # 检查移动是否合法
            if 0 <= new_row < 3 and 0 <= new_col < 3:
                # 创建新状态
                new_pos = new_row * 3 + new_col
                new_state = list(curr_state)
                new_state[zero_pos], new_state[new_pos] = new_state[new_pos], new_state[zero_pos]
                new_state_str = ''.join(new_state)
                
                # 判断是否已访问过该状态
                if new_state_str not in visited:
                    # 加入队列并标记为已访问
                    queue.put((heuristic(new_state_str) + steps + 1, new_state_str, steps + 1))
                    visited.add(new_state_str)
    
    # 如果无解,则返回-1
    return -1

本文为娱乐之作,如有错误欢迎指出。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值