📜  N皇后问题使用带有随机邻居的爬山进行本地搜索(1)

📅  最后修改于: 2023-12-03 15:18:04.836000             🧑  作者: Mango

N皇后问题使用带有随机邻居的爬山进行本地搜索

简介

N皇后问题是计算机科学领域中经典的问题之一,其目的是在N×N的棋盘上放置N个皇后,使得每个皇后都无法攻击另一个皇后。其中攻击包括:在同一行、同一列或对角线上。

爬山算法是一种局部搜索算法,其基本思想是通过不断地移动当前解的邻居中最优的解来优化目标函数值。然而,基本的爬山算法极易陷入局部最优解的情况,导致算法无法找到全局最优解。

为了避免陷入局部最优解的情况,我们可以加入随机邻居的机制。这种改进方法可以使算法跳出当前的局部最优,发现新的、更优的解。

程序实现

我们使用Python语言来实现这个问题。实现过程中我们需要用到numpy库和random库。

首先,我们定义了一个Queen类来存储每个皇后的坐标,并在棋盘上进行布局。通过定义get_indexes函数来获取每个皇后的位置。

import numpy as np
import random

class Queen:
    def __init__(self, x, y):
        self.x = x
        self.y = y

class NQueen:
    def __init__(self, N):
        self.N = N
        self.board = np.zeros((N, N), dtype=int)
        self.queens = []

    def init_board(self):
        for i in range(self.N):
            j = random.randint(0, self.N - 1)
            self.board[i, j] = 1
            self.queens.append(Queen(i, j))

    def get_indexes(self):
        indexes = []
        for i in range(self.N):
            for j in range(self.N):
                if self.board[i][j]:
                    indexes.append((i,j))
        return indexes

接下来,我们需要定义评估函数,用来判断当前的解是否是一个可行的解。评估函数判断以下三种情况:

1、同一行不能出现多个皇后;

2、同一列不能出现多个皇后;

3、同一对角线不能出现多个皇后。

    def evaluate(self):
        clashes = 0
        for i in range(self.N):
            for j in range(self.N):
                if self.board[i][j] == 1:
                    for k in range(self.N):
                        if (k != i and self.board[k][j] == 1) or (k != j and self.board[i][k] == 1):
                            clashes += 1

        for i in range(self.N):
            for j in range(self.N):
                if self.board[i][j]:
                    left, right = j - min(i, j), j + min(self.N - 1 - j, i)
                    for k in range(self.N):
                        if (i != k and left + k < self.N and self.board[k][left + k]) or \
                                (i != k and right - k >= 0 and self.board[k][right - k]):
                            clashes += 1
        return clashes

实现随机邻居的方法如下:

    def get_neighbors(self):
        neighbors = []
        queens = list(self.queens)
        for i in range(self.N):
            for j in range(self.N):
                if not self.board[i][j]:
                    queens_copy = list(queens)
                    for q in queens_copy:
                        if q.x == i:
                            queens_copy.remove(q)
                            break
                    queens_copy.append(Queen(i, j))

                    board_copy = np.zeros((self.N, self.N), dtype=int)
                    for q in queens_copy:
                        board_copy[q.x][q.y] = 1

                    neighbor = NQueen(self.N)
                    neighbor.board = board_copy
                    neighbor.queens = queens_copy
                    neighbors.append(neighbor)

        return neighbors

最后,我们使用简单的爬山算法来解决问题。每次移动只选择其中最好的邻居,而不是全部都移动。这个算法可以保证局部最优解的逼近速度,并在算法结束时返回当前的最优解。

    def climb_hill(self):
        self.init_board()
        best_eval = self.evaluate()
        print("Initial board evaluation score: {}".format(best_eval))

        while best_eval != 0:
            neighbors = self.get_neighbors()
            best_neighbor = self
            for neighbor in neighbors:
                if neighbor.evaluate() < best_eval:
                    best_eval = neighbor.evaluate()
                    best_neighbor = neighbor
            if best_neighbor == self:
                break
            self.board = best_neighbor.board
            self.queens = best_neighbor.queens

        if best_eval == 0:
            print("Found soluiton:")
            print(self.board)
            return self.get_indexes()
        else:
            print("Failed to find solution.")
            return None
结论

本文介绍了如何使用带有随机邻居的爬山算法来解决N皇后问题。通过添加随机邻居可以避免算法陷入局部最优解,并且可以更快的找到全局最优解。

完整代码实现如下:

import numpy as np
import random

class Queen:
    def __init__(self, x, y):
        self.x = x
        self.y = y

class NQueen:
    def __init__(self, N):
        self.N = N
        self.board = np.zeros((N, N), dtype=int)
        self.queens = []

    def init_board(self):
        for i in range(self.N):
            j = random.randint(0, self.N - 1)
            self.board[i, j] = 1
            self.queens.append(Queen(i, j))

    def get_indexes(self):
        indexes = []
        for i in range(self.N):
            for j in range(self.N):
                if self.board[i][j]:
                    indexes.append((i,j))
        return indexes

    def evaluate(self):
        clashes = 0
        for i in range(self.N):
            for j in range(self.N):
                if self.board[i][j] == 1:
                    for k in range(self.N):
                        if (k != i and self.board[k][j] == 1) or (k != j and self.board[i][k] == 1):
                            clashes += 1

        for i in range(self.N):
            for j in range(self.N):
                if self.board[i][j]:
                    left, right = j - min(i, j), j + min(self.N - 1 - j, i)
                    for k in range(self.N):
                        if (i != k and left + k < self.N and self.board[k][left + k]) or \
                                (i != k and right - k >= 0 and self.board[k][right - k]):
                            clashes += 1
        return clashes

    def get_neighbors(self):
        neighbors = []
        queens = list(self.queens)
        for i in range(self.N):
            for j in range(self.N):
                if not self.board[i][j]:
                    queens_copy = list(queens)
                    for q in queens_copy:
                        if q.x == i:
                            queens_copy.remove(q)
                            break
                    queens_copy.append(Queen(i, j))

                    board_copy = np.zeros((self.N, self.N), dtype=int)
                    for q in queens_copy:
                        board_copy[q.x][q.y] = 1

                    neighbor = NQueen(self.N)
                    neighbor.board = board_copy
                    neighbor.queens = queens_copy
                    neighbors.append(neighbor)

        return neighbors

    def climb_hill(self):
        self.init_board()
        best_eval = self.evaluate()
        print("Initial board evaluation score: {}".format(best_eval))

        while best_eval != 0:
            neighbors = self.get_neighbors()
            best_neighbor = self
            for neighbor in neighbors:
                if neighbor.evaluate() < best_eval:
                    best_eval = neighbor.evaluate()
                    best_neighbor = neighbor
            if best_neighbor == self:
                break
            self.board = best_neighbor.board
            self.queens = best_neighbor.queens

        if best_eval == 0:
            print("Found soluiton:")
            print(self.board)
            return self.get_indexes()
        else:
            print("Failed to find solution.")
            return None

nqueen = NQueen(8)
nqueen.climb_hill()