Python 什么';这是我的BFS实现中8个方块的问题

Python 什么';这是我的BFS实现中8个方块的问题,python,algorithm,data-structures,Python,Algorithm,Data Structures,我设计了一个算法,可以使用BFS或DFS解决8平方问题。当前的问题是,它运行了无限长的时间。如果我让它运行30分钟左右。它以我的RAM满为结束。我的实现中有什么问题。我调试此代码失败。 提前谢谢 import copy import time import pprint def get_empty_board(): return [ [7,4,2], [8,3,0], [1,5,6] ] end_state = [

我设计了一个算法,可以使用BFS或DFS解决8平方问题。当前的问题是,它运行了无限长的时间。如果我让它运行30分钟左右。它以我的RAM满为结束。我的实现中有什么问题。我调试此代码失败。 提前谢谢

import copy
import time
import pprint
def get_empty_board():
    return [
        [7,4,2],
        [8,3,0],
        [1,5,6]
        ]
end_state = [
    [1,2,3],
    [8,0,4],
    [7,6,5]
    ]
def solve_squares(board):
    states_explored = list()
    states = [ (neighbor,[board] + [neighbor]) for neighbor in get_neighbors(board) ]
    while states:
        print(len(states))
        current_state = states.pop(0)
        if (current_state[0]) in states_explored:
            continue
        # if len(states) > 300:
            # states =[ states[0] ]
        # pprint.pprint(current_state)
        # pprint.pprint(current_state)
        if current_state[0] == end_state:
            return True,current_state[1]
        neighbors = get_neighbors(current_state[0])
        states_explored.append(current_state[0])
        for neighbor in neighbors:
            if (neighbor) not in states_explored:
                states.append((neighbor,current_state[1] + [neighbor]))
                states_explored.append((neighbor[0]))
    return False,None
def get_neighbors(board):
    x = None
    y = None
    neighbors = list()
    for i in range(len(board)):
        for j in range(len(board[0])):
            if board[i][j] == 0:
                x = i
                y = j
                break
    # print(x,y)
    for i in range(len(board)):
        for j in range(len(board[0])):
            if abs(i-x) <= 1 and abs(j-y) <= 1:
                if abs(i-x) != abs(j-y):
                    # print(i,j)
                    new_state = copy.deepcopy(board)
                    new_state[x][y] = new_state[i][j]
                    new_state[i][j] = 0
                    # pprint.pprint(new_state)
                    # time.sleep(5)
                    neighbors.append(new_state)
    return neighbors
def main():
    result,path = solve_squares(get_empty_board())
    print(result)
    print(path)
main()
导入副本
导入时间
导入pprint
def get_empty_board():
返回[
[7,4,2],
[8,3,0],
[1,5,6]
]
结束状态=[
[1,2,3],
[8,0,4],
[7,6,5]
]
def解算方格(板):
州=列表()
states=[(邻居,[board]+[neighbor])表示get_邻居(board)]
而缔约国:
印刷品(国家)
当前状态=states.pop(0)
如果(当前_状态[0])处于_探索状态:
持续
#如果len(状态)>300:
#州=[州[0]]
#pprint.pprint(当前状态)
#pprint.pprint(当前状态)
如果当前\u状态[0]==结束\u状态:
返回True,当前_状态[1]
邻居=获取邻居(当前状态[0])
states\u explored.append(当前\u状态[0])
对于邻居中的邻居:
如果(邻居)未处于以下状态:
states.append((邻居,当前_状态[1]+[邻居])
states_.append((邻居[0]))
返回False,无
def get_邻居(板):
x=无
y=无
邻居=列表()
对于范围内的i(len(board)):
对于范围内的j(len(板[0]):
如果板[i][j]==0:
x=i
y=j
打破
#打印(x,y)
对于范围内的i(len(board)):
对于范围内的j(len(板[0]):

如果abs(i-x)您的解决方案中需要进行两项改进:

  • 您正在使用python列表(例如,
    states\u explored
    )跟踪您已经访问过的电路板配置。现在,s中
    x的列表的平均大小写复杂度为:O(n)。为此,您需要应用一些有效的数据结构(例如,
    set
    )。您可以查看有关此优化的详细讨论
  • 您的RAM已满,因为您正在队列中存储每个发现的板配置的完整路径(例如,
    状态
    )。这是非常不必要的,而且内存效率低下。为了解决这个问题,您可以使用有效的数据结构(例如,
    map
    )来存储给定状态的父状态。发现目标后,需要通过回溯地图来构建路径
  • 我们可以通过下面的示例来可视化第二次优化。比方说,您正在将
    映射为键值:

    <initial-state, NaN>
    <state-1, initial-state>
    <state-2, state-1>
    <state-3, state-2>
    ...
    ...
    <final-state, state-n>
    
    
    ...
    ...
    
    现在,在发现
    final state
    之后,我们可以在地图中查询
    final state
    的父级是什么。然后,递归地进行此查询,直到到达
    初始状态


    如果您应用这两种优化,您将在运行时和内存消耗方面获得巨大的改进。

    解决方案中需要几项改进:

  • 您正在使用python列表(例如,
    states\u explored
    )跟踪您已经访问过的电路板配置。现在,s中
    x的列表的平均大小写复杂度为:O(n)。为此,您需要应用一些有效的数据结构(例如,
    set
    )。您可以查看有关此优化的详细讨论
  • 您的RAM已满,因为您正在队列中存储每个发现的板配置的完整路径(例如,
    状态
    )。这是非常不必要的,而且内存效率低下。为了解决这个问题,您可以使用有效的数据结构(例如,
    map
    )来存储给定状态的父状态。发现目标后,需要通过回溯地图来构建路径
  • 我们可以通过下面的示例来可视化第二次优化。比方说,您正在将
    映射为键值:

    <initial-state, NaN>
    <state-1, initial-state>
    <state-2, state-1>
    <state-3, state-2>
    ...
    ...
    <final-state, state-n>
    
    
    ...
    ...
    
    现在,在发现
    final state
    之后,我们可以在地图中查询
    final state
    的父级是什么。然后,递归地进行此查询,直到到达
    初始状态


    如果您应用这两种优化,您将在运行时以及内存消耗方面获得巨大的改进。

    问题确实是性能。要了解速度,请在代码中输入以下
    print
    (仅此一项):

    您将看到它是如何随着进程而减慢的。我编写了一个更高效的实现,发现该算法需要访问100000多个州才能找到解决方案。按照您看到上述行输出行的速度,您可以想象需要很长时间才能完成。我没有耐心等它

    请注意,我在您的代码中发现了一个错误,但它不会损害算法:

    states_explored.append((neighbor[0]))
    
    这种说法是错误的,原因有二:

    • neighbor
      是一块板,因此从中获取索引
      [0]
      ,将生成该板的第一行,而这对该算法是无用的
    • 如果您将其更正为仅
      邻居
      ,它将变得重要,但会停止算法的运行,因为当这些邻居从队列中弹出时,搜索将停止
    所以这一行应该省略

    以下是一些提高算法效率的方法:

    states_explored.append((neighbor[0]))
    
    • 使用原语值表示电路板,而不是列表。例如,一个9个字符的字符串就可以完成这项工作。Python处理字符串的速度比处理二维列表的速度快得多。这也意味着您不需要
      deep\u copy
    • 不要使用列表跟踪已访问的状态。使用一套——或者也包括下一点——措辞
      # move mapping (based on position of the zero/empty block)
      moves = { 0: [1,3],
                1: [0,2,4],
                2: [1,5],
                3: [0,4,6],
                4: [1,3,5,7],
                5: [2,4,8],
                6: [3,7],
                7: [4,6,8],
                8: [5,7] }
      
      from collections import deque               
      def solve(board,target=(1,2,3,4,5,6,7,8,0)):
          if isinstance(board[0],list):  # flatten board
              board  = tuple(p for r in board for p in r)
          if isinstance(target[0],list): # flatten target
              target = tuple(p for r in target for p in r)
          seen = set()
          stack = deque([(board,[])])         # BFS stack with board/path
          while stack:
              board,path = stack.popleft()    # consume stack breadth first
              z = board.index(0)              # position of empty block
              for m in moves[z]:              # possible moves
                  played = list(board)                       
                  played[z],played[m] = played[m],played[z] # execute move
                  played = tuple(played)                    # board as tuple
                  if played in seen: continue               # skip visited layouts
                  if played == target: return path + [m]    # check target
                  seen.add(played)                          
                  stack.append((played,path+[m]))           # stack move result
      
      initial = [ [7,4,2],
                  [8,3,0],
                  [1,5,6]
                ]
      target  = [ [1,2,3],
                  [8,0,4],
                  [7,6,5]
                ]
      
      solution = solve(initial,target) # runs in 0.19 sec.
      
      # solution = (flat) positions of block to move to the zero/empty spot
      [4, 1, 0, 3, 6, 7, 4, 1, 0, 3, 4, 1, 2, 5, 8, 7, 6, 3, 4, 5, 8, 7, 4]
      
      
      board = [p for r in initial for p in r]
      print(*(board[i:i+3] for i in range(0,9,3)),sep="\n")
      for m in solution:
          print(f"move {board[m]}:")
          z = board.index(0)
          board[z],board[m] = board[m],board[z]
          print(*(board[i:i+3] for i in range(0,9,3)),sep="\n")
      
      [7, 4, 2]
      [8, 3, 0]
      [1, 5, 6]
      move 3:
      [7, 4, 2]
      [8, 0, 3]
      [1, 5, 6]
      move 4:
      [7, 0, 2]
      [8, 4, 3]
      [1, 5, 6]
      move 7:
      [0, 7, 2]
      [8, 4, 3]
      [1, 5, 6]
      move 8:
      [8, 7, 2]
      [0, 4, 3]
      [1, 5, 6]
      move 1:
      [8, 7, 2]
      [1, 4, 3]
      [0, 5, 6]
      move 5:
      [8, 7, 2]
      [1, 4, 3]
      [5, 0, 6]
      move 4:
      [8, 7, 2]
      [1, 0, 3]
      [5, 4, 6]
      move 7:
      [8, 0, 2]
      [1, 7, 3]
      [5, 4, 6]
      move 8:
      [0, 8, 2]
      [1, 7, 3]
      [5, 4, 6]
      move 1:
      [1, 8, 2]
      [0, 7, 3]
      [5, 4, 6]
      move 7:
      [1, 8, 2]
      [7, 0, 3]
      [5, 4, 6]
      move 8:
      [1, 0, 2]
      [7, 8, 3]
      [5, 4, 6]
      move 2:
      [1, 2, 0]
      [7, 8, 3]
      [5, 4, 6]
      move 3:
      [1, 2, 3]
      [7, 8, 0]
      [5, 4, 6]
      move 6:
      [1, 2, 3]
      [7, 8, 6]
      [5, 4, 0]
      move 4:
      [1, 2, 3]
      [7, 8, 6]
      [5, 0, 4]
      move 5:
      [1, 2, 3]
      [7, 8, 6]
      [0, 5, 4]
      move 7:
      [1, 2, 3]
      [0, 8, 6]
      [7, 5, 4]
      move 8:
      [1, 2, 3]
      [8, 0, 6]
      [7, 5, 4]
      move 6:
      [1, 2, 3]
      [8, 6, 0]
      [7, 5, 4]
      move 4:
      [1, 2, 3]
      [8, 6, 4]
      [7, 5, 0]
      move 5:
      [1, 2, 3]
      [8, 6, 4]
      [7, 0, 5]
      move 6:
      [1, 2, 3]
      [8, 0, 4]
      [7, 6, 5]