You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

8x8棋盘递归骑士巡游Python实现的索引错误问题排查

解决骑士巡游递归回溯的IndexError问题及基础优化建议

嘿,作为刚学Python两个月就能挑战骑士巡游这种经典算法问题的新手,已经相当厉害了!咱们一步步拆解你遇到的问题,先搞定错误,再优化逻辑~

先分析你遇到的IndexError原因

你提到的IndexError: Cannot choose from an empty sequence,本质是当递归到某个位置时,Next_move函数返回了空的可行走法列表,但你的代码还是尝试从空列表里选走法导致的。结合你的描述,大概率是这两个逻辑点出了问题:

  • 回溯时的状态没有正确回退:递归回溯的核心是「尝试路径→走不通→撤销当前选择」,如果你的solution_list或pos_visited在走不通的时候没有移除当前位置,会导致后续路径误以为这些位置是永久死路,最终过滤掉所有可行走法。
  • pos_visited的定义或使用逻辑有误:你说它是「记录无后续可行走法的位置」,但骑士巡游中,某个位置在当前路径里是死路,不代表在其他路径里也走不通——提前标记死路会错误缩小搜索范围,导致可行走法被误过滤。

基础排查步骤(适合新手的调试方法)

  • 打印关键状态调试:在递归函数里,每次调用前打印这些信息:当前solution_list的长度、当前位置坐标、Next_move返回的可行走法列表。这样你能直观看到是哪一步的走法列表变空,是真的没有可行走法,还是逻辑错误过滤掉了正确选项。
  • 检查Next_move的过滤条件:确认它只过滤「已经在当前巡游路径里的位置」和「棋盘外的位置」,不要错误加入pos_visited的判断(除非你能确保pos_visited的回退逻辑完全正确)。
  • 验证回溯时的回退操作:当递归返回False(表示当前路径走不通)时,必须把当前位置从solution_list中移除,如果用了单独的已访问集合,也要同步移除。

修正后的基础递归回溯实现(解决IndexError)

先把逻辑理顺,去掉容易混淆的pos_visited,改用「已访问集合+路径列表」的标准回溯模式,同时处理空走法的情况:

# 初始化全局变量(新手用全局变量更容易理解,后续可以改成类封装)
solution_list = []
visited = set()
# 骑士的8种可能移动方向
KNIGHT_MOVES = [(-2, -1), (-2, 1), (-1, -2), (-1, 2),
                (1, -2), (1, 2), (2, -1), (2, 1)]

def next_move(current_pos):
    """生成当前位置的所有可行走法"""
    x, y = current_pos
    possible_moves = []
    for dx, dy in KNIGHT_MOVES:
        nx = x + dx
        ny = y + dy
        # 检查是否在棋盘内,且未被访问过
        if 0 <= nx < 8 and 0 <= ny < 8 and (nx, ny) not in visited:
            possible_moves.append((nx, ny))
    return possible_moves

def knight_tour(current_pos):
    # 记录当前位置到路径和已访问集合
    solution_list.append(current_pos)
    visited.add(current_pos)
    
    # 终止条件:已经走完64个格子
    if len(solution_list) == 64:
        print("找到巡游路径:")
        print(solution_list)
        return True
    
    # 尝试所有可行走法
    moves = next_move(current_pos)
    for move in moves:
        if knight_tour(move):
            return True
    
    # 所有走法都尝试失败,回溯:移除当前位置的记录
    solution_list.pop()
    visited.remove(current_pos)
    return False

# 从(0,0)位置开始尝试
if __name__ == "__main__":
    knight_tour((0, 0))

这个版本做了这些关键改进:

  • 去掉了容易出错的pos_visited,只用visited集合跟踪当前路径已走过的位置
  • 当next_move返回空列表时,循环不会执行,直接触发回溯(pop和remove),不会尝试从空列表选走法,自然避免了IndexError
  • 明确了终止条件:路径长度达到64时返回成功

新手友好的基础优化建议

  1. 提升查找效率:用set存储已访问位置,因为集合的in操作是O(1),比列表的O(n)快很多,尤其当路径变长时,差异会很明显。
  2. 避免全局变量(可选):如果想让代码更规范,可以把路径和已访问集合作为参数传入递归函数,或者封装成类,不过新手先从全局变量入手没问题。
  3. 尝试启发式优化(Warnsdorff算法):纯回溯在8x8棋盘上会非常慢(因为搜索空间太大),Warnsdorff算法是适合新手理解的启发式:每次优先选择「下一步可行走法最少」的位置,能大幅减少无效搜索。实现起来也很简单,只需要修改next_move的返回顺序:
def next_move(current_pos):
    x, y = current_pos
    possible_moves = []
    for dx, dy in KNIGHT_MOVES:
        nx = x + dx
        ny = y + dy
        if 0 <= nx < 8 and 0 <= ny < 8 and (nx, ny) not in visited:
            # 计算该下一步位置的可行走法数量
            count = 0
            for dx2, dy2 in KNIGHT_MOVES:
                nnx = nx + dx2
                nny = ny + dy2
                if 0 <= nnx < 8 and 0 <= nny < 8 and (nnx, nny) not in visited:
                    count += 1
            possible_moves.append((count, (nx, ny)))
    # 按可行走法数量从小到大排序,优先选走法少的位置
    possible_moves.sort()
    # 只返回位置列表
    return [move[1] for move in possible_moves]

这个优化后,几乎能瞬间找到8x8棋盘的巡游路径。

最后总结

先把基础回溯的逻辑跑通(解决IndexError),再逐步尝试启发式优化。作为学Python两个月的新手,能走到这一步已经很棒了,慢慢来,每一步调试都是积累经验的过程~

内容的提问来源于stack exchange,提问作者Santosh Kutti

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.08 17:02:36