【参考リンク】Nクイーン問題 過去記事一覧はこちらから
エイト・クイーンのソース置き場 BashもJavaもPythonも!
対象解除法について
まず、Nが小さな盤面で考えていきます。
以下、順に見て理解を深めてもらえればと思います。
##対象解除法について(N2版)
ひとつの解には、盤面を90度・180度・270度回転、及びそれらの鏡像の合計8個の対称解が存在します。
原型 90度 180度 270度
12 41 34 23
43 32 21 14
上の行を左右反転
21 14 43 32
34 23 12 41
上図左上がユニーク解です。
原型(ユニーク解)
12
43
1行目はユニーク解を90度、180度、270度回転したものです。
原型 90度 180度 270度
12 41 34 23
43 32 21 14
2行目は1行目のそれぞれを左右反転したものです。
上の行を左右反転
21 14 43 32
34 23 12 41
2行目はユニーク解を左右反転、対角反転、上下反転、逆対角反転したものとも解釈できます。
ただし、回転・線対称な解もあるので注意が必要です。
対称的な解を除去し、ユニーク解のみを求める方法はいくつかあります。
ここでは、一つの解には8つのパターンが存在することを覚えておいてください。
対象解を導き出す2つの方法
対象解を導き出す方法として、
1.解が見つかるたびに回転・対象をチェック
2.解のすべてを保存して、保存された解の回転・対象をチェック
最小解選択法
解がひとつみつかるとすべての対称解を生成、 状態を数値とみなして最も小さいもののみを解とする方法。
最も最適と言われています。
最小選択法よりも劣る方法
解全てを保存しておき、新しい解が発見されるたびに対称形が無いかどうかを調べる方法。
保存領域を必要とするし、比較時に対称形を生成する必要があります。
対象解除法(N5版)
全探索によって得られたある1つの解が、回転・反転などによる本質的に変わることのない変換によって他の解と同型となるものが存在する場合、それを別の解とはしないとする解の数え方で得られる解を「ユニーク解」といいます。
ユニーク解とは、全解の中から回転・反転などによる変換によって同型になるものどうしをグループ化することを意味しています。
グループ1:
+-+-+-+-+-+ +-+-+-+-+-+
| | | |Q| | | |Q| | | |
+-+-+-+-+-+ +-+-+-+-+-+
|Q| | | | | | | | | |Q|
+-+-+-+-+-+ +-+-+-+-+-+
| | |Q| | | | | |Q| | |
+-+-+-+-+-+ +-+-+-+-+-+
| | | | |Q| |Q| | | | |
+-+-+-+-+-+ +-+-+-+-+-+
| |Q| | | | | | | |Q| |
+-+-+-+-+-+ +-+-+-+-+-+
グループ2:
+-+-+-+-+-+ +-+-+-+-+-+ +-+-+-+-+-+ +-+-+-+-+-+ +-+-+-+-+-+ +-+-+-+-+-+ +-+-+-+-+-+ +-+-+-+-+-+
| | | | |Q| |Q| | | | | | | |Q| | | | | |Q| | | | | | |Q| | | |Q| | | | |Q| | | | | | | | | |Q|
+-+-+-+-+-+ +-+-+-+-+-+ +-+-+-+-+-+ +-+-+-+-+-+ +-+-+-+-+-+ +-+-+-+-+-+ +-+-+-+-+-+ +-+-+-+-+-+
| | |Q| | | | | |Q| | | |Q| | | | | | | | | |Q| | |Q| | | | | | | |Q| | | | | |Q| | | |Q| | | |
+-+-+-+-+-+ +-+-+-+-+-+ +-+-+-+-+-+ +-+-+-+-+-+ +-+-+-+-+-+ +-+-+-+-+-+ +-+-+-+-+-+ +-+-+-+-+-+
|Q| | | | | | | | | |Q| | | | |Q| | | |Q| | | | | | | | |Q| |Q| | | | | | |Q| | | | | | | |Q| |
+-+-+-+-+-+ +-+-+-+-+-+ +-+-+-+-+-+ +-+-+-+-+-+ +-+-+-+-+-+ +-+-+-+-+-+ +-+-+-+-+-+ +-+-+-+-+-+
| | | |Q| | | |Q| | | | | |Q| | | | | | | |Q| | | | |Q| | | | | |Q| | | | | | | |Q| |Q| | | | |
+-+-+-+-+-+ +-+-+-+-+-+ +-+-+-+-+-+ +-+-+-+-+-+ +-+-+-+-+-+ +-+-+-+-+-+ +-+-+-+-+-+ +-+-+-+-+-+
| |Q| | | | | | | |Q| | | | | | |Q| |Q| | | | | |Q| | | | | | | | | |Q| | | |Q| | | | | |Q| | |
+-+-+-+-+-+ +-+-+-+-+-+ +-+-+-+-+-+ +-+-+-+-+-+ +-+-+-+-+-+ +-+-+-+-+-+ +-+-+-+-+-+ +-+-+-+-+-+
ユニーク解はその「個数のみ」に着目され、この解はユニーク解であり、この解はユニーク解ではないという定まった判定方法はありません。
ユニーク解であるかどうかの判断はユニーク解の個数を数える目的の為だけにあります。
どのような定義をしたとしてもユニーク解の個数それ自体は変わりません。
Nクイーン問題は、正方形のボードで形成されるので、回転・反転による変換パターンはぜんぶで8通りあります。
だからといって「全解数=ユニーク解数×8」と単純にはいきません。
ひとつのグループの要素数が必ず8個あるとは限らないのです。
N=5の例では2つのグループがあり、要素数が2個のグループ、要素数が8個のグループがあります。
結論から言うと、N=5の全解は10個で、ユニーク解は2個です。
ユニーク解を判定するための定義
各行のクイーンが左から何番目にあるかを調べて、最上段の行(row)から下の行(row)へ順番に列挙します。
そしてそれをN桁の数値として見た場合に「最大値」となるものをユニーク解とします。
このN桁の数を以後は「ユニーク判定値」と呼ぶことにします。
0 1 2 3 4
+-+-+-+-+-+
| | | | |Q| 4
+-+-+-+-+-+
| | |Q| | | 2
+-+-+-+-+-+
|Q| | | | | 0 ----> 4 2 0 3 1 (ユニーク判定値)
+-+-+-+-+-+ 数が大きい方をユニークとみなす
| | | |Q| | 3
+-+-+-+-+-+
| |Q| | | | 1
+-+-+-+-+-+
0 1 2 3 4 左右反転!
+-+-+-+-+-+
|Q| | | | | 0
+-+-+-+-+-+
| | |Q| | | 2
+-+-+-+-+-+
| | | | |Q| 4 ----> 0 2 4 1 3
+-+-+-+-+-+ 数が小さいのでユニーク解とはしません
| |Q| | | | 1
+-+-+-+-+-+
| | | |Q| | 3
+-+-+-+-+-+
探索によって得られたある1つの解(オリジナル)がユニーク解であるかどうかを判定するには、「8通りの変換を試み、その中でオリジナルのユニーク判定値が最小であるかを調べる」ことになります。
N2の場合の8通りの変換
原型 90度 180度 270度
12 41 34 23
43 32 21 14
上の行を左右反転
21 14 43 32
34 23 12 41
結論から先にいえば、ユニーク解とは成り得ないことが明確なパターンを探索中に切り捨てる「枝刈り」を組み込むことにより、3通りの変換を試みるだけでユニーク解の判定が可能になります。
枝刈り
先ず最上段の行(row)のクイーンの位置に着目します。
その位置が左半分の領域にあればユニーク解には成り得ません。
何故なら左右反転によって得られるパターンのユニーク判定値の方が確実に小さくなるからです。
0 1 2 3 4
+-+-+-+-+-+
|Q| | | | | 0
+-+-+-+-+-+
| | |Q| | | 2
+-+-+-+-+-+
| | | | |Q| 4 ----> 0 2 4 1 3
+-+-+-+-+-+ 数が小さいのでユニークとはしません
| |Q| | | | 1
+-+-+-+-+-+
| | | |Q| | 3
+-+-+-+-+-+
0 1 2 3 4 左右反転!
+-+-+-+-+-+
| | | | |Q| 4
+-+-+-+-+-+
| | |Q| | | 2
+-+-+-+-+-+
|Q| | | | | 0 ----> 4 2 0 3 1 (ユニーク判定値)
+-+-+-+-+-+ 数が大きい方をユニークとみなす
| | | |Q| | 3
+-+-+-+-+-+
| |Q| | | | 1
+-+-+-+-+-+
Nが奇数の場合に中央にあった場合はどうでしょう。
+-+-+-+-+-+
| | |Q| | | 2
+-+-+-+-+-+
| | | | | |
+-+-+-+-+-+
| | | | | |
+-+-+-+-+-+
| | | | | |
+-+-+-+-+-+
| | | | | |
+-+-+-+-+-+
これもユニーク解には成り得ません。
何故なら仮に中央にあった場合、それがユニーク解であるためには少なくとも他の3辺におけるクイーンの位置も中央になければならず、それは互いの効き筋にあたるので解になりえないからです。
+-+-+-+-+-+
| | |Q| | |
+-+-+-+-+-+
| | | | | |
+-+-+-+-+-+
|Q| | | |Q|
+-+-+-+-+-+
| | | | | |
+-+-+-+-+-+
| | |Q| | |
+-+-+-+-+-+
最上段の行のクイーンの位置は中央を除く右側の領域に限定されます。(ただし、N ≧ 2)
+-+-+-+-+-+
| | | |Q|Q|
+-+-+-+-+-+
| | | | | |
+-+-+-+-+-+
| | | | | |
+-+-+-+-+-+
| | | | | |
+-+-+-+-+-+
| | | | | |
+-+-+-+-+-+
次にその中でも一番右端(右上の角)にクイーンがある場合を考えてみます。
+-+-+-+-+-+
| | | | |Q|
+-+-+-+-+-+
| | | | | |
+-+-+-+-+-+
| | | | | |
+-+-+-+-+-+
| | | | | |
+-+-+-+-+-+
| | | | | |
+-+-+-+-+-+
他の3つの角にクイーンを置くことはできないので、
+-+-+-+-+-+
|Q| | | |Q|
+-+-+-+-+-+
| | | | | |
+-+-+-+-+-+
| | | | | |
+-+-+-+-+-+
| | | | | |
+-+-+-+-+-+
|Q| | | |Q|
+-+-+-+-+-+
ユニーク解であるかどうかを判定するには、右上角から左下角を通る斜軸で反転させたパターンとの比較だけになります。
+-+-+-+-+-+
| | | | |Q|
+-+-+-+-+-+
| | | |/| |
+-+-+-+-+-+
| | |/| | |
+-+-+-+-+-+
| |/| | | |
+-+-+-+-+-+
|/| | | | |
+-+-+-+-+-+
突き詰めれば、上から2行目(row1)のクイーンの位置が右から何番目にあるかと、
+-+-+-+-+-+
| | | | |Q|
+-+-+-+-+-+
| |Q| |/| | ←
+-+-+-+-+-+
| | |/| | |
+-+-+-+-+-+
| |/| | | |
+-+-+-+-+-+
|/| | | | |
+-+-+-+-+-+
右から2列目のクイーンの位置が上から何番目にあるかを比較するだけで判定することができます。
+-+-+-+-+-+
| | | | |Q|
+-+-+-+-+-+
| |Q| |/| |
+-+-+-+-+-+
| | |/| | |
+-+-+-+-+-+
| |/| |Q| | ←
+-+-+-+-+-+
|/| | | | |
+-+-+-+-+-+
この2つの値が同じになることはないからです。
3 0
+-+-+-+-+-+
| | | | |Q| 0
+-+-+-+-+-+
| |Q| |/| | 3 上から2行目のクイーンの位置が右から4番目にある。
+-+-+-+-+-+
| | |/| | |
+-+-+-+-+-+
| |/| |Q| | ← 右から2列目のクイーンの位置が上から4番目にある。
+-+-+-+-+-+ しかし、互いの効き筋にあたるのでこれは有り得ない。
|/| | | | |
+-+-+-+-+-+
結局、再帰探索中において下図の X への配置を禁止する枝刈りを入れておけば、得られる解は総てユニーク解であることが保証されます。
+-+-+-+-+-+
| | | |X|Q|
+-+-+-+-+-+
| |Q| |X| |
+-+-+-+-+-+
| | | |X| |
+-+-+-+-+-+
| | | |Q| |
+-+-+-+-+-+
| | | | | |
+-+-+-+-+-+
次に右端以外にクイーンがある場合を考えてみます。
オリジナルがユニーク解であるためには先ず下図の X への配置は禁止されます。
よって、その枝刈りを先ず入れておきます。
+-+-+-+-+-+-+-+-+
|X|X| | | |Q|X|X|
+-+-+-+-+-+-+-+-+
|X| | | | | | |X|
+-+-+-+-+-+-+-+-+
| | | | | | | | |
+-+-+-+-+-+-+-+-+
| | | | | | | | |
+-+-+-+-+-+-+-+-+
| | | | | | | | |
+-+-+-+-+-+-+-+-+
| | | | | | | | |
+-+-+-+-+-+-+-+-+
|X| | | | | | |X|
+-+-+-+-+-+-+-+-+
|X|X| | | | |X|X|
+-+-+-+-+-+-+-+-+
次にクイーンの利き筋を辿っていくと、結局、オリジナルがユニーク解ではない可能性があるのは、下図の A,B,C の位置のどこかにクイーンがある場合に限られます。
+-+-+-+-+-+-+-+-+
|X|X| | | |Q|X|X|
+-+-+-+-+-+-+-+-+
|X| | | |x|x|x|X|
+-+-+-+-+-+-+-+-+
|C| | |x| |x| |x|
+-+-+-+-+-+-+-+-+
| | |x| | |x| | |
+-+-+-+-+-+-+-+-+
| |x| | | |x| | |
+-+-+-+-+-+-+-+-+
|x| | | | |x| |A|
+-+-+-+-+-+-+-+-+
|X| | | | |x| |X|
+-+-+-+-+-+-+-+-+
|X|X|B| | |x|X|X|
+-+-+-+-+-+-+-+-+
従って、90度回転、180度回転、270度回転の3通りの変換パターンだけを調べれはよいことになります。
ユニーク解を数える
これまでの考察はユニーク解の個数を求めるためのものでした。
全解数を求めるにはユニーク解を求めるための枝刈りを取り除いて全探索する必要があります。
したがって探索時間を犠牲にしてしまうことになります。
そこで「ユニーク解の個数から全解数を導いてしまおう」という試みが考えられます。
これは、左右反転(前章のミラー)によるパターンの探索を省略して最後に結果を2倍するというアイデアの拡張版といえるものです。
そしてそれを実現させるには「ユニーク解が属するグループの要素数はいくつあるのか」という考察が必要になってきます。
最初に、クイーンが右上角にあるユニーク解を考えます。
+-+-+-+-+-+
| | | | |Q|
+-+-+-+-+-+
| | | | | |
+-+-+-+-+-+
| | | | | |
+-+-+-+-+-+
| | | | | |
+-+-+-+-+-+
| | | | | |
+-+-+-+-+-+
斜軸で反転したパターンがオリジナルと同型になることは有り得ないことと(×2)、
+-+-+-+-+-+ +-+-+-+-+-+
| | | | |Q| | | | | |Q|
+-+-+-+-+-+ +-+-+-+-+-+
| | |Q|/| | | | | |/| |
+-+-+-+-+-+ +-+-+-+-+-+
| | |/| | | | | |/|Q| |
+-+-+-+-+-+ +-+-+-+-+-+
| |/| | | | | |/| | | |
+-+-+-+-+-+ +-+-+-+-+-+
|/| | | | | |/| | | | |
+-+-+-+-+-+ +-+-+-+-+-+
右上角のクイーンを他の3つの角に写像させることができるので(×4)、
+-+-+-+-+-+ +-+-+-+-+-+ +-+-+-+-+-+ +-+-+-+-+-+
| | | | |Q| | | | | | | | | | | | | |Q| | | | |
+-+-+-+-+-+ +-+-+-+-+-+ +-+-+-+-+-+ +-+-+-+-+-+
| | | | | | | | | | | | | | | | | | | | | | | |
+-+-+-+-+-+ +-+-+-+-+-+ +-+-+-+-+-+ +-+-+-+-+-+
| | | | | | | | | | | | | | | | | | | | | | | |
+-+-+-+-+-+ +-+-+-+-+-+ +-+-+-+-+-+ +-+-+-+-+-+
| | | | | | | | | | | | | | | | | | | | | | | |
+-+-+-+-+-+ +-+-+-+-+-+ +-+-+-+-+-+ +-+-+-+-+-+
| | | | | | | | | | |Q| |Q| | | | | | | | | | |
+-+-+-+-+-+ +-+-+-+-+-+ +-+-+-+-+-+ +-+-+-+-+-+
このユニーク解が属するグループの要素数は必ず8個(=2×4)になります。
次に、クイーンが右上角以外にある場合は少し複雑になります。
(1) 90度回転させてオリジナルと同型になる場合、さらに90度回転(オリジナルから180度回転)させても、さらに90度回転(オリジナルから270度回転)させてもオリジナルと同型になる。
90度回転させても同じ場合は
+-+-+-+-+-+ +-+-+-+-+-+
| | | |Q| | | | | |Q| |
+-+-+-+-+-+ +-+-+-+-+-+
|Q| | | | | |Q| | | | |
+-+-+-+-+-+ +-+-+-+-+-+
| | |Q| | | | | |Q| | |
+-+-+-+-+-+ +-+-+-+-+-+
| | | | |Q| | | | | |Q|
+-+-+-+-+-+ +-+-+-+-+-+
| |Q| | | | | |Q| | | |
+-+-+-+-+-+ +-+-+-+-+-+
さらに90度回転(原型から180度回転)させても
さらに90度回転
+-+-+-+-+-+ +-+-+-+-+-+ +-+-+-+-+-+
| | | |Q| | | | | |Q| | | | | |Q| |
+-+-+-+-+-+ +-+-+-+-+-+ +-+-+-+-+-+
|Q| | | | | |Q| | | | | |Q| | | | |
+-+-+-+-+-+ +-+-+-+-+-+ +-+-+-+-+-+
| | |Q| | | | | |Q| | | | | |Q| | |
+-+-+-+-+-+ +-+-+-+-+-+ +-+-+-+-+-+
| | | | |Q| | | | | |Q| | | | | |Q|
+-+-+-+-+-+ +-+-+-+-+-+ +-+-+-+-+-+
| |Q| | | | | |Q| | | | | |Q| | | |
+-+-+-+-+-+ +-+-+-+-+-+ +-+-+-+-+-+
さらに90度回転(原型から270度回転)させても同じ!
さらに90度回転
+-+-+-+-+-+ +-+-+-+-+-+ +-+-+-+-+-+ +-+-+-+-+-+
| | | |Q| | | | | |Q| | | | | |Q| | | | | |Q| |
+-+-+-+-+-+ +-+-+-+-+-+ +-+-+-+-+-+ +-+-+-+-+-+
|Q| | | | | |Q| | | | | |Q| | | | | |Q| | | | |
+-+-+-+-+-+ +-+-+-+-+-+ +-+-+-+-+-+ +-+-+-+-+-+
| | |Q| | | | | |Q| | | | | |Q| | | | | |Q| | |
+-+-+-+-+-+ +-+-+-+-+-+ +-+-+-+-+-+ +-+-+-+-+-+
| | | | |Q| | | | | |Q| | | | | |Q| | | | | |Q|
+-+-+-+-+-+ +-+-+-+-+-+ +-+-+-+-+-+ +-+-+-+-+-+
| |Q| | | | | |Q| | | | | |Q| | | | | |Q| | | |
+-+-+-+-+-+ +-+-+-+-+-+ +-+-+-+-+-+ +-+-+-+-+-+
(2) 90度回転させてオリジナルと異なる場合は、270度回転させても必ずオリジナルとは異なる。
ただし、180度回転させた場合はオリジナルと同型になることも有り得る。
ユニーク数から全解を求める
1.クイーンが右上角にある場合、ユニーク解が属するグループの要素数は必ず8個(=2×4)
2.クイーンが右上角以外にある場合、
(1) 90度回転させてオリジナルと同型になる場合、さらに90度回転(オリジナルから180度回転)させても、
さらに90度回転(オリジナルから270度回転)させてもオリジナルと同型になる。
こちらに該当するユニーク解が属するグループの要素数は、左右反転させたパターンを加えて2個しかありません。
(2) 90度回転させてオリジナルと異なる場合は、270度回転させても必ずオリジナルとは異なる。
ただし、180度回転させた場合はオリジナルと同型になることも有り得る。
こちらに該当するユニーク解が属するグループの要素数は、180度回転させて同型になる場合は4個(左右反転×縦横回転)
(3)180度回転させてもオリジナルと異なる場合は、8個(左右反転×縦横回転×上下反転)
1の場合は、解の数x8
2−(1)の場合は、解の数x2
2−(2)の場合は、解の数x4
2−(3)の場合は、解の数x8
それぞれのグループの解の数にグループのパターン数をかけ合わせた数の総計が全解となります。
ひとつひとつのユニーク解が上のどの種類に該当するのかを調べることにより全解数を計算で導き出すことができるのです。
対象解除法 4つの関数
対象解除法のソースは4つの関数で構成されています。
1.printRecord()
解を発見するたびにボードレイアウトを表示します。
以降、新しい枝刈りポイントを発見するためにも使います。
board[]を使ってQの位置を確認するのに役立ちます。
2.symmetryOps()
対象解除を行うロジックメソッドです。
以下のロジックが同梱されています。
1.クイーンが右上角にある場合、ユニーク解が属するグループの要素数は必ず8個(=2×4)
2.クイーンが右上角以外にある場合、
(1) 90度回転させてオリジナルと同型になる場合、さらに90度回転(オリジナルから180
度回転)させても、さらに90度回転(オリジナルから270度回転)させてもオリジナルと同
型になる。
こちらに該当するユニーク解が属するグループの要素数は、左右反転させたパター ンを
加えて2個しかありません。
(2) 90度回転させてオリジナルと異なる場合は、270度回転させても必ずオリジナルとは
異なる。ただし、180度回転させた場合はオリジナルと同型になることも有り得る。
こちらに該当するユニーク解が属するグループの要素数は、180度回転させて同型になる
場合は4個(左右反転×縦横回転)
(3)180度回転させてもオリジナルと異なる場合は、8個(左右反転×縦横回転×上下反転)
3.backTrack()
再帰ロジックで作られています。
右上角にQが配置されているかいないかで判定処理が分岐されます。
その判定処理に従って、見合った枝刈りが行われます。
4.symmetry()
本体メソッドです。
こちらのメソッドでは、Qが角に配置されている場合、そうでない場合を分岐して3のbackTrack()に渡します。
symmetry()メソッド
角にQがある時の処理
symmetry()メソッドは、Qが角にあるかどうかを判定し、適切にbackTrack()に処理を渡します。
では上から順番に見ていきます。
とても大切なことなのですが、グローバル変数の初期化を行います。
TOTAL=UNIQUE=COUNT2=COUNT4=COUNT8=0;
角にQがある時のボードのイメージは以下のとおりです。
角にQがある時の処理
+-+-+-+-+-+
| | | | |Q|
+-+-+-+-+-+
| | | | | |
+-+-+-+-+-+
| | | | | |
+-+-+-+-+-+
| | | | | |
+-+-+-+-+-+
| | | | | |
+-+-+-+-+-+
MASKについてですが、こちらはrow全体にビットを立てて配置済みとするフィルタのことです。
N5の場合、 10進数では31、2進数では 11111 となります。
MASK=(1<<size)-1;
+-+-+-+-+-+
|M|M|M|M|M| MASK=(1<<size)-1
+-+-+-+-+-+ $(( (1<<5)-1 )):31
| | | | | | $(( 2#11111 ))
+-+-+-+-+-+ $ 31
| | | | | |
+-+-+-+-+-+
| | | | | |
+-+-+-+-+-+
| | | | | |
+-+-+-+-+-+
bashでは以下のようにすると2進数を10進数に変換して表示することができます。
$ echo $(( 2#11111 ))
$ 31
TOPBITは左上角にビットを立てたものです。
TOPBIT=1<<(size-1);
+-+-+-+-+-+
|T| | | | | TOPBIT=1<<(size-1);
+-+-+-+-+-+ $(( (1<<(5-1) )):16
| | | | | | $(( 2#10000 ))
+-+-+-+-+-+ $ 16
| | | | | |
+-+-+-+-+-+
| | | | | |
+-+-+-+-+-+
| | | | | |
+-+-+-+-+-+
ENDBITはTOPBITの1つ右にビットを立てたものです。現在はまだ初期値0のままです。
後述しますがENDBITは、
ENDBIT=TOPBIT>>1;
となります。
ENDBIT=LASTMASK=SIDEMASK=0;
0は何も置かない(なにもはじまってない)
+-+-+-+-+-+
|T|E| | | | 0#01000
+-+-+-+-+-+
| | | | | |
+-+-+-+-+-+
| | | | | |
+-+-+-+-+-+
| | | | | |
+-+-+-+-+-+
| | | | | |
+-+-+-+-+-+
配列BOUND1とBOUND2を初期化して
Qを右上角に配置します。
BOUND1=2;
BOUND2=0;
+-+-+-+-+-+ 右角から1つ左から開始
| | | |B| | BOUND1は右から左へシフト
+-+-+-+-+-+
| | | | | |
+-+-+-+-+-+
| | | | | |
+-+-+-+-+-+
| | | | | |
+-+-+-+-+-+
| | | | | |
+-+-+-+-+-+
+-+-+-+-+-+ 左角から1つ右から開始
| |B| | | | BOUND2は左から右へシフトします。
+-+-+-+-+-+
| | | | | |
+-+-+-+-+-+
| | | | | |
+-+-+-+-+-+
| | | | | |
+-+-+-+-+-+
| | | | | |
+-+-+-+-+-+
ここはすごく重要で、row[0]の1というのは右上の角のことを意味します。ですので、「Qを右上角に配置した」ということになります。
board[0]=1; # Qを右上角に配置
ここで、rowは2行目の処理に入ります。
というのも、1行目は右上角にQを配置したので次に進んだわけです。
その後、以下の行は「2行目の真ん中にQを配置する」という意味となります。
bit=1<<BOUND1;
イメージでは以下のとおりです。
N5の場合、BOUND1は2と3になる
1<<BOUND1 2行目は真ん中に置く
+-+-+-+-+-+
| | | | |Q| 1#00001
+-+-+-+-+-+
| | |B| | | BOUND1=2 4#00100
+-+-+-+-+-+ $(( 1<<2 ))
| | | | | | $ 4
+-+-+-+-+-+ 1->2->4
| | | | | |
+-+-+-+-+-+
| | | | | |
+-+-+-+-+-+
さらに、処理の中でBOUND1は左に1つシフトします。
1<<BOUND1 2行目は真ん中の次に置く
+-+-+-+-+-+
| | | | |Q| 1#00001
+-+-+-+-+-+
| |B| | | | BOUND1=3 8#01000
+-+-+-+-+-+ $(( 1<<3 ))
| | | | | | $ 8
+-+-+-+-+-+ 1->2->4->8
| | | | | |
+-+-+-+-+-+
| | | | | |
+-+-+-+-+-+
角にQがない時の処理
TOPBITは先程も書いたとおり、左上角にビットを立てた状態を示します。
TOPBIT=1<<(size-1)
+-+-+-+-+-+
|T| | | | | TOPBIT=1<<(size-1)
+-+-+-+-+-+ $(( 2#10000 ))
| | | | | | $ 16
+-+-+-+-+-+
| | | | | |
+-+-+-+-+-+
| | | | | |
+-+-+-+-+-+
| | | | | |
+-+-+-+-+-+
ENDBITは、TOPBITの1つ右にビットを立てた状態です。
ENDBIT=TOPBIT>>1
+-+-+-+-+-+
|T|E| | | | ENDBIT=TOPBIT>>1
+-+-+-+-+-+ $(( 2#01000 ))
| | | | | | $ 8
+-+-+-+-+-+
| | | | | |
+-+-+-+-+-+
| | | | | |
+-+-+-+-+-+
| | | | | |
+-+-+-+-+-+
SIDEMASKは行の両サイドの角にビットを立てた状態を示します。
SIDEMASK=TOPBIT|1
+-+-+-+-+-+
|S| | | |S| SIDEMASK=TOPBIT|1
+-+-+-+-+-+ $(( 2#10001 ))
| | | | | | $ 17
+-+-+-+-+-+
| | | | | |
+-+-+-+-+-+
| | | | | |
+-+-+-+-+-+
| | | | | |
+-+-+-+-+-+
LASTMASKもSIDEMASK同様左右の角のビットを立てた状態を示します。ただ使いみちとしては、上記の状態でも使えるわけですが、
LASTMASK=TOPBIT|1
+-+-+-+-+-+
|L| | | |L| LASTMASK=TOPBIT|1
+-+-+-+-+-+ $(( 2#10001 ))
| | | | | | $ 17
+-+-+-+-+-+
| | | | | |
+-+-+-+-+-+
| | | | | |
+-+-+-+-+-+
| | | | | |
+-+-+-+-+-+
こういうシチュエーションで使います。
LASTMASK=TOPBIT|1
+-+-+-+-+-+
| | | | | | LASTMASK=TOPBIT|1
+-+-+-+-+-+ $(( 2#10001 ))
| | | | | | $ 17
+-+-+-+-+-+
| | | | | |
+-+-+-+-+-+
| | | | | |
+-+-+-+-+-+
|L| | | |L|
+-+-+-+-+-+
ENDBITは左角の1つ手前から右へシフトするビットです。
ENDBIT=ENDBIT>>1
+-+-+-+-+-+
|T|E| | | | ENDBIT=TOPBIT>>1
+-+-+-+-+-+ $(( 2#01000 ))
| | | | | | $ 8
+-+-+-+-+-+
| | | | | |
+-+-+-+-+-+
| | | | | |
+-+-+-+-+-+
| | | | | |
+-+-+-+-+-+
↓
+-+-+-+-+-+
|T| |E| | | ENDBIT=ENDBIT>>1
+-+-+-+-+-+ $(( 2#00100 ))
| | | | | | $ 4
+-+-+-+-+-+
| | | | | |
+-+-+-+-+-+
| | | | | |
+-+-+-+-+-+
| | | | | |
+-+-+-+-+-+
LASTMASKに代入されるビットは以下のイメージとなります。使いみちはLASTMASKと似ていますが、
+-+-+-+-+-+
|L|L| |L|L| LASTMASK=LASTMASK<<1 | LASTMASK | LASTMASK>>1
+-+-+-+-+-+ $(( 2#11011 ))
| | | | | | $ 27
+-+-+-+-+-+
| | | | | |
+-+-+-+-+-+
| | | | | |
+-+-+-+-+-+
| | | | | |
+-+-+-+-+-+
こういったシチュエーションにも使います。
+-+-+-+-+-+
| | | | | | LASTMASK=LASTMASK<<1 | LASTMASK | LASTMASK>>1
+-+-+-+-+-+ $(( 2#11011 ))
| | | | | | $ 27
+-+-+-+-+-+
| | | | | |
+-+-+-+-+-+
| | | | | |
+-+-+-+-+-+
|L|L| |L|L|
+-+-+-+-+-+
backTrack()メソッド
角にQがある時の処理
このメソッドでは、角にQがある場合の処理、ない場合の処理の分岐があり、その分岐の中で、((COUNT8++))
であったり、symmetryOps
といった対象解除の処理へ進んだり、はたまた「枝刈り」をしたりと、濃厚なメソッドです。
では、順番に見ていきます。
ここは、Qが角にある場合の処理で、rowが最終行までたどり着いた(配置できた)場合の処理となります。グループの要素数は必ず8あるわけですので、あとから8倍するCOUNT8
をインクリメントします。
1.クイーンが右上角にある場合、ユニーク解が属する
グループの要素数は必ず8個(=2×4)
if(row==(size-1)){
if(bitmap){
board[row]=bitmap;
COUNT8++;
}
}else{
次は、Qが角にある場合の処理で、
上から2行目のクイーンの位置が左から何番目にあるかと、
右から2列目のクイーンの位置が上から何番目にあるかを、
比較するだけで判定します。
具体的には、2行目と2列目の位置を数値とみなし、
2行目<2列目という条件を課せばよい
という枝仮ルールを実現している処理となります。
}else{
if(row<BOUND1){ //枝刈り
bitmap=bitmap|2;
bitmap=bitmap^2;
}
while(bitmap){
: '
上から2行目のクイーンの位置が左から何番目にあるかと、
右から2列目のクイーンの位置が上から何番目にあるかを、
比較するだけで判定します。
具体的には、2行目と2列目の位置を数値とみなし、
2行目<2列目という条件を課せばよい
結論: 以下の図では、1,2,4を枝刈りを入れる
+-+-+-+-+-+
| | | |X|Q|
+-+-+-+-+-+
| |Q| |X| | 8(左から数えて1,2,4,8)
+-+-+-+-+-+
| | | |X| |
+-+-+-+-+-+
| | | |Q| | 8(上から数えて1,2,4,8)
+-+-+-+-+-+
| | | | | |
+-+-+-+-+-+
';
fi
たったこれだけの2行ですごいですね。
bitmap=bitmap|2;
bitmap=bitmap^2;
角にQがない時の処理
ここは少しわかりにくいですね。丁寧に説明文を入れましたので、じっくりと理解してみてください。
}else{ # Qが角にない
: '
オリジナルがユニーク解であるためには先ず、
前提:symmetryOpsは回転・鏡像変換により得られる状態の
ユニーク値を比較し最小のものだけがユニーク解となるようにしている。
Qができるだけ右に置かれている方がユニーク値は小さい。
例えば1行目の2列目にQが置かれている方が3列目に置かれているより
ユニーク値は小さくユニーク解に近い。
1行目のクイーンの位置が同じなら2行目のクイーンの位置がより右の
列におかれているものがユニーク値は小さくユニーク解に近い。
下図の X への配置は禁止されます。
Qの位置より右位置の8対象位置(X)にクイーンを置くことはできない。
置いた場合回転・鏡像変換したユニーク値が最小にならなくなり、symmetryOps
で負けるので枝刈りをする
1行目のクイーンが3列目に置かれている場合
+-+-+-+-+-+-+-+-+
|X|X| | | |Q|X|X|
+-+-+-+-+-+-+-+-+
|X| | | | | | |X|
+-+-+-+-+-+-+-+-+
| | | | | | | | |
+-+-+-+-+-+-+-+-+
| | | | | | | | |
+-+-+-+-+-+-+-+-+
| | | | | | | | |
+-+-+-+-+-+-+-+-+
| | | | | | | | |
+-+-+-+-+-+-+-+-+
|X| | | | | | |X|
+-+-+-+-+-+-+-+-+
|X|X| | | | |X|X|
+-+-+-+-+-+-+-+-+
1行目のクイーンが4列目に置かれている場合
+-+-+-+-+-+-+-+-+
|X|X|X| |Q|X|X|X|
+-+-+-+-+-+-+-+-+
|X| | | | | | |X|
+-+-+-+-+-+-+-+-+
|X| | | | | | |X|
+-+-+-+-+-+-+-+-+
| | | | | | | | |
+-+-+-+-+-+-+-+-+
| | | | | | | | |
+-+-+-+-+-+-+-+-+
|X| | | | | | |X|
+-+-+-+-+-+-+-+-+
|X| | | | | | |X|
+-+-+-+-+-+-+-+-+
|X|X|X| | |X|X|X|
+-+-+-+-+-+-+-+-+
プログラムではこの枝刈を上部サイド枝刈り、下部サイド枝刈り、最下段枝刈り
の3か所で行っている
それぞれ、1,2,3の数字で表すと以下の通り
1行目のクイーンが3列目に置かれている場合
+-+-+-+-+-+-+-+-+
|X|X| | | |Q|X|X|
+-+-+-+-+-+-+-+-+
|1| | | | | | |1|
+-+-+-+-+-+-+-+-+
| | | | | | | | |
+-+-+-+-+-+-+-+-+
| | | | | | | | |
+-+-+-+-+-+-+-+-+
| | | | | | | | |
+-+-+-+-+-+-+-+-+
| | | | | | | | |
+-+-+-+-+-+-+-+-+
|2| | | | | | |2|
+-+-+-+-+-+-+-+-+
|2|3| | | | |3|2|
+-+-+-+-+-+-+-+-+
1行目にXが残っているが当然Qの効き筋なので枝刈する必要はない
';
if(row<BOUND1){
bitmap=bitmap|SIDEMASK;
bitmap=bitmap^SIDEMASK;
}else{
if(row==BOUND2){
if((down&SIDEMASK)==0){
return;
}
if( (down&SIDEMASK)!=SIDEMASK){
bitmap=bitmap&SIDEMASK;
}
}
}
symmetryOps()メソッド
ここもロジックを理解するところから初めて見ると良いと思います。
あまり細かいことを考えるよりも、大枠でどんなときにこのメソッドが呼ばれ、カウンターがいつインクリメントするのか、などを掴んだほうが良いと思います。
symmetryOps()では以下を実現しています。
2.クイーンが右上角以外にある場合、
(1) 90度回転させてオリジナルと同型になる場合、さらに90度回転(オリジナルか
ら180度回転)させても、さらに90度回転(オリジナルから270度回転)させてもオリ
ジナルと同型になる。
こちらに該当するユニーク解が属するグループの要素数は、左右反転させたパター
ンを加えて2個しかありません。
2.クイーンが右上角以外にある場合、
(2) 90度回転させてオリジナルと異なる場合は、270度回転させても必ずオリジナル
とは異なる。ただし、180度回転させた場合はオリジナルと同型になることも有り得
る。こちらに該当するユニーク解が属するグループの要素数は、180度回転させて同
型になる場合は4個(左右反転×縦横回転)
2.クイーンが右上角以外にある場合、
(3)180度回転させてもオリジナルと異なる場合は、8個(左右反転×縦横回転×上下反転)
クイーンの利き筋を辿っていくと、オリジナルがユニーク解ではない可能性があり、
それは下図の A,B,C の位置のどこかにクイーンがある場合に限られます。
symmetryOpsは、以下の図のA,B,CにQが置かれた場合にユニーク解かを判定します。
原型と、90,180,270回転させたもののユニーク値を比較します。
0 1 2 3 4
+-+-+-+-+-+
| | | | |Q| 4
+-+-+-+-+-+
| | |Q| | | 2
+-+-+-+-+-+
|Q| | | | | 0 ----> 4 2 0 3 1 (ユニーク判定値)
+-+-+-+-+-+ 数が大きい方をユニークとみなす
| | | |Q| | 3
+-+-+-+-+-+
| |Q| | | | 1
+-+-+-+-+-+
0 1 2 3 4 左右反転!
+-+-+-+-+-+
|Q| | | | | 0
+-+-+-+-+-+
| | |Q| | | 2
+-+-+-+-+-+
| | | | |Q| 4 ----> 0 2 4 1 3
+-+-+-+-+-+ 数が小さいのでユニーク解とはしません
| |Q| | | | 1
+-+-+-+-+-+
| | | |Q| | 3
+-+-+-+-+-+
Qができるだけ右に置かれている方がユニーク値は大きくなります。
例えば1行目の2列目にQが置かれている方が、
3列目に置かれているよりユニーク値は大きくユニーク解に近い。
1行目のクイーンの位置が同じなら2行目のクイーンの位置がより右の列におかれてい
るものがユニーク値は大きくユニーク解に近くなります。
それ以外はユニーク解なのでCOUNT8にする
+-+-+-+-+-+-+-+-+
|X|X| | | |Q|X|X|
+-+-+-+-+-+-+-+-+
|X| | | |x|x|x|X|
+-+-+-+-+-+-+-+-+
|C| | |x| |x| |x|
+-+-+-+-+-+-+-+-+
| | |x| | |x| | |
+-+-+-+-+-+-+-+-+
| |x| | | |x| | |
+-+-+-+-+-+-+-+-+
|x| | | | |x| |A|
+-+-+-+-+-+-+-+-+
|X| | | | |x| |X|
+-+-+-+-+-+-+-+-+
|X|X|B| | |x|X|X|
+-+-+-+-+-+-+-+-+
Aの場合 右90度回転 board[BOUND2]==1
Bの場合 右180度回転 board[size-1]==ENDBIT
Cの場合 右270度回転 board[BOUND1]==TOPBIT
ソースコード
C言語で実装した配置フラグのプログラムソースは以下のとおりです。解もきちんとでます。
/**
*
* bash版対称解除法のC言語版
*
詳しい説明はこちらをどうぞ
https://suzukiiichiro.github.io/search/?keyword=Nクイーン問題
*
bash-3.2$ gcc 06GCC_Symmetry.c && ./a.out -r
6.対称解除法
N: Total Unique hh:mm:ss.ms
4: 2 1 0.00
5: 10 2 0.00
6: 4 1 0.00
7: 40 6 0.00
8: 92 12 0.00
9: 352 46 0.00
10: 724 92 0.00
11: 2680 341 0.00
12: 14200 1787 0.00
13: 73712 9233 0.02
14: 365596 45752 0.09
15: 2279184 285053 0.54
16: 14772512 1846955 3.48
17: 95815104 11977939 24.08
bash-3.2$ gcc 06GCC_Symmetry.c && ./a.out -c
6.対称解除法
N: Total Unique hh:mm:ss.ms
4: 2 1 0.00
5: 10 2 0.00
6: 4 1 0.00
7: 40 6 0.00
8: 92 12 0.00
9: 352 46 0.00
10: 724 92 0.00
11: 2680 341 0.00
12: 14200 1787 0.00
13: 73712 9233 0.02
14: 365596 45752 0.12
15: 2279184 285053 0.71
16: 14772512 1846955 4.66
17: 95815104 11977939 32.40
bash-3.2$
*/
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <sys/time.h>
#define MAX 27
// システムによって以下のマクロが必要であればコメントを外してください。
//#define UINT64_C(c) c ## ULL
//
// グローバル変数
typedef unsigned long long uint64_t;
uint64_t TOTAL=0;
uint64_t UNIQUE=0;
// 構造体
typedef struct{
unsigned int size;
unsigned int pres_a[930];
unsigned int pres_b[930];
// uint64_t COUNTER[3];
// //カウンター配列
// unsigned int COUNT2;
// unsigned int COUNT4;
// unsigned int COUNT8;
}Global; Global g;
// 構造体
typedef struct{
uint64_t row;
uint64_t down;
uint64_t left;
uint64_t right;
uint64_t x[MAX];
}Board ;
typedef struct{
Board B;
Board nB;
Board eB;
Board sB;
Board wB;
unsigned n;
unsigned e;
unsigned s;
unsigned w;
uint64_t dimx;
uint64_t dimy;
uint64_t COUNTER[3];
//カウンター配列
unsigned int COUNT2;
unsigned int COUNT4;
unsigned int COUNT8;
}Local;
//hh:mm:ss.ms形式に処理時間を出力
void TimeFormat(clock_t utime,char* form)
{
int dd,hh,mm;
float ftime,ss;
ftime=(float)utime/CLOCKS_PER_SEC;
mm=(int)ftime/60;
ss=ftime-(int)(mm*60);
dd=mm/(24*60);
mm=mm%(24*60);
hh=mm/60;
mm=mm%60;
if(dd)
sprintf(form,"%4d %02d:%02d:%05.2f",dd,hh,mm,ss);
else if(hh)
sprintf(form," %2d:%02d:%05.2f",hh,mm,ss);
else if(mm)
sprintf(form," %2d:%05.2f",mm,ss);
else
sprintf(form," %5.2f",ss);
}
// ボード外側2列を除く内側のクイーン配置処理
uint64_t solve(uint64_t row,uint64_t left,uint64_t down,uint64_t right)
{
if(down+1==0){ return 1; }
while((row&1)!=0) {
row>>=1;
left<<=1;
right>>=1;
}
row>>=1;
uint64_t total=0;
for(uint64_t bitmap=~(left|down|right);bitmap!=0;){
uint64_t const bit=bitmap&-bitmap;
total+=solve(row,(left|bit)<<1,down|bit,(right|bit)>>1);
bitmap^=bit;
}
return total;
}
// クイーンの効きをチェック
bool placement(void* args)
{
Local *l=(Local *)args;
if(l->B.x[l->dimx]==l->dimy){ return true; }
if (l->B.x[0]==0){
if (l->B.x[1]!=(uint64_t)-1){
if((l->B.x[1]>=l->dimx)&&(l->dimy==1)){ return false; }
}
}else{
if( (l->B.x[0]!=(uint64_t)-1) ){
if(( (l->dimx<l->B.x[0]||l->dimx>=g.size-l->B.x[0])
&& (l->dimy==0 || l->dimy==g.size-1)
)){ return 0; }
if (( (l->dimx==g.size-1)&&((l->dimy<=l->B.x[0])||
l->dimy>=g.size-l->B.x[0]))){
return 0;
}
}
}
l->B.x[l->dimx]=l->dimy; //xは行 yは列
uint64_t row=UINT64_C(1)<<l->dimx;
uint64_t down=UINT64_C(1)<<l->dimy;
uint64_t left=UINT64_C(1)<<(g.size-1-l->dimx+l->dimy); //右上から左下
uint64_t right=UINT64_C(1)<<(l->dimx+l->dimy); // 左上から右下
if((l->B.row&row)||(l->B.down&down)||(l->B.left&left)||(l->B.right&right)){ return false; }
l->B.row|=row; l->B.down|=down; l->B.left|=left; l->B.right|=right;
return true;
}
//解除法
void carryChain_symmetry(void* args)
{
Local *l=(Local *)args;
// 解除法
unsigned const int ww=(g.size-2)*(g.size-1)-1-l->w;
unsigned const int w2=(g.size-2)*(g.size-1)-1;
// # 対角線上の反転が小さいかどうか確認する
if((l->s==ww)&&(l->n<(w2-l->e))){ return ; }
// # 垂直方向の中心に対する反転が小さいかを確認
if((l->e==ww)&&(l->n>(w2-l->n))){ return; }
// # 斜め下方向への反転が小さいかをチェックする
if((l->n==ww)&&(l->e>(w2-l->s))){ return; }
// 枝刈り 1行目が角の場合回転チェックせずCOUNT8にする
if(l->B.x[0]==0){
l->COUNTER[l->COUNT8]+=solve(l->B.row>>2,
l->B.left>>4,((((l->B.down>>2)|(~0<<(g.size-4)))+1)<<(g.size-5))-1,(l->B.right>>4)<<(g.size-5));
return ;
}
// n,e,s==w の場合は最小値を確認する。右回転で同じ場合は、
// w=n=e=sでなければ値が小さいのでskip w=n=e=sであれば90度回転で同じ可能性
if(l->s==l->w){ if((l->n!=l->w)||(l->e!=l->w)){ return; }
l->COUNTER[l->COUNT2]+=solve(l->B.row>>2,
l->B.left>>4,((((l->B.down>>2)|(~0<<(g.size-4)))+1)<<(g.size-5))-1,(l->B.right>>4)<<(g.size-5));
return;
}
// e==wは180度回転して同じ 180度回転して同じ時n>=sの時はsmaller?
if((l->e==l->w)&&(l->n>=l->s)){ if(l->n>l->s){ return; }
l->COUNTER[l->COUNT4]+=solve(l->B.row>>2,
l->B.left>>4,((((l->B.down>>2)|(~0<<(g.size-4)))+1)<<(g.size-5))-1,(l->B.right>>4)<<(g.size-5));
return;
}
l->COUNTER[l->COUNT8]+=solve(l->B.row>>2,
l->B.left>>4,((((l->B.down>>2)|(~0<<(g.size-4)))+1)<<(g.size-5))-1,(l->B.right>>4)<<(g.size-5));
return;
}
// pthread run()
void thread_run(void* args)
{
Local *l=(Local *)args;
// memcpy(&l->B,&l->wB,sizeof(Board)); // B=wB;
l->B=l->wB;
l->dimx=0; l->dimy=g.pres_a[l->w];
//if(!placement(l)){ continue; }
if(!placement(l)){ return; }
l->dimx=1; l->dimy=g.pres_b[l->w];
// if(!placement(l)){ continue; }
if(!placement(l)){ return; }
//2 左2行に置く
// memcpy(&l->nB,&l->B,sizeof(Board)); // nB=B;
l->nB=l->B;
for(l->n=l->w;l->n<(g.size-2)*(g.size-1)-l->w;++l->n){
// memcpy(&l->B,&l->nB,sizeof(Board)); // B=nB;
l->B=l->nB;
l->dimx=g.pres_a[l->n]; l->dimy=g.size-1;
if(!placement(l)){ continue; }
l->dimx=g.pres_b[l->n]; l->dimy=g.size-2;
if(!placement(l)){ continue; }
// 3 下2行に置く
// memcpy(&l->eB,&l->B,sizeof(Board)); // eB=B;
l->eB=l->B;
for(l->e=l->w;l->e<(g.size-2)*(g.size-1)-l->w;++l->e){
// memcpy(&l->B,&l->eB,sizeof(Board)); // B=eB;
l->B=l->eB;
l->dimx=g.size-1; l->dimy=g.size-1-g.pres_a[l->e];
if(!placement(l)){ continue; }
l->dimx=g.size-2; l->dimy=g.size-1-g.pres_b[l->e];
if(!placement(l)){ continue; }
// 4 右2列に置く
// memcpy(&l->sB,&l->B,sizeof(Board)); // sB=B;
l->sB=l->B;
for(l->s=l->w;l->s<(g.size-2)*(g.size-1)-l->w;++l->s){
// memcpy(&l->B,&l->sB,sizeof(Board)); // B=sB;
l->B=l->sB;
l->dimx=g.size-1-g.pres_a[l->s]; l->dimy=0;
if(!placement(l)){ continue; }
l->dimx=g.size-1-g.pres_b[l->s]; l->dimy=1;
if(!placement(l)){ continue; }
// 解除法
carryChain_symmetry(l);
} //w
} //e
} //n
}
// チェーンのビルド
void buildChain()
{
Local l[(g.size/2)*(g.size-3)];
// カウンターの初期化
l->COUNT2=0; l->COUNT4=1; l->COUNT8=2;
l->COUNTER[l->COUNT2]=l->COUNTER[l->COUNT4]=l->COUNTER[l->COUNT8]=0;
// Board の初期化 nB,eB,sB,wB;
l->B.row=l->B.down=l->B.left=l->B.right=0;
// Board x[]の初期化
for(unsigned int i=0;i<g.size;++i){ l->B.x[i]=-1; }
//1 上2行に置く
// memcpy(&l->wB,&l->B,sizeof(Board)); // wB=B;
l->wB=l->B;
for(l->w=0;l->w<=(unsigned)(g.size/2)*(g.size-3);++l->w){
thread_run(&l);
} //w
/**
* 集計
*/
UNIQUE= l->COUNTER[l->COUNT2]+
l->COUNTER[l->COUNT4]+
l->COUNTER[l->COUNT8];
TOTAL= l->COUNTER[l->COUNT2]*2+
l->COUNTER[l->COUNT4]*4+
l->COUNTER[l->COUNT8]*8;
}
// チェーンのリストを作成
void listChain()
{
unsigned int idx=0;
for(unsigned int a=0;a<(unsigned)g.size;++a){
for(unsigned int b=0;b<(unsigned)g.size;++b){
if(((a>=b)&&(a-b)<=1)||((b>a)&&(b-a)<=1)){ continue; }
g.pres_a[idx]=a;
g.pres_b[idx]=b;
++idx;
}
}
}
// キャリーチェーン
void carryChain()
{
listChain(); //チェーンのリストを作成
buildChain(); // チェーンのビルド
// calcChain(&l); // 集計
}
unsigned int board[MAX]; //ボード配列
unsigned int down[MAX]; //ポストフラグ/ビットマップ/ミラー
unsigned int left[MAX]; //ポストフラグ/ビットマップ/ミラー
unsigned int right[MAX]; //ポストフラグ/ビットマップ/ミラー
unsigned int bitmap[MAX]; //ミラー
unsigned long COUNT2=0; //ミラー/対称解除法
unsigned long COUNT4=0; //対称解除法
unsigned long COUNT8=0; //対称解除法
unsigned int BOUND1=0; //対称解除法
unsigned int BOUND2=0; //対称解除法
unsigned int SIDEMASK=0; //対称解除法
unsigned int LASTMASK=0; //対称解除法
unsigned int TOPBIT=0; //対称解除法
unsigned int ENDBIT=0; //対称解除法
// 対称解除法
void symmetryOps(unsigned int size)
{
/**
2.クイーンが右上角以外にある場合、
(1) 90度回転させてオリジナルと同型になる場合、さらに90度回転(オリジナルか
ら180度回転)させても、さらに90度回転(オリジナルから270度回転)させてもオリ
ジナルと同型になる。
こちらに該当するユニーク解が属するグループの要素数は、左右反転させたパター
ンを加えて2個しかありません。
*/
if(board[BOUND2]==1){
unsigned int ptn;
unsigned int own;
for(ptn=2,own=1;own<size;++own,ptn<<=1){
unsigned int bit;
unsigned int you;
for(bit=1,you=size-1;(board[you]!=ptn)&&board[own]>=bit;--you){
bit<<=1;
}
if(board[own]>bit){
return ;
}
if(board[own]<bit){
break;
}
}//end for
// 90度回転して同型なら180度回転しても270度回転しても同型である
if(own>size-1){
COUNT2++;
return ;
}//end if
}//end if
/**
2.クイーンが右上角以外にある場合、
(2) 90度回転させてオリジナルと異なる場合は、270度回転させても必ずオリジナル
とは異なる。ただし、180度回転させた場合はオリジナルと同型になることも有り得
る。こちらに該当するユニーク解が属するグループの要素数は、180度回転させて同
型になる場合は4個(左右反転×縦横回転)
*/
//180度回転
if(board[size-1]==ENDBIT){
unsigned int you;
unsigned int own;
for(you=size-1-1,own=1;own<=size-1;++own,--you){
unsigned int bit;
unsigned int ptn;
for(bit=1,ptn=TOPBIT;(ptn!=board[you])&&(board[own]>=bit);ptn>>=1){
bit<<=1;
}
if(board[own]>bit){
return ;
}
if(board[own]<bit){
break;
}
}//end for
//90度回転が同型でなくても180度回転が同型であることもある
if(own>size-1){
COUNT4++;
return ;
}
}//end if
/**
2.クイーンが右上角以外にある場合、
(3)180度回転させてもオリジナルと異なる場合は、8個(左右反転×縦横回転×上下反転)
*/
//270度回転
if(board[BOUND1]==TOPBIT){
unsigned int ptn;
unsigned int own;
unsigned int you;
unsigned int bit;
for(ptn=TOPBIT>>1,own=1;own<=size-1;++own,ptn>>=1){
for(bit=1,you=0;(board[you]!=ptn)&&(board[own]>=bit);++you){
bit<<=1;
}
if(board[own]>bit){
return ;
}
if(board[own]<bit){
break;
}
}//end for
}//end if
COUNT8++;
}
//
void symmetry_backTrack_NR(unsigned int size,unsigned int row,unsigned int _left,unsigned int _down,unsigned int _right)
{
unsigned int mask=(1<<size)-1;
left[row]=_left;
down[row]=_down;
right[row]=_right;
bitmap[row]=mask&~(left[row]|down[row]|right[row]);
while(row>0){
if(bitmap[row]>0){
if(row<BOUND1){ //上部サイド枝刈り
bitmap[row]|=SIDEMASK;
bitmap[row]^=SIDEMASK;
}else if(row==BOUND2){ //下部サイド枝刈り
if((down[row]&SIDEMASK)==0){
row--;
}
if((down[row]&SIDEMASK)!=SIDEMASK){
bitmap[row]&=SIDEMASK;
}
}
unsigned int save_bitmap=bitmap[row];
unsigned int bit=-bitmap[row]&bitmap[row];
bitmap[row]^=bit;
board[row]=bit; //Qを配置
if((bit&mask)!=0){
if(row==(size-1)){
if( (save_bitmap&LASTMASK)==0){
symmetryOps(size); //対称解除法
}
row--;
}else{
unsigned int n=row++;
left[row]=(left[n]|bit)<<1;
down[row]=(down[n]|bit);
right[row]=(right[n]|bit)>>1;
bitmap[row]=mask&~(left[row]|down[row]|right[row]);
}
}else{
row--;
}
}else{
row--;
}
}//end while
}
void symmetry_backTrack_corner_NR(unsigned int size,unsigned int row,unsigned int _left,unsigned int _down, unsigned int _right)
{
unsigned int mask=(1<<size)-1;
unsigned int bit=0;
left[row]=_left;
down[row]=_down;
right[row]=_right;
bitmap[row]=mask&~(left[row]|down[row]|right[row]);
while(row>=2){
if(row<BOUND1){
// bitmap[row]=bitmap[row]|2;
// bitmap[row]=bitmap[row]^2;
bitmap[row]&=~2;
}
if(bitmap[row]>0){
bit=-bitmap[row]&bitmap[row];
bitmap[row]^=bit;
if(row==(size-1)){
COUNT8++;
row--;
}else{
unsigned int n=row++;
left[row]=(left[n]|bit)<<1;
down[row]=(down[n]|bit);
right[row]=(right[n]|bit)>>1;
board[row]=bit; //Qを配置
//クイーンが配置可能な位置を表す
bitmap[row]=mask&~(left[row]|down[row]|right[row]);
}
}else{
row--;
}
}//end while
}
// 対称解除法 非再帰版
void symmetry_NR(unsigned int size)
{
TOTAL=UNIQUE=COUNT2=COUNT4=COUNT8=0;
unsigned int mask=(1<<size)-1;
unsigned int bit=0;
TOPBIT=1<<(size-1);
ENDBIT=SIDEMASK=LASTMASK=0;
BOUND1=2;
BOUND2=0;
board[0]=1;
while(BOUND1>1&&BOUND1<size-1){
if(BOUND1<size-1){
bit=1<<BOUND1;
board[1]=bit; //2行目にQを配置
//角にQがあるときのバックトラック
symmetry_backTrack_corner_NR(size,2,(2|bit)<<1,1|bit,(2|bit)>>1);
}
BOUND1++;
}
TOPBIT=1<<(size-1);
ENDBIT=TOPBIT>>1;
SIDEMASK=TOPBIT|1;
LASTMASK=TOPBIT|1;
BOUND1=1;
BOUND2=size-2;
while(BOUND1>0 && BOUND2<size-1 && BOUND1<BOUND2){
if(BOUND1<BOUND2){
bit=1<<BOUND1;
board[0]=bit; //Qを配置
//角にQがないときのバックトラック
symmetry_backTrack_NR(size,1,bit<<1,bit,bit>>1);
}
BOUND1++;
BOUND2--;
ENDBIT=ENDBIT>>1;
LASTMASK=LASTMASK<<1|LASTMASK|LASTMASK>>1;
}//ene while
UNIQUE=COUNT2+COUNT4+COUNT8;
TOTAL=COUNT2*2+COUNT4*4+COUNT8*8;
}
// 再帰 角にQがないときのバックトラック
void symmetry_backTrack(unsigned int size,unsigned int row,unsigned int left,unsigned int down,unsigned int right)
{
unsigned int mask=(1<<size)-1;
unsigned int bitmap=mask&~(left|down|right);
if(row==(size-1)){
if(bitmap){
if( (bitmap&LASTMASK)==0){
board[row]=bitmap; //Qを配置
symmetryOps(size); //対称解除
}
}
}else{
if(row<BOUND1){
bitmap=bitmap|SIDEMASK;
bitmap=bitmap^SIDEMASK;
}else{
if(row==BOUND2){
if((down&SIDEMASK)==0){
return;
}
if( (down&SIDEMASK)!=SIDEMASK){
bitmap=bitmap&SIDEMASK;
}
}
}
while(bitmap){
unsigned int bit=-bitmap&bitmap;
bitmap=bitmap^bit;
board[row]=bit;
symmetry_backTrack(size,row+1,(left|bit)<<1,down|bit,(right|bit)>>1);
}//end while
}//end if
}
// 再帰 角にQがあるときのバックトラック
void symmetry_backTrack_corner(unsigned int size,unsigned int row,unsigned int left,unsigned int down,unsigned int right)
{
unsigned int mask=(1<<size)-1;
unsigned int bitmap=mask&~(left|down|right);
unsigned int bit=0;
if(row==(size-1)){
if(bitmap){
board[row]=bitmap;
COUNT8++;
}
}else{
if(row<BOUND1){ //枝刈り
bitmap=bitmap|2;
bitmap=bitmap^2;
}
while(bitmap){
bit=-bitmap&bitmap;
bitmap=bitmap^bit;
board[row]=bit; //Qを配置
symmetry_backTrack_corner(size,row+1,(left|bit)<<1,down|bit,(right|bit)>>1);
}
}
}
// 対称解除法 再帰版
void symmetry_R(unsigned int size)
{
TOTAL=UNIQUE=COUNT2=COUNT4=COUNT8=0;
unsigned int mask=(1<<size)-1;
unsigned int bit=0;
TOPBIT=1<<(size-1);
ENDBIT=LASTMASK=SIDEMASK=0;
BOUND1=2;
BOUND2=0;
board[0]=1;
while(BOUND1>1 && BOUND1<size-1){
if(BOUND1<size-1){
bit=1<<BOUND1;
board[1]=bit; //2行目にQを配置
//角にQがあるときのバックトラック
symmetry_backTrack_corner(size,2,(2|bit)<<1,1|bit,(2|bit)>>1);
}
BOUND1++;
}//end while
TOPBIT=1<<(size-1);
ENDBIT=TOPBIT>>1;
SIDEMASK=TOPBIT|1;
LASTMASK=TOPBIT|1;
BOUND1=1;
BOUND2=size-2;
while(BOUND1>0 && BOUND2<size-1 && BOUND1<BOUND2){
if(BOUND1<BOUND2){
bit=1<<BOUND1;
board[0]=bit; //Qを配置
//角にQがないときのバックトラック
symmetry_backTrack(size,1,bit<<1,bit,bit>>1);
}
BOUND1++;
BOUND2--;
ENDBIT=ENDBIT>>1;
LASTMASK=LASTMASK<<1|LASTMASK|LASTMASK>>1;
}//ene while
UNIQUE=COUNT2+COUNT4+COUNT8;
TOTAL=COUNT2*2+COUNT4*4+COUNT8*8;
}
//ミラー処理部分 非再帰版
void mirror_solve_NR(unsigned int size,unsigned int row,unsigned int _left,unsigned int _down, unsigned int _right)
{
unsigned int mask=(1<<size)-1;
unsigned int bit=0;
left[row]=_left;
down[row]=_down;
right[row]=_right;
bitmap[row]=mask&~(left[row]|down[row]|right[row]);
while(row>0){
if(bitmap[row]>0){
bit=-bitmap[row]&bitmap[row];
bitmap[row]=bitmap[row]^bit;
board[row]=bit;
if(row==(size-1)){
COUNT2++;
row--;
}else{
unsigned int n=row++;
left[row]=(left[n]|bit)<<1;
down[row]=(down[n]|bit);
right[row]=(right[n]|bit)>>1;
board[row]=bit; //Qを配置
//クイーンが配置可能な位置を表す
bitmap[row]=mask&~(left[row]|down[row]|right[row]);
}
}else{
row--;
}
}
}
// ミラー 非再帰版
void mirror_NR(unsigned int size)
{
COUNT2=0;
unsigned int mask=(1<<size)-1;
unsigned int bit=0;
unsigned int limit=size%2 ? size/2-1 : size/2;
for(unsigned int i=0;i<size/2;++i){ //奇数でも偶数でも通過
bit=1<<i;
board[0]=bit; //1行目にQを置く
mirror_solve_NR(size,1,bit<<1,bit,bit>>1);
}
if(size%2){ //奇数で通過
bit=1<<(size-1)/2;
board[0]=1<<(size-1)/2; //1行目の中央にQを配置
unsigned int left=bit<<1;
unsigned int down=bit;
unsigned int right=bit>>1;
for(unsigned int i=0;i<limit;++i){
bit=1<<i;
mirror_solve_NR(size,2,(left|bit)<<1,down|bit,(right|bit)>>1);
}
}
TOTAL=COUNT2<<1; //倍にする
}
//ミラーロジック 再帰版
void mirror_solve_R(unsigned int size,unsigned int row,unsigned int left,unsigned int down,unsigned int right)
{
unsigned int mask=(1<<size)-1;
unsigned int bit=0;
if(row==size){
COUNT2++;
}else{
for(unsigned int bitmap=mask&~(left|down|right);bitmap;bitmap=bitmap&~bit){
bit=-bitmap&bitmap;
board[row]=bit;
mirror_solve_R(size,row+1,(left|bit)<<1,down|bit,(right|bit)>>1);
}
}
}
// ミラー 再帰版
void mirror_R(unsigned int size)
{
COUNT2=0;
unsigned int mask=(1<<size)-1;
unsigned int bit=0;
unsigned int limit=size%2 ? size/2-1 : size/2;
for(unsigned int i=0;i<size/2;++i){
bit=1<<i;
board[0]=bit; //1行目にQを置く
mirror_solve_R(size,1,bit<<1,bit,bit>>1);
}
if(size%2){ //奇数で通過
bit=1<<(size-1)/2;
board[0]=1<<(size-1)/2; //1行目の中央にQを配置
unsigned int left=bit<<1;
unsigned int down=bit;
unsigned int right=bit>>1;
for(unsigned int i=0;i<limit;++i){
bit=1<<i;
mirror_solve_R(size,2,(left|bit)<<1,down|bit,(right|bit)>>1);
}
}
TOTAL=COUNT2<<1; //倍にする
}
// ビットマップ 非再帰版
void bitmap_NR(unsigned int size,int row)
{
unsigned int mask=(1<<size)-1;
unsigned int bitmap[size];
unsigned int bit=0;
bitmap[row]=mask;
while(row>-1){
if(bitmap[row]>0){
bit=-bitmap[row]&bitmap[row];//一番右のビットを取り出す
bitmap[row]=bitmap[row]^bit;//配置可能なパターンが一つずつ取り出される
board[row]=bit;
if(row==(size-1)){
TOTAL++;
row--;
}else{
unsigned int n=row++;
left[row]=(left[n]|bit)<<1;
down[row]=down[n]|bit;
right[row]=(right[n]|bit)>>1;
board[row]=bit;
//クイーンが配置可能な位置を表す
bitmap[row]=mask&~(left[row]|down[row]|right[row]);
}
}else{
row--;
}
}//end while
}
// ビットマップ 再帰版
void bitmap_R(unsigned int size,unsigned int row,unsigned int left,unsigned int down, unsigned int right)
{
unsigned int mask=(1<<size)-1;
unsigned int bit=0;
if(row==size){
TOTAL++;
}else{
for(unsigned int bitmap=mask&~(left|down|right);bitmap;bitmap=bitmap&~bit){
bit=-bitmap&bitmap;
board[row]=bit;
bitmap_R(size,row+1,(left|bit)<<1,down|bit,(right|bit)>>1);
}
}
}
// ポストフラグ 非再帰版
void postFlag_NR(unsigned int size,int row)
{
// 1.非再帰は初期化が必要
for(unsigned int i=0;i<size;++i){
board[i]=-1;
}
// 2.再帰で呼び出される関数内を回す処理
while(row>-1){
unsigned int matched=0; //クイーンを配置したか
// 3.再帰処理のループ部分
// 非再帰では過去の譜石を記憶するためにboard配列を使う
for(unsigned int col=board[row]+1;col<size;++col){
if(!down[col]
&& !right[col-row+size-1]
&& !left[col+row]){
// unsigned int dix=col;
// unsigned int rix=row-col+(size-1);
// unsigned int lix=row+col;
/** バックトラックではここで効きをチェックしていた
check_backTracking "$row"; # 効きをチェック
*/
// 効きとしてフラグをfalseにする
if(board[row]!=-1){
down[board[row]]=0;
right[board[row]-row+(size-1)]=0;
left[board[row]+row]=0;
}
board[row]=col; //クイーンを配置
// 効きを開放(trueに)する
down[col]=1;
right[col-row+(size-1)]=1;
left[col+row]=1;
matched=1;
break;
} //end if
}//end for
// 4.配置したら実行したい処理
if(matched){
row++;
// 5.最下部まで到達したときの処理
if(row==size){
row--;
/** ブルートフォースではここで効きをチェックしていた
// check_bluteForce "$size"; # 効きをチェック
*/
TOTAL++;
}
// 6.配置できなくてバックトラックしたい時の処理
}else{
if(board[row]!=-1){
down[board[row]]=0;
right[board[row]-row+(size-1)]=0;
left[board[row]+row]=0;
board[row]=-1;
}
row--;
}
}//end while
}
// ポストフラグ 再帰版
void postFlag_R(unsigned int size,unsigned int row)
{
if(row==size){
TOTAL++;
}else{
for(unsigned int col=0;col<size;++col){
board[row]=col;
if(down[col]==0
&& right[row-col+size-1]==0
&& left[row+col]==0){
down[col]=1;
right[row-col+(size-1)]=1;
left[row+col]=1;
postFlag_R(size,row+1);
down[col]=0;
right[row-col+(size-1)]=0;
left[row+col]=0;
}//end if
}//end for
}//end if
}
// バックトラック 効き筋をチェック
int check_backTracking(unsigned int row)
{
for(unsigned int i=0;i<row;++i){
unsigned int val=0;
if(board[i]>=board[row]){
val=board[i]-board[row];
}else{
val=board[row]-board[i];
}
if(board[i]==board[row]||val==(row-i)){
return 0;
}
}
return 1;
}
// バックトラック 非再帰版
void backTracking_NR(unsigned int size,int row)
{
// 1.非再帰は初期化が必要
for(unsigned int i=0;i<size;++i){
board[i]=-1;
}
// 2.再帰で呼び出される関数内を回す処理
while(row>-1){
unsigned int matched=0; //クイーンを配置したか
// 3.再帰処理のループ部分
for(unsigned int col=board[row]+1;col<size;++col){
board[row]=col; // クイーンを配置
// 効きをチェック
if(check_backTracking(row)==1){
matched=1;
break;
} // end if
} // end for
// 4.配置したら実行したい処理
if(matched){
row++;
// 5.最下部まで到達したときの処理
if(row==size){
row--;
TOTAL++;
}
// 6.配置できなくてバックトラックしたい時の処理
}else{
if(board[row]!=-1){
board[row]=-1;
}
row--;
}
} //end while
}
// バックトラック 再帰版
void backTracking_R(unsigned int size,unsigned int row)
{
if(row==size){
TOTAL++;
}else{
for(unsigned int col=0;col<size;++col){
board[row]=col;
if(check_backTracking(row)==1){
backTracking_R(size,row+1);
}
}// end for
}//end if
}
// ブルートフォース 効き筋をチェック
int check_bluteForce()
{
unsigned int size=g.size;
for(unsigned int r=1;r<size;++r){
unsigned int val=0;
for(unsigned int i=0;i<r;++i){
if(board[i]>=board[r]){
val=board[i]-board[r];
}else{
val=board[r]-board[i];
}
if(board[i]==board[r]||val==(r-i)){
return 0;
}
}
}
return 1;
}
//ブルートフォース 非再帰版
void bluteForce_NR(unsigned int size,int row)
{
// 1.非再帰は初期化が必要
for(unsigned int i=0;i<size;++i){
board[i]=-1;
}
// 2.再帰で呼び出される関数内を回す処理
while(row>-1){
unsigned int matched=0; //クイーンを配置したか
// 3.再帰処理のループ部分
// 非再帰では過去の譜石を記憶するためにboard配列を使う
for(unsigned int col=board[row]+1;col<size;++col){
board[row]=col;
matched=1;
break;
}
// 4.配置したら実行したい処理
if(matched){
row++;
// 5.最下部まで到達したときの処理';
if(row==size){
row--;
// 効きをチェック
if(check_bluteForce()==1){
TOTAL++;
}
}
// 6.配置できなくてバックトラックしたい処理
}else{
if(board[row]!=-1){
board[row]=-1;
}
row--;
} // end if
}//end while
}
//ブルートフォース 再帰版
void bluteForce_R(unsigned int size,int row)
{
if(row==size){
if(check_bluteForce()==1){
TOTAL++; // グローバル変数
}
}else{
for(int col=0;col<size;++col){
board[row]=col;
bluteForce_R(size,row+1);
}
}
}
//メインメソッド
int main(int argc,char** argv)
{
bool cpu=false,cpur=false;
int argstart=2;
if(argc>=2&&argv[1][0]=='-'){
if(argv[1][1]=='c'||argv[1][1]=='C'){cpu=true;}
else if(argv[1][1]=='r'||argv[1][1]=='R'){cpur=true;}
else{ cpur=true;}
}
if(argc<argstart){
printf("Usage: %s [-c|-r|-g]\n",argv[0]);
printf(" -c: CPU Without recursion\n");
printf(" -r: CPUR Recursion\n");
printf(" -g: GPU Without Recursion\n");
}
printf("6.対称解除法\n");
printf("%s\n"," N: Total Unique hh:mm:ss.ms");
clock_t st; //速度計測用
char t[20]; //hh:mm:ss.msを格納
unsigned int min=4;
unsigned int targetN=21;
// sizeはグローバル
for(unsigned int size=min;size<=targetN;++size){
TOTAL=UNIQUE=0;
st=clock();
g.size=size;
if(cpu){ // 非再帰
symmetry_NR(size); //6.対称解除法
// mirror_NR(size); //5.ミラー
// bitmap_NR(size,0); //4.ビットマップ
// postFlag_NR(size,0); //3.ポストフラグ
// backTracking_NR(size,0);//2.バックトラック
// bluteForce_NR(size,0); //1.ブルートフォース
// carryChain();
}else{ // 再帰
symmetry_R(size); //6.対称解除法
// mirror_R(size); //5.ミラー
// bitmap_R(size,0,0,0,0); //4.ビットマップ
// postFlag_R(size,0); //3.ポストフラグ
// backTracking_R(size,0); //2.バックトラック
// bluteForce_R(size,0); //1.ブルートフォース
// carryChain();
}
TimeFormat(clock()-st,t);
printf("%2d:%13lld%16lld%s\n",size,TOTAL,UNIQUE,t);
}
return 0;
}
実行結果
実行結果は以下のとおりです。
bash-3.2$ gcc 06GCC_Symmetry.c && ./a.out -r
6.対称解除法
N: Total Unique hh:mm:ss.ms
4: 2 1 0.00
5: 10 2 0.00
6: 4 1 0.00
7: 40 6 0.00
8: 92 12 0.00
9: 352 46 0.00
10: 724 92 0.00
11: 2680 341 0.00
12: 14200 1787 0.00
13: 73712 9233 0.02
14: 365596 45752 0.09
15: 2279184 285053 0.54
16: 14772512 1846955 3.48
17: 95815104 11977939 24.08
bash-3.2$ gcc 06GCC_Symmetry.c && ./a.out -c
6.対称解除法
N: Total Unique hh:mm:ss.ms
4: 2 1 0.00
5: 10 2 0.00
6: 4 1 0.00
7: 40 6 0.00
8: 92 12 0.00
9: 352 46 0.00
10: 724 92 0.00
11: 2680 341 0.00
12: 14200 1787 0.00
13: 73712 9233 0.02
14: 365596 45752 0.12
15: 2279184 285053 0.71
16: 14772512 1846955 4.66
17: 95815104 11977939 32.40
bash-3.2$
参考リンク
以下の詳細説明を参考にしてください。
【参考リンク】Nクイーン問題 過去記事一覧
【Github】エイト・クイーンのソース置き場 BashもJavaもPythonも!
Nクイーン問題(50)第七章 マルチプロセス Python編
https://suzukiiichiro.github.io/posts/2023-06-21-04-n-queens-suzuki/
Nクイーン問題(49)第七章 マルチスレッド Python編
https://suzukiiichiro.github.io/posts/2023-06-21-03-n-queens-suzuki/
Nクイーン問題(48)第七章 シングルスレッド Python編
https://suzukiiichiro.github.io/posts/2023-06-21-02-n-queens-suzuki/
Nクイーン問題(47)第七章 クラス Python編
https://suzukiiichiro.github.io/posts/2023-06-21-01-n-queens-suzuki/
Nクイーン問題(46)第七章 ステップNの実装 Python編
https://suzukiiichiro.github.io/posts/2023-06-16-02-n-queens-suzuki/
Nクイーン問題(45)第七章 キャリーチェーン Python編
https://suzukiiichiro.github.io/posts/2023-06-16-01-n-queens-suzuki/
Nクイーン問題(44)第七章 対称解除法 Python編
https://suzukiiichiro.github.io/posts/2023-06-14-02-n-queens-suzuki/
Nクイーン問題(43)第七章 ミラー Python編
https://suzukiiichiro.github.io/posts/2023-06-14-01-n-queens-suzuki/
Nクイーン問題(42)第七章 ビットマップ Python編
https://suzukiiichiro.github.io/posts/2023-06-13-05-n-queens-suzuki/
Nクイーン問題(41)第七章 配置フラグ Python編
https://suzukiiichiro.github.io/posts/2023-06-13-04-n-queens-suzuki/
Nクイーン問題(40)第七章 バックトラック Python編
https://suzukiiichiro.github.io/posts/2023-06-13-03-n-queens-suzuki/
Nクイーン問題(39)第七章 バックトラック準備編 Python編
https://suzukiiichiro.github.io/posts/2023-06-13-02-n-queens-suzuki/
Nクイーン問題(38)第七章 ブルートフォース Python編
https://suzukiiichiro.github.io/posts/2023-06-13-01-n-queens-suzuki/
Nクイーン問題(37)第六章 C言語移植 その17 pthread並列処理完成
https://suzukiiichiro.github.io/posts/2023-05-30-17-n-queens-suzuki/
Nクイーン問題(36)第六章 C言語移植 その16 pthreadの実装
https://suzukiiichiro.github.io/posts/2023-05-30-16-n-queens-suzuki/
Nクイーン問題(35)第六章 C言語移植 その15 pthread実装直前版完成
https://suzukiiichiro.github.io/posts/2023-05-30-15-n-queens-suzuki/
Nクイーン問題(34)第六章 C言語移植 その14
https://suzukiiichiro.github.io/posts/2023-05-30-14-n-queens-suzuki/
Nクイーン問題(33)第六章 C言語移植 その13
https://suzukiiichiro.github.io/posts/2023-05-30-13-n-queens-suzuki/
Nクイーン問題(32)第六章 C言語移植 その12
https://suzukiiichiro.github.io/posts/2023-05-30-12-n-queens-suzuki/
Nクイーン問題(31)第六章 C言語移植 その11
https://suzukiiichiro.github.io/posts/2023-05-30-11-n-queens-suzuki/
Nクイーン問題(30)第六章 C言語移植 その10
https://suzukiiichiro.github.io/posts/2023-05-30-10-n-queens-suzuki/
Nクイーン問題(29)第六章 C言語移植 その9
https://suzukiiichiro.github.io/posts/2023-05-30-09-n-queens-suzuki/
Nクイーン問題(28)第六章 C言語移植 その8
https://suzukiiichiro.github.io/posts/2023-05-30-08-n-queens-suzuki/
Nクイーン問題(27)第六章 C言語移植 その7
https://suzukiiichiro.github.io/posts/2023-05-30-07-n-queens-suzuki/
Nクイーン問題(26)第六章 C言語移植 その6
https://suzukiiichiro.github.io/posts/2023-05-30-06-n-queens-suzuki/
Nクイーン問題(25)第六章 C言語移植 その5
https://suzukiiichiro.github.io/posts/2023-05-30-05-n-queens-suzuki/
Nクイーン問題(24)第六章 C言語移植 その4
https://suzukiiichiro.github.io/posts/2023-05-30-04-n-queens-suzuki/
Nクイーン問題(23)第六章 C言語移植 その3
https://suzukiiichiro.github.io/posts/2023-05-30-03-n-queens-suzuki/
Nクイーン問題(22)第六章 C言語移植 その2
https://suzukiiichiro.github.io/posts/2023-05-30-02-n-queens-suzuki/
Nクイーン問題(21)第六章 C言語移植 その1
N-Queens問://suzukiiichiro.github.io/posts/2023-05-30-01-n-queens-suzuki/
Nクイーン問題(20)第五章 並列処理
https://suzukiiichiro.github.io/posts/2023-05-23-02-n-queens-suzuki/
Nクイーン問題(19)第五章 キャリーチェーン
https://suzukiiichiro.github.io/posts/2023-05-23-01-n-queens-suzuki/
Nクイーン問題(18)第四章 エイト・クイーンノスタルジー
https://suzukiiichiro.github.io/posts/2023-04-25-01-n-queens-suzuki/
Nクイーン問題(17)第四章 偉人のソースを読む「N24を発見 Jeff Somers」
https://suzukiiichiro.github.io/posts/2023-04-21-01-n-queens-suzuki/
Nクイーン問題(16)第三章 対称解除法 ソース解説
https://suzukiiichiro.github.io/posts/2023-04-18-01-n-queens-suzuki/
Nクイーン問題(15)第三章 対称解除法 ロジック解説
https://suzukiiichiro.github.io/posts/2023-04-13-02-nqueens-suzuki/
Nクイーン問題(14)第三章 ミラー
https://suzukiiichiro.github.io/posts/2023-04-13-01-nqueens-suzuki/
Nクイーン問題(13)第三章 ビットマップ
https://suzukiiichiro.github.io/posts/2023-04-05-01-nqueens-suzuki/
Nクイーン問題(12)第二章 まとめ
https://suzukiiichiro.github.io/posts/2023-03-17-02-n-queens-suzuki/
Nクイーン問題(11)第二章 配置フラグの再帰・非再帰
https://suzukiiichiro.github.io/posts/2023-03-17-01-n-queens-suzuki/
Nクイーン問題(10)第二章 バックトラックの再帰・非再帰
https://suzukiiichiro.github.io/posts/2023-03-16-01-n-queens-suzuki/
Nクイーン問題(9)第二章 ブルートフォースの再帰・非再帰
https://suzukiiichiro.github.io/posts/2023-03-14-01-n-queens-suzuki/
Nクイーン問題(8)第一章 まとめ
https://suzukiiichiro.github.io/posts/2023-03-09-01-n-queens-suzuki/
Nクイーン問題(7)第一章 ブルートフォース再び
https://suzukiiichiro.github.io/posts/2023-03-08-01-n-queens-suzuki/
Nクイーン問題(6)第一章 配置フラグ
https://suzukiiichiro.github.io/posts/2023-03-07-01-n-queens-suzuki/
Nクイーン問題(5)第一章 進捗表示テーブルの作成
https://suzukiiichiro.github.io/posts/2023-03-06-01-n-queens-suzuki/
Nクイーン問題(4)第一章 バックトラック
https://suzukiiichiro.github.io/posts/2023-02-21-01-n-queens-suzuki/
Nクイーン問題(3)第一章 バックトラック準備編
https://suzukiiichiro.github.io/posts/2023-02-14-03-n-queens-suzuki/
Nクイーン問題(2)第一章 ブルートフォース
https://suzukiiichiro.github.io/posts/2023-02-14-02-n-queens-suzuki/
Nクイーン問題(1)第一章 エイトクイーンについて
https://suzukiiichiro.github.io/posts/2023-02-14-01-n-queens-suzuki/