8人の女王(再帰呼び出し,バックトラック,matplotlibアニメーション)
Python3(3.10)で動くソースコード(.pyファイル .ipynbファイル)あります
「anaconda3」on .py「PyCharm」.ipynb「Jupyter Notebook」
パズル「8人の女王」は盤上に女王のコマを置いていき,人間の試行のように順序よく確認していく解き方が一番である。
このような場合は,バックトラックのアルゴリズムが一番簡単で確実です。そして,バックトラックは再帰呼び出しにするのがアルゴリズムが判りやすくなります。
左上隅からダウンロードできるプログラムの1つが,解を見つけるたびに出力して表示するもの。もう1つは女王のコマを置くことができる盤面を記憶しておきそれをアニメーションで見せるものです。
アニメは,matplotlibのArtistAnimationを使います。女王のコマを置くことができる盤面を保存して貯めておき,メインルーチンでArtistAnimationからそれを呼ぶだけです。ArtistAnimationの使い方は簡単ですのでアニメの解説は省略します。
再帰呼び出しを使うバックトラックは2つのプログラムでまったく同じものです。そのアルゴリズムを詳細に解説します。
◆◆パズル「8人の女王」◆◆(2023-11-14)Python3.10で動作確認済み
◆◆8人の女王のtkinterアニメーションはこちらのWeb◆◆
1.イニシャライズする 2.CALL SUB(1番目のステップ) 3.END SUB(N番目のステップ) 1.WHILE 方法がまだある A.次の方法を探す B.IF その方法が可能なら THEN 1.その方法を実行する 2.IF ゴールに達したなら THEN A.解を出力する 3.ELSE A.CALL SUB 4.その方法を取り消す 2.RETURN
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
Main 8人の女王 イニシャライズ Gosub EightQueens End Main Sub EightQueens: Y=1 While Y <= 8: Yを1~8まで順に選択 試行の計算 If 干渉がなく置くことができるなら Then 仮に実行して結果を記録する If X=8なら Then 解を出力 Else Stackに保存する Stack Pointer1増 X=X+1 Gosub EightQueens(recursiveなcall) 次のステップがもうなく戻ってきた 試行を取り消す Y=Y+1 Else Stackがまだ残っていれば Stack Pointer1減 Stackから戻す Return
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103
# -*- coding: utf-8 -*- # EithtQueens import numpy as np import matplotlib.pyplot as plt import sys # Initialize Num = 1 # 試行の回数 NumDebug = 0 # debug用 # 過去の試行パターンの記録 Old = [[0 for i in range(8)] for j in range(8)] Xpos = 0 NumSolution = 1 RowIsFull = [0 for col in range(8)] UpIsFull = [0 for col in range(15)] DownIsFull = [0 for col in range(15)] StackYpos = [0 for col in range(8)] # スタック StackXpos = [0 for col in range(8)] StackPointer = 1 # スタックポインタ sol = [] def QueensLayout(): """メインなサブルーチン """ global Num, NumDebug, Ypos, Xpos, NumSolution global Old, RowIsFull, UpIsFull, DownIsFull global StackPointer, StackYpos, StackXpos Ypos = 0 while Ypos <= 7: # あるXposにおいてYposを順に試みる if RowIsFull[Ypos] == 0 and UpIsFull[Xpos - Ypos + 7] == 0 \ and DownIsFull[Xpos + Ypos] == 0: # 干渉がなく置くことができるなら仮に試みる NumDebug += 1 Old[Xpos][Ypos] = 1 RowIsFull[Ypos] = 1 UpIsFull[Xpos - Ypos + 7] = 1 DownIsFull[Xpos + Ypos] = 1 # Progress(Num) # 途中経過を表示する # print raw_input(u'女王を置くことができる') if Xpos == 7: Solution(Num) # 解を発見 NumSolution += 1 # print raw_input(u'解を発見') else: # 次のステップに進める StackYpos[StackPointer] = Ypos StackXpos[StackPointer] = Xpos StackPointer += 1 Xpos += 1 QueensLayout() # recursiveなcall # 次のステップがもうなく戻ってきた RowIsFull[Ypos] = 0 UpIsFull[Xpos - Ypos + 7] = 0 DownIsFull[Xpos + Ypos]= 0 Old[Xpos][Ypos] = 0 Num -= 1 # 同じXposで次のYposを試みる準備 Ypos += 1 # while文の終了 else: # while文の終了後に実行 # recursiveなreturn StackPointer if StackPointer != 1: StackPointer -= 1 Ypos = StackYpos[StackPointer] Xpos = StackXpos[StackPointer] def Solution(Num): """解を表示 """ global sol x = [0.5, 1.5, 2.5, 3.5, 4.5, 5.5, 6.5, 7.5] y = [0 for col in range(8)] Ysol = [0 for col in range(8)] for i in range(8): for j in range(8): if Old[i][j] == 1: y[i] = j + 0.5 Ysol[i] = j sol = sol + [Ysol] # チェス盤を表示 # print(input(u'「OK」続行:グラフの「×」をクリック,「Cancel」終了')) plt.plot(x, y, 'o', markersize=30) plt.xlim([0,8]) plt.ylim([0,8]) plt.grid() plt.show() def main_8queens(): """main program """ QueensLayout() return sol if __name__ == "__main__": sol_8queens = main_8queens() print(sol_8queens)