5

私はさまざまな数のパズルを解くためのプログラムを書いてきましたが、最適化できない不当に複雑な検索アルゴリズムを常に設計しています。

たとえば、あるパズルでは、以下の1から9までの数字の3x3グリッドが与えられます。

123
456
789

任意の行または列の数値を任意の方向に循環させることができます。以下は、数字の一番上の行をにシフトする例です。グリッドの端にある場合、番号はループします。

123 -> 312
456    456
789    789

各列、行、対角線の数字の合計が15になる魔方陣を作成するまで、この方法で数字を移動する必要があります。

可能なすべての移動シーケンスをテストするためにDFSブルートフォースアルゴリズムを作成しましたが、各ターンで使用可能な移動の数は指数関数的に増加し(約12 ^ [現在のターン])、役に立たなくなります。

正しい動きを見つけるにはBFSが最適であるように思われますが、バックトラックするには、グリッドのコピーを数千とは言わないまでも数百を保存する必要があります。


私はいつもこの種の問題に遭遇します。BFSアルゴリズムとDFSアルゴリズムはどちらも、それぞれ大量のメモリと時間を使用します。これらのようなアルゴリズムを最適化して、より高速かつ効率的に実行できるようにするための支援が必要です。おそらく、数字のパターンと関係を認識したり、アルゴリズムロジックを与えて目標に向かって取り組むことが役立つでしょうか?(それが何を伴うのかわかりません)。

編集:

私の固定アルゴリズムは魅力のように機能します。私の順列に番号を付ける方法を学ぶことは不可欠でした。皆さん、ありがとうございました!

4

5 に答える 5

9

メモ化を調べることをお勧めします(入力に基づいて関数呼び出しの結果をキャッシュし、同じ後続の呼び出しに対して関数が再計算されないようにします)。メモ化を理解したので、動的計画法を調べます (関数の結果は引き続き保存されますが、計算を並べ替えて不要な呼び出しを排除します)。動的計画法のいくつかの説明では、フィボナッチの再帰的定義を使用し、次にフィボナッチ + メモ化を使用し、計算の並べ替えで終了します。

DFS および BFS の問題全般については、Branch and Bound として知られる手法が役立つ場合があります。境界部分は、いくつかの問題でかなりの利益をもたらす可能性があります。洗練されていない境界よりも 1 世代高いサブツリーをトリミングすると、検索ツリー内の多くの新しいブランチが削除されます (言い換えると、ツリーは深さとともに指数関数的に成長するため、検索を早期に剪定することが重要です)。

あなたの特定の問題については、最適化が可能だと思います。

まず、DFS について考えてみましょう。ボードのすべての順列は、ボードのどの構成からでも到達できると思います。結果として。DFS は後戻りせずに実装できます (ただし、ご存知だと思いますが)。深さのみの検索?(編集:Daniel Fischerによると、これは間違っています。状態の半分は到達可能ですが、バックトラッキングは到達不能状態に到達するのに役立たないため、バックトラッキングなしステートメントには影響しません)

しかし、まだ問題を解決していないことを確認するためだけに、多くの順列を移動したくない場合があります。バックトラッキングは実際に役立つかもしれません。または...

最終目標を検討してください。魔方陣には、操作をより慎重に選択するために利用できる特定のプロパティがいくつかあります。たとえば、行と列の合計は 15 になる必要があるため、9、8、および 7 は行または列を互いに共有できないことがわかります。9 と 6 もできません。6 は 8 と 1 または 7 と 2 と一緒に使用できます。ピジョンホールの原理により、6 は 5 と 4 と列/行を共有できませんが、合計すると 15 になります (各行/列には 9 と 9 のいずれかが含まれます)。 、8、または 7)。実際、あなたの問題には、すべての行、すべての列、リフレクション、および転置におけるある種の巡回順列を法とする独自の解決策があることに気付くかもしれません。対角要件は、有効な解をさらに制約します。

余談ですが、前の段落で使用したロジックは、制約ベースのプログラミングと同じです。これは実際には最適化手法ではありません (ただし、実行時ではなくても実装時間の最適化と見なされる可能性があります) が、同様に興味があるかもしれません (魔方陣と数独は、制約ベースのプログラミングを説明するために頻繁に使用されることにも注意してください)。 .

これで目標ができました:

  1. ソリューションの状態を記述します。
  2. 既知の解決状態の 1 つに最も少ない移動で到達します。

これは、問題が解決されるまでさまざまな順列を検索するのとは根本的に異なるアプローチです。動的計画法の解決策を見つけようとします。増分操作で開始状態から目標状態に移行する少し簡単な動的計画問題については、レーベンシュタイン編集距離問題を見てください。

于 2012-01-06T07:07:20.160 に答える
4

特定の例といくつかの一般原則に関する、ccoakleyの素敵な答えとstubbscrollのコメントに加えて、いくつかの発言。

この特定の問題には9つしかないというstubbscrollの発言について!= 362880 の異なる状態:
順列を数値としてエンコードする 1 つの (かなり簡単な) 方法は、辞書順で順列にインデックスを付けることです。例えば

0 1 2 3  -->  0
0 1 3 2  -->  1
0 2 1 3  -->  2
...
1 0 2 3  -->  6
1 0 3 2  -->  7
...
3 1 2 0  --> 21
3 2 0 1  --> 22
3 2 1 0  --> 23

トリックは階乗ベースでインデックスを書くことです、

n = a_1 * 1! + a_2 * 2! + a_3 * 3! + a_4 * 4! + ...

