Python:通過隨機遊走解決八皇后問題
阿新 • • 發佈:2020-12-30
文章目錄
1 八皇后問題
有一個8乘8的棋盤,現在要將八個皇后放到棋盤上,滿足:對於每一個皇后,在自己所在的行、列、兩個對角線都沒有其他皇后。
規定初始狀態為【空棋盤】,動作為【每次只在最左面未放置皇后的列上放一個皇后】。這樣就使得棋盤的同列最多隻能出現一個皇后。
2 程式程式碼
2.1 程式1
程式1:functions.py。包括兩個函式:attacked_queens_pairs, display_board,分別完成【計算序列對應棋盤的互相攻擊的皇后對數】和【列印輸出序列對應的棋盤】的功能。如下:
import numpy as np
def attacked_queens_pairs(seqs, pos):
"""
計算序列對應棋盤的【互相攻擊的皇后對數n】
因為主程式只在最左面未放置皇后的列上放一個皇后,因此只需檢查放置了皇后的前幾列
引數pos的作用是限制只統計前pos列【互相攻擊的皇后對數n】
並且只需要檢查當前棋盤的皇后在各自的行和兩條對角線上是否有其他皇后
"""
a = np.array([0] * 81) # 建立一個有81個0的一維陣列
a = a. reshape(9, 9) # 改為9*9二維陣列。為方便後面使用,只用後八行和後八列的8*8部分,作為一個空白棋盤
n = 0 # 互相攻擊的皇后對數初始化為0
for i in range(1, pos+1):
a[seqs[i - 1]][i] = 1 # 根據序列的前pos個數,按從第1列到pos列的順序,在空白棋盤對應位置放一個皇后,生成當前序列對應的棋盤
for i in range(1, pos+1): # 只統計前pos列【互相攻擊的皇后對數n】
for k in list(range(1, i)) + list(range (i + 1, 9)): # 檢查每個皇后各自所在的行上是否有其他皇后
if a[seqs[i - 1]][k] == 1: # 有其他皇后
n += 1
t1 = t2 = seqs[i - 1]
for j in range(i - 1, 0, -1): # 看左半段的兩條對角線
if t1 != 1:
t1 -= 1
if a[t1][j] == 1:
n += 1 # 正對角線左半段上還有其他皇后
if t2 != 8:
t2 += 1
if a[t2][j] == 1:
n += 1 # 次對角線左半段上還有其他皇后
t1 = t2 = seqs[i - 1]
for j in range(i + 1, 9): # 看右半段的兩條對角線
if t1 != 1:
t1 -= 1
if a[t1][j] == 1:
n += 1 # 正對角線右半段上還有其他皇后
if t2 != 8:
t2 += 1
if a[t2][j] == 1:
n += 1 # 次對角線右半段上還有其他皇后
return int(n/2) # 返回n/2,因為A攻擊B也意味著B攻擊A,因此返回n的一半
def display_board(seqs):
"""
顯示序列對應的棋盤
"""
board = np.array([0] * 81) # 建立一個有81個0的一維陣列
board = board.reshape(9, 9) # 改變為9*9二維陣列,為了後面方便使用,只用後八行和後八列的8*8部分,作為一個空白棋盤
for i in range(1, 9):
board[seqs[i - 1]][i] = 1 # 根據序列,從第一列到最後一列的順序,在對應位置放一個皇后,生成當前序列對應的棋盤
print('對應棋盤如下:')
for i in board[1:]:
for j in i[1:]:
print(j, ' ', end="") # 有了end="",print就不會換行
print() # 輸出完一行後再換行,這裡不能是print('\n'),否則會換兩行
print('攻擊的皇后對數為' + str(attacked_queens_pairs(seqs,8)))
此程式無任何輸出,只是定義了2個函式以供主程式呼叫。
2.2 程式2
程式2:main.py。為主程式,通過呼叫程式2的兩個函式,完成隨機遊走解決八皇后問題的全過程。如下:
import random
from functions import attacked_queens_pairs, display_board
seqs = [0] * 8 # 初始化序列
flag = 0 # 失敗標誌
for i in range(8):
nums = list(range(1, 9)) # 從nums中選擇在當前列放置皇后的位置
if flag == 1: # 失敗則結束迴圈
break
while True:
try:
temp = random.choice(nums) # 隨機挑選,即隨機遊走
seqs[i] = temp # 暫時將皇后放在當前列的第temp行
nums.remove(temp) # 從nums移除已產生的隨機數
if attacked_queens_pairs(seqs, i+1) == 0: # 將皇后放在當前列的第temp行後,若已放置皇后的前i+1列中無互相攻擊的皇后,則進行下一列位置的選擇
break
except IndexError:
print('本次計算失敗,請重新執行本程式!')
flag = 1 # 已失敗
break
if flag == 0:
print('已找到解序列:' + str(seqs))
display_board(seqs)
一種輸出如下:
已找到解序列:[7, 1, 3, 8, 6, 4, 2, 5]
對應棋盤如下:
0 1 0 0 0 0 0 0
0 0 0 0 0 0 1 0
0 0 1 0 0 0 0 0
0 0 0 0 0 1 0 0
0 0 0 0 0 0 0 1
0 0 0 0 1 0 0 0
1 0 0 0 0 0 0 0
0 0 0 1 0 0 0 0
攻擊的皇后對數為0
另一種輸出如下:
本次計算失敗,請重新執行本程式!
上面列出了兩種輸出,第一種找到了解序列,第二種沒找到。因為演算法是將皇后隨機放置在當前列的任意位置,若已放置皇后的前幾列中無互相攻擊的皇后,則進行下一列位置的選擇,因此過程中可能會達到【無合適位置可放】的狀態,不得不宣告失敗,只能通過重新執行程式來重新進行探索。
3 評價
主程式簡短易懂,但是要得到解可能需要多執行幾次,如果失敗了不是您的原因,重新執行就好了。
END