2019年3月6日水曜日

Python N Queenのコードを自分なりに解析してみました。




E:\MyBackups\goolgedrive\myprg_main\python_my_prg\wxpython\8queen_py3\queen.py


# coding: UTF-8

#以下のサイトのN Queenのコードを自分なりに解析してみました。

#参考サイト
#N Queen (Python 3.0 版 )
#http://www.shido.info/py/queen_py3.html

#Python3ではwxPythonはインストールできない

"""N Queen: taking symmetry into account"""

import sys


def comb(f1, f2):
"""2 つの 関数 を   合成 した 関数 を   返す """
return lambda x:f1(f2(x))


# 対称 操作
# REVはlsが上書きされてしまうのでは
REV  = lambda ls: list(reversed(ls)) # 左右反転
#前のqueen.pyでは以下のコードだったので式的には合っている
#return [7 - o for o in ls0]
USD  = lambda ls: [ len(ls)-x-1 for x in ls ]    # 上下 反転

#前のコードのまま
#nreverse([ls0.index(i) for i in range(8)])
T90  = lambda ls: [ ls.index(x) for x in range(len(ls)) ]    # 90 度 回転

#前のコードでは以下だったのに、上下反転+左右反転が180度回転と等しい
#何か数学的に深い意味があるのだろうが
#queen_90(queen_90(ls0))
T180 = comb(USD, REV)    # 180 度 回転
T270 = comb(T90, T180)   # 270 度 回転

D1   = comb(REV, T90)    # 対角線 での 反転 その 1
D2   = comb(USD, T90)    # 対角線 での 反転 その 2


#is_different(cols, sols) で、解 (cols) と その対称操作によって生じた解
#が今まで見つかった解の集合 (sols) に含まれているか調べます。 cols に全
#ての対称操作を施しても、 sols の中に同じものが見つからなければTrueを 
#返します。
def is_different(cols, sols):
""" 対称性 を  考慮し ても 固有 の 解か """
#タプルの内包表記ではなく、ジェネレーターを作成している
#opの中に対称性移動する関数をいれて
#solsは集合 
#op(cols)はいずれもリスト形式
return all( tuple(op(cols)) not in sols for op in (REV, USD, T90, T180, T270, D1, D2) )


#qpos:今までに置いたqueenのリスト row1:これから置く行
#col=[4, 2]なら7列2行、6列4行にqueenがおいてある
def printBord(qpos, row1):
#print()
bord = [[0 for i in range(8)] for j in range(8)] 

#colの順序を逆にする
ls = [qpos[i] for i in range(len(qpos)-1, -1, -1)]
c = 8
for row in ls:
c -= 1
bord[c][row] = 1

bord[c-1][row1] = "*" 

for row in range(8):
for col in range(8):
#print(bord[col][row], end='')
#print(" ", end='')
pass
#print()
pass
#print()

#新しく置く Queen と既においてある Queen の縦の升目の差の絶対値が、横の
#升目の差と等しいとき、 斜めの利きがぶつかるので、 queen_ok() では、既
#に置いてある、すべての Queen について利きが 重ならないことを確かめてい
#ます。
def queen_ok(ls, row1):
"""row1 が ls にある queen と 利き が 重な らないとき True を   返す """

#abs(row1-row)!=col+1 for col, row in enumerate(ls) とすると、 ls 
#中にある (盤中にある) すべてのこまについて、 新しく置くこまと利き
#が 重ならないかを返す generatorが 生成します( 重ならないとき
# True)。 Python 3.0では 、内包表現で generatorを 生成するこ
#とができます。

#all() 関数を使って、新しく置くこまと、既に置いてあるこま全ての利
#きが重ならないときTrue を返すようにします。
#all:全ての要素がTrueの時Trueとなる 
#all(リスト)などと使うが、下のジェネレーターの場合allジェネレータ
#ーとなり()がぬけている。()をいれてジェネレーターなのにそのまま
#allをつけているだけ。でもこれで動作する

#enumerate col(列):インデックス、row(行):要素を取得 

#abs(row1-row)==col+1 ならばqueenの利き筋が重なっている
#下の図の如く col+1が列の差を表している
#queen(cols, i)を調べてみる
#iは*に置くことになる。5が3の左に追加することになり(Trueであれば) 
#列は2列 行は5
#  (cols= (3, 1, 4, 2, 0) i= 5

#  col 0 row 3
#  True
#  
#  col 1 row 1
#  True
#  
#  col 2 row 4
#  True
#  
#  col 3 row 2
#  True
#  
# col=4 つまり7列0行と利き筋が重なっている
#  col 4 row 0
#  False
#  
#  0 0 0 0 0 0 0 1
#  0 0 0 0 1 0 0 0
#  0 0 0 0 0 0 1 0
#  0 0 0 1 0 0 0 0
#  0 0 0 0 0 1 0 0
#  0 0 * 0 0 0 0 0
#  0 0 0 0 0 0 0 0
#  0 0 0 0 0 0 0 0


#ここで、 row1 は新しく置く駒の縦の升目、 row は既にあるこまの縦 の
#升目、 col は既にあるこまの横 の升目です。
#input------------------------
#input()
##print()
##print(  [(col, row) for col, row in enumerate(ls)]  )
#col=0なら今置くrow1の右隣りがcol=0 さらに右がcol=1となる
#qpos:今までに置いたqueenのリスト row1:これから置く行

#col=[4, 2]なら7列2行、6列4行にqueenがおいてある
for col, row in enumerate(ls):
if abs(row1-row)!=col+1:
#print("col", col,"row", row)
#print("True")
#printBord(ls, row1)
pass
else:
#print("col", col,"row", row)
#print("False")
#printBord(ls, row1)
#print()
pass
#print()