どこで0 <= a_k <= k。シンボルがある場合s、インデックスの範囲は 0 から s!-1 であるためs-1、n, の階乗ベース展開に係数があります(a_1,a_2,...,a_(s-1))。インデックス n の順列は、次のように求められます。

 for i = 1 to s-1
    the i-th symbol becomes the (a_(s-i)+1)-th smallest unused symbol
 the last symbol is the left over one

それは特に明確ではないので、例を挙げます。{1,2,3,4,5,6,7,8} のインデックス 4231 を持つ順列を探すとします。まず、階乗ベースで 4231 を展開します。

4231 = 1 + 2*2115 :  a_1 = 1
2115 = 0 + 3* 705 :  a_2 = 0
 705 = 1 + 4* 176 :  a_3 = 1
 176 = 1 + 5*  35 :  a_4 = 1
  35 = 5 + 6*   5 :  a_5 = 5
   5 = 5 + 7*   0 :  a_6 = 5

それ以降のすべての係数 (ここでは a_7 のみ) は 0 です。a_i を逆の順序 (a_7、a_6、...a_1) で書く方がよいので、

 coefficients      symbols       choice
0,5,5,1,1,0,1  1,2,3,4,5,6,7,8     1
 5,5,1,1,0,1    2,3,4,5,6,7,8      7
  5,1,1,0,1      2,3,4,5,6,8       8
   1,1,0,1        2,3,4,5,6        3
    1,0,1          2,4,5,6         4
     0,1            2,5,6          2
      1              5,6           6
      -               5            5

結果: 17834265。

246351 のインデックスを見つけます。

symbols     count     perm    index(head)
1,2,3,4,5,6   6   2,4,6,3,5,1    1         a_5 = 1
 1,3,4,5,6    5    4,6,3,5,1     2         a_4 = 2
  1,3,5,6     4     6,3,5,1      3         a_3 = 3
   1,3,5      3      3,5,1       1         a_2 = 1
    1,5       2       5,1        1         a_1 = 1

インデックスは `1*5! + 2*4! + 3*3! + 1*2! + 1*1! = 187。

これで、順列とそのインデックスを変換するかなり簡単な方法が得られました。変換は超高速ではありません (O(s^2)) が、簡単かつ高速な比較とルックアップが得られます (前に状態を見たことがありますか?)。それが利益であるかどうかは、それぞれのケースで決定される必要があります。

さて、当面の特定のケースでは、検索スペースを削減するいくつかの制限があります。

  • 各移動は 3 つの要素の巡回順列であるため、偶順列です。

したがって、そのような動きのすべての組み合わせは偶数順列でもあり、可能な状態の半分は到達不可能です。(最大で) 9!/2 = 181440 の到達可能な州が残っています。辞書式順序付けによる偶数順列の索引付けは、わずかに複雑です。重要な点は、インデックスの階乗ベースの展開における係数 a_k の合計が偶数である場合にのみ、順列が偶数であるということです。

制約と対称性を使用して探索空間を減らします。考えられるすべての状態を持つ構造を使用する検索戦略を採用している場合、これにより、対応する係数だけメモリ要件が削減されます。検索戦略が到達可能な状態のみに触れる場合、制約によってステップ数が減ることはありませんが、メモリ フットプリントが小さいため、検索を高速化できます。対称性を使用すると、同等の状態を識別することでステップ数を減らすことができます。

例の問題では、5 がすでに正しい場所にあり、最適な解がそれを動かさないというさらに良い状況があります。したがって、8 つのシンボルの偶数順列を考慮するだけでよく、検索空間を 8!/2 = 20160 の可能な状態に減らします。(それは明らかではありませんが。)

ただし、一般に、最適な解が可能な状態の特定のサブセットを決して離れないことを証明することは困難であるため、そのような制限を検索に直接課すことはめったにありません。しかし、多くの場合、このような制限を使用して問題の適切な
解決策を 見つけ、その適切な解決策を使用して、制限のない検索空間で最適な解決策を探す初期段階で枝刈りを行うことができます。

よく使用できるバリアントは、貪欲な戦略によって近似を見つけ、これを限界として使用して、徹底的な検索の初期にプルーニングすることです。

于 2012-01-06T15:22:10.360 に答える
3

行と列の回転を使用して 3x3 の魔方陣を生成する方法が問題である場合は、おそらく 3x3 の魔方陣を生成するための既知の解決策(またはこのアニメーション化されたもの) から始める必要があります。また、中央の行または列を回転させるクラスなど、特定のクラスの回転を単純に除外することもできます。

実際、4 回の回転だけで済む解決策があります。

DFS または BFS が指数関数的な探索空間をもたらす場合、通常は問題の構造を利用することで大きな勝利を収めます。魔方陣の場合、有効な答えを得るために中央の行または列を回転できないことを知っています。

于 2012-01-06T06:59:29.903 に答える
1

A*を使用してみてください。数字がある場所からあるべき場所までのマンハッタン距離のヒューリスティックを使用できます。目標の状態として、すでに魔方陣を念頭に置いていると思います。

于 2012-01-06T06:06:47.833 に答える
1

この全体的な質問に対する一般的な答えはありません。特定のケースには特定の答えがありますが、ブルートフォースアルゴリズムが必要とするものよりも大幅に優れた答えがないことが数学的に証明できる特定のケースもあります。

最良のアルゴリズムを見つけるという問題が活発な研究課題であり、非常に賢い人々がほとんど成功せずにそれに取り組んでいる場合も多くあります。

「NP完全性」について読むと、この問題のほんの一部にすぎないので興味深いかもしれませんが、よく研究されています。

于 2012-01-06T06:07:47.423 に答える