6

少し方向性を探しているだけで、与えられた例は力ずくの反復を使用して解決できることに気付きましたが、かなり大きな例(たとえば、20x20または30x30)を解決できる可能性のあるよりエレガントな(つまり数学的な?)解決策を探しています)。これができない可能性は十分にあり、ブルートフォースに依存しないアプローチを思い付くのにほとんど成功していません...

nxnであるマトリックス(それをAと呼びます)があります。行列Aから点のサブセット(Bと呼びます)を選択したいと思います。サブセットはn個の要素で構成され、Aの各行と各列から1つの要素のみが取得されます。出力は解を提供する必要があります( B)Bを構成する要素の合計が、これらの制約が与えられた場合に可能な最大値になるようにします(たとえば、以下の例では25)。Bの複数のインスタンスが見つかった場合(つまり、同じ最大合計を与える異なる解)、最大の最小要素を持つBの解を選択する必要があります。

Bは、nxnである選択行列にすることもできますが、n個の目的の要素のみが非ゼロです。

例:A=の場合

|5 4 3 2 1|
|4 3 2 1 5|
|3 2 1 5 4|
|2 1 5 4 3|
|1 5 4 3 2|

=>Bは

|5 5 5 5 5|

ただし、A =

|5 4 3|
|4 3 2|
|3 2 1|

B =

|3 3 3|

Bの最小要素は3であるため、

|5 3 1|

また

|4 4 1|

これも合計で9になります

4

4 に答える 4

6

あなたの問題は、割り当て問題とほとんど同じです。これは、たとえば、多項式時間でハンガリーのアルゴリズムによって解決できます。

割り当ての問題は通常、最小化の問題ですが、行列に-1を掛けて定数を追加すると、この方法が適用可能になるはずです。さらに、複数の最適なソリューションの場合、正式なタイブレーキ条件はありません。ただし、この方法では、最適な合計を持つソリューションが得られます。mを最小の被加数とします。m以下のすべてのエントリをゼロに設定して行列を変更し、再度解きます。どちらか、最後のものよりも優れている同じ合計のソリューションを取得します。そうでない場合は、前のソリューションがすでに最適でした。

于 2012-05-02T08:52:26.567 に答える
0

痛い。このアルゴリズムは間違っています。それが間違っているので証明はありません、そしてそれ故にそれが正しいことを証明することは不可能です。;)完全に削除するにはあまりにも執着しているので、ここに残しておきます。これは、「これは正しく見えます。これが機能しない可能性はありません!」と言う代わりに、アルゴリズムを正式に証明する必要がある理由の良いデモンストレーションです。


とりあえず、この解決策を証明なしで提供します。証明スケッチがありますが、この問題に最適な下部構造を証明するのに問題があります。ともかく...

問題

数値の正方形の配列が与えられた場合、選択された数値の合計が最大になるように、できるだけ多くの「重複しない」数値を選択します。「重複しない」とは、2つの数値が同じ行または同じ列からのものであってはならないことを意味します。

アルゴリズム

  • A数の正方形の配列にしましょうn by n
  • th行とth列のAijの要素を示します。Aij
  • 行と列の共通部分を含むS( i1:i2, j1:j2 )正方形のサブ配列の重複しない数の最適な合計を示します。Ai1i2j1j2

次に、重複しない数値の最適な合計が示さS( 1:n , 1:n )れ、次のように与えられます。

S( 1:n , 1:n ) = max {  [ S(   2:n , 2:n   ) + A11 ]
                        [ S(   2:n , 1:n-1 ) + A1n ]
                        [ S( 1:n-1 , 2:n   ) + An1 ]
                        [ S( 1:n-1 , 1:n-1 ) + Ann ] }
(recursively)

Note that S( i:i, j:j ) is simply Aij.

つまり、サイズの正方形配列の最適な合計は、サイズnの4つのサブ配列のそれぞれの最適な合計を個別に計算しn-1、サブ配列と「除外された」要素の合計を最大化することによって決定できます。 "。

S for |# # # #|
      |# # # #|
      |# # # #|
      |# # # #|

Is the best of the sums S for:

|#      |      |      #|      |# # #  |       |  # # #|
|  # # #|      |# # #  |      |# # #  |       |  # # #|
|  # # #|      |# # #  |      |# # #  |       |  # # #|
|  # # #|      |# # #  |      |      #|       |#      |

実装

上記の再帰的アルゴリズムは、再帰的な解決策を示唆しています。

def S(A,i1,i2,j1,j2):
    if (i1 == i2) and (j1==j2):
        return A[i1][j1]
    else:
        return max ( S( A, i1+1, i2, j1+1, j2) + A[i1][j1] ],
                     S( A, i1+1, i2, j1, j2-1) + A[i1][j2] ],
                     S( A, i1, i2-1, j1+1, j2) + A[i2][j1] ],
                     S( A, i1, i2-1, j1, j2-1) + A[i2][j2] ], )

これにより!!がO(4^n)呼び出されることに注意してください。S()これは、「ブルートフォース」ソリューションの階乗時間計算量よりもはるかに優れていますO(n!)が、それでもパフォーマンスはひどいものです。

ここで注意すべき重要なことは、呼び出しの多くが同じパラメーターで繰り返されることです。たとえば、3 * 3配列を解く場合、各2*2配列は何度も解かれます。

これは、スピードアップのための2つの可能な解決策を示唆しています。

  • 再帰関数のキャッシュ結果を作成して、各に対して1回S()だけ必要になるようにします。これは、結果を計算するだけでよいことを意味します。他のすべてのリクエストはキャッシュから満たされます。(これはメモと呼ばれます。)S(A,i1,i2,j1,j2)i1,i2,j1,j2S()O(n^3)
  • 大きなn*n配列で上から始めて、連続して小さなサブ問題を処理する代わりに、可能な限り最小のサブ問題で下から始めて、ケースに積み上げていきます。n*nこれは動的計画法と呼ばれます。これもですが、常にキャッシュをヒットする必要がないためO(n^3)、はるかに高速です。O(n^3)

動的計画法ソリューションは、次のように進行します。

  • すべての1x1サブアレイに対する最適なソリューションを見つけます。(トリビアル。)
  • すべての2x2サブアレイに最適なソリューションを見つけます。
  • すべての3x3サブアレイに最適なソリューションを見つけてください。
  • ..。
  • すべてのn-1*n-1サブアレイの最適なソリューションを見つけます。
  • 完全なn*nサブアレイの最適なソリューションを見つけます。

このソリューションに関する注意:

  • まだ証拠はありません。私はそれに取り組んでいます。
  • S()上記のアルゴリズムでは、最適な合計のみが得られることに注意してください。どの数値が実際にその合計を構成しているかはわかりません。ソリューションへのパスをバックトレースする独自の方法を追加できます。
  • 上記のアルゴリズムは、2,2vs 。のような関係1,3が壊れるという特性を保証するものではなく、すべての個々の数をできるだけ大きくすることを支持します(勝つために)。最大数を支持して関係を壊すように2,2定義できると思います。max()可能であり、それはあなたが望むことをするでしょう、しかし私はそれを証明することができません。

一般的な注意事項:

動的計画法は、次の2つの特性を示す問題に対して高速アルゴリズムを考案するための強力な手法です。

  1. 最適な下部構造:問題はわずかに小さい部分に分割でき、それぞれが元の問題の解決策の一部として使用できます。
  2. サブ問題が重複しているということは、解決すべき実際のサブ問題がほとんどなく、サブ問題の解決策が何度も再利用されることを意味します。

問題に最適な部分構造があり、問題がわずかに小さい問題に分解される場合(たとえば、サイズの問題がサイズnのサブ問題に分解される場合)、問題は動的計画法n-1によって解決できます。

問題をはるかに小さなチャンクに分割できる場合(たとえば、サイズの問題nを半分に分割し、それぞれのサイズn/2)、それは分割統治法であり、動的計画法ではありません。分割統治法は一般に非常に高速です。たとえば、二分探索では、ソートされた配列内の要素がO(log n)時間内に検出されます。

于 2012-04-30T16:53:14.467 に答える
0

Matthiasが示したように、バックトラッキングを使用する必要があります。

  1. 合理的な解決策を見つけてください。各行から最大値を選択し、それらが重複していないかどうかを確認します。そうでない場合は、結果が重ならないように、解の一部を摂動させます。
  2. 部分解の適合性を定義します。各行の値を繰り返し取得していて、最初のk行からすでに値を取得しているとします。このソリューションの適合性は、すでに選択されている値の合計+残りの行と選択されていない列からの最大値に等しくなります
  3. ここで、解決策の検索を再帰的に開始します。最初の行から値を選択し、それらの適合度を計算して、優先キューに挿入します。現在の最適解(ステップ1で初期化)よりも適合度が低いすべての解を削除します。キューの先頭にあるソリューションを選択し、次のレベルのソリューションを計算して、優先キューに挿入し直します。すべての列と行から値を選択したら、合計を計算し、それが現在の最適値よりも高い場合は、それを置き換えます。
于 2012-04-30T18:17:43.540 に答える
-1

これは、対角線を気にせず、重み付きの解があることを除いて、 nクイーンの問題に関連しています。クイーンズの問題として、(複数の)バックトラックによってそれを解決することができます。

つまり、解決策を見つけたら、その重みを覚えて、魂を無効としてマークし、最初からやり直します。(a)最も重みの高いソリューションが優先されます。

于 2012-04-30T05:15:47.163 に答える