#print("queen_ok return ", end='')
#print( all( abs(row1-row)!=col+1  for col, row in enumerate(ls) ))
return all( abs(row1-row)!=col+1  for col, row in enumerate(ls) )
#タプルの内包表記ではなく、ジェネレーターを作成している
#ジェネレーターにすると途中でFalseになればall()でFalseにするようだ
#all([abs(row1-row)!=col+1  for col, row in enumerate(ls) ])
#とすると最後まで forをつづける?て無駄となる



#手持ちの駒 rows にある全ての駒について、駒が置けるのであれば
#(queen_ok() が True を返す ) 、 駒を置いて、再帰的に qiter() を呼び出
#します 。 
#全ての駒を配置し終わる (手持ちの 駒が 無くなる) と対称操作の チェック
#を 行い、 対称操作を考慮しても別の解だったら、 見つかった解を sols (解
#の集合) に加えます。

#n:n×n のマス目の大きさを指定する
k = 0
def nqueen(n):
"""n x n マス の N-Queen パズル を   解く """
#col 列 row 行
#qiter(cols, rows) が盤上にQueenを 置いていく関数です。 
#cols は既においてあるこまを表すタプル。 rows はまだ、盤上に無い縦
#の(行)升目の 集合です。 
#colsはケツから置いていくから、7列目から6、5...と決まっていく
#前のNqueenと逆で墓穴を掘った
#colsはタプル rowsは0~7のいずれかの数字
def qiter(cols, rows):
global k
#例えば以下の時
#cols= (0,) rows= frozenset({1, 2, 3, 4, 5, 6, 7})
#for i in rows:
#i= 1
#queen_ok( (0,), 1 )を計算する

#if not rows:
# print()
# print(cols)
# print(USD(cols))
# print()


#rowsが空集合でないなら
if rows:
#input------------------------
#input()
for i in rows:
n = len(rows)
k = 8 - n + 1
#print("   " * k ,end='') 
#print("for i in rows:")
#print("   " * k ,end='') 
#print("i=", i,"rows=", rows)
#print("   " * k ,end='') 
#print("cols=", cols, "i=",i)

if queen_ok(cols, i):
#print("   " * k ,end='') 
#print("queen_ok(cols, i)はTrue") 
#縦の升目 i に駒を置くと、 (i,) + cols でそれをタ
#プルの先頭に追加し、 rows - {i} で、 まだおかれて
#いない駒 の集合 から取り除きます。 
#rows-{i}:集合同士の引き算
#print("   " * k ,end='') 
#print("qiter( (i,)+cols, rows-{i})")
#print()
#要素が1個のタプルを生成する場合は、末尾にカンマ,が必要
#タプルは要素は変更できないが、+でタプル同士を連結
#して新しいタプルを作成できる
#rowsもfrozensetで要素を変更できない集合だが-で新
#しい集合を作成しているのか?
#n -= 1
#print("   " * k ,end='') 
#print("qiter")
#print("   " * k ,end='') 
#print("cols=", (i,)+cols,"rows=", rows-{i})
qiter( (i,)+cols, rows-{i})
else:
#もしiで利き筋だったら次のiに進む
#かつiがrowsの終わりまできたら
#print("   " * k ,end='') 
#print("queen_ok(cols, i)はFalse") 
#print("   " * k ,end='') 
#print()
pass


#is_different(cols, sols) で、解 (cols) と その対称操作によって生じた解
#が今まで見つかった解の集合 (sols) に含まれているか調べます。 cols に全
#ての対称操作を施しても、 sols の中に同じものが見つからなければTrueを 
#返します。
elif is_different(cols, sols):
#print("elif is_different(cols, sols):")
#print
#print
#対称パターンがなければsolsにいれる
sols.add(cols)

#空集合作成
sols=set()
#frozenset(range(n)): ({0, 1, 2, 3, 4, 5, 6, 7})
#tuple()で空のタプルを返すようだ
#8queenの解が(2, 6, 7, 4, 5, 3, 1, 0)とすると2行0列、6行1列、...と
#queenを置く事になる
#前回のqposとは逆になる
#8queenは利き筋に同じqueenを置いてはだめなので0~7の数字を取り出し
#て解のタプルの中に数字を入れていっていけばいい。
#ただし途中で、利き筋になっていないか又合同なパターンは取り除く事
#が必要

qiter(tuple(), frozenset(range(n)))
return sols

if __name__=='__main__':
#sols=nqueen(int(sys.argv[1]))
sols=nqueen(8)
print ('N of solutions:', len(sols))
for sol in sols:
print (sol)

#  abs(row1-row)!=col+1 実行結果
#  N of solutions: 12
#  (4, 6, 0, 2, 7, 5, 3, 1)
#  (5, 1, 6, 0, 3, 7, 4, 2)
#  (3, 0, 4, 7, 5, 2, 6, 1)
#  (5, 3, 6, 0, 7, 1, 4, 2)
#  (2, 5, 3, 0, 7, 4, 6, 1)
#  (3, 6, 0, 7, 4, 1, 5, 2)
#  (4, 2, 7, 3, 6, 0, 5, 1)
#  (4, 6, 3, 0, 2, 7, 5, 1)
#  (3, 5, 7, 2, 0, 6, 4, 1)
#  (4, 1, 3, 6, 2, 7, 5, 0)
#  (2, 5, 7, 0, 3, 6, 4, 1)
#  (3, 1, 6, 2, 5, 7, 4, 0)
#  

0 件のコメント:

コメントを投稿

About

参加ユーザー

連絡フォーム

名前

メール *

メッセージ *

ページ

Featured Posts