繁体   English   中英

回溯 8 Queens Python 问题

[英]Backtracking 8 Queens Python problems

我已经开始用 Python 中的回溯来解决 8 个皇后问题。 一切都很好。 它甚至打印出了第一个答案。 然而,它坚持自己的第一次回溯尝试。

任务听起来是这样的:

实现一个 Python 函数来解决 8 个皇后的难题。 8皇后拼图包括在棋盘上放置8个皇后,因此,没有一个皇后可以捕获任何其他皇后。 请注意,皇后可以在任何方向上正交或对角移动。

您应该实现一个函数 solve() ,当调用该函数时,它会打印拼图的第一个解决方案,然后等待输入。 一旦用户按下“回车”,就会打印下一个解决方案,依此类推。

- 您的程序应该能够找到谜题的所有解决方案,并且每个解决方案只能找到一次。 '

- 修改程序应该很容易,因此它适用于不同的电路板尺寸。 提示:

- 在任何一排,只有一个女王。 因此,您需要计算的只是可以放置 8 个皇后中的每一个的列。

- 您应该实现一个递归函数 solve(n) ,它为第 n+1 个皇后找到一个位置,然后为第 n+1 个皇后递归调用自己(除非所有皇后都已被放置)。 它应该使用回溯系统地探索所有可能性。

- 允许(并鼓励)您定义额外的函数(solve() 除外)以在必要时提高代码质量。


    import numpy as np
    grid = np.zeros((8, 8), dtype = int)
    
    
    def possible(y, n):
        global solved
        global grid
        for i in range(0, 8):
            if grid[y][i] == n:
                return False
        try:
            for item in solved[str(y)]:
                if grid[y].all() == item.all():
                    return False
        except KeyError:
            return True
        return True
    
    max_y = 7
    max_x = 7
    
    def print_grid():
        global grid
        for line in grid:
            for square in line:
                if square == 0:
                    print(".", end = " ")
                else :
                    print("Q", end = " ")
            print()
    
    solved = {}
    
    def prefilled_solved():
        global solved
        for i in range(0, len(grid[0])):
            solved[f"{str(i)}"] = []
    
    def solve(y=0):
        global grid
        global solved
        while y < 8:
            for x in range(0, 8):
                if grid[y][x] == 0:
                    if possible(x, 1):
                        grid[y][x] = 1
                        solved[f"{str(y)}"].append(grid[y])
                        y += 1
                        solve(y)
                        #y -= 1 or y = 0 or y -=2
                        # backtracking - bad choice
                        # grid[y][x] = 0
    
        print_grid()
        print(grid)
        return
        input("More?")
    
    if __name__ == '__main__':
        prefilled_solved()
        solve()

我遵循了@mkam 的建议,现在我得到了皇后的随机星座,但我已经完全摆脱了递归。

```import numpy as np
grid = np.zeros((8, 8), dtype = int)
from random import randint, shuffle, choice
from itertools import permutations


constellations_drawn  = []


def print_grid():
    global grid
    for line in grid:
        for square in line:
            if square == 0:
                print(".", end = " ")
            else :
                print("Q", end = " ")
        print()

solved = []

def prefilled_solved():
    global solved
    new_board = ['1', '2', '3', '4', '5', '6', '7', '8']
    new_board_i = ''.join(new_board)
    solved = permutations(new_board_i, 8)



def solve(y=0):
    global grid
    global solved
    global constellations_drawn
    list_solved = list(solved)
    len_solved = len(list_solved)
    board_drawn = list_solved[randint(0, len_solved-1)]
    board_drawn_str = ''.join(board_drawn)
    while board_drawn_str in constellations_drawn:
        board_drawn = list_solved[randint(0, len_solved - 1)]
    new_board_list = [int(item) for item in board_drawn]
    for i, x in enumerate(new_board_list):
        if grid[i-1][x-1] == 0:
            grid[i-1][x-1] = 1
            #y += 1
            #solve(y)
            #y -= 1 or y = 0 or y -=2
            # backtracking - bad choice
            # grid[y][x] = 0
    constellations_drawn.append(board_drawn_str)
    print_grid()
    print(grid)
    return
    input("More?")

if __name__ == '__main__':
    prefilled_solved()
    solve()

I've merged the code of @mkam and mine. And it works. I still use numpy ndarray.

import numpy as np

    from numpy.core._multiarray_umath import ndarray
    
    
    def print_grid(solutions_found, board) -> None:
        line: ndarray
        len_board = len(board)
        grid: ndarray = np.zeros((len_board, len_board), dtype=int)
        for i, number in enumerate(board):
            grid[i - 1][number - 1] = 1
        for line in grid:
            for square in line:
                if square == 0:
                    print(".", end=" ")
                else:
                    print("Q", end=" ")
            print()
        print(f'Solution - {solutions_found}')
    
    
    def solve(boardsize, board=[], solutions_found=0):
        if len(board) == boardsize:
            solutions_found += 1
            print_grid(solutions_found, board)
        else:
            for q in [col for col in range(1, boardsize + 1) if col not in board]:
                if is_safe(q, board):
                    solutions_found = solve(boardsize, board + [q], solutions_found)
        return solutions_found
    
    
    def is_safe(q, board, x=1):
        if not board:
            return True
        if board[-1] in [q + x, q - x]:
            return False
        return is_safe(q, board[:-1], x + 1)
    
    
    if __name__ == '__main__':
        solve(8)

这是一个如何递归解决 8-Queens 问题的例子,使用一个简单的列表来表示板。 [8, 4, 1, 3, 6, 2, 7, 5]这样的列表代表一个棋盘从上到下的8行,顶行第8列有Q,第4列是Q第 7 行,第 6 行的第 1 列……和底行的第 5 列。

一个解决方案是从一个空板[]开始构建的,方法是在下一行的列位置放置一个 Q,在那里它不能被采用。 可能的位置是之前尚未被采用的列(这是函数solve的 for 循环)。 对于这些可能的列位置中的每一个,函数issafe检查该位置是否安全,不会被棋盘上的 Q 对角地占据。 如果位置是安全的,则解决方案板会扩展另一行,并且解决方案递归,直到板被填满( len(board) == boardsize ),此时解决方案计数增加并显示板。

请注意,函数solve适用于任何尺寸的方形棋盘 - 所需的尺寸作为参数传递给solve ,该函数返回找到的解决方案的总数。

希望这有助于解释如何在没有numpy递归解决 8-Queens 问题。

def display(solution_number, board):
    row = '|   ' * len(board) + '|'
    hr  = '+---' * len(board) + '+'
    for col in board:
        print(hr)
        print(row[:col*4-3],'Q',row[col*4:])
    print(f'{hr}\n{board}\nSolution - {solution_number}\n')

def issafe(q, board, x=1):
    if not board: return True
    if board[-1] in [q+x,q-x]: return False
    return issafe(q, board[:-1], x+1)

def solve(boardsize, board=[], solutions_found=0):
    if len(board) == boardsize:
        solutions_found += 1
        display(solutions_found, board)
    else:
        for q in [col for col in range(1,boardsize+1) if col not in board]:
            if issafe(q,board):
                solutions_found = solve(boardsize, board + [q], solutions_found)
    return solutions_found

if __name__ == '__main__':
    solutions = solve(8)
    print(f'{solutions} solutions found')

您提到使用yield - 这也是可能的,并且会将solve转换为生成器,一次生成一个解决方案。 然后,您的程序可以使用for循环依次接收每个解决方案并根据需要对其进行处理。 以下yield解决方案适用于 Python v.3.3 以上,因为它使用yield from

def display(solution_number, board):
    row = '|   ' * len(board) + '|'
    hr  = '+---' * len(board) + '+'
    for col in board:
        print(hr)
        print(row[:col*4-3],'Q',row[col*4:])
    print(f'{hr}\n{board}\nSolution - {solution_number}\n')

def issafe(q, board, x=1):
    if not board: return True
    if board[-1] in [q+x,q-x]: return False
    return issafe(q, board[:-1], x+1)

def solve(boardsize, board=[]):
    if len(board) == boardsize:
        yield board
    else:
        for q in [col for col in range(1,boardsize+1) if col not in board]:
            if issafe(q,board):
                yield from solve(boardsize, board + [q])

if __name__ == '__main__':
    for solutionnumber, solution in enumerate(solve(8)):
        display(solutionnumber+1, solution)

如果递归函数issafe看起来令人困惑,这里是一个非递归版本:

def issafe(q, board):
    x = len(board)
    for col in board:
        if col in [q+x,q-x]: return False
        x -= 1
    return True

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM