web-dev-qa-db-ja.com

O(n)時間でn x n行列の極小値を見つける

ですから、これは私の宿題ではありませんが、アルゴリズムとデータ構造(これで完了です)に関するコースのコースの評定されていない宿題から取ったものです。

N行n列のグリッドが表示されます。隣接するすべての値よりも小さい数は、極小値です。 (数値の近傍は、すぐ上、下、左、または右の1つです。ほとんどの数値には4つの近傍があります。側面の数値には3つあります。4つの角には2つあります。)分割統治アルゴリズム設計を使用します。数値のペア間のO(n)比較のみで極小値を計算するためのパラダイム。 (注:n2 入力の数値は、それらすべてを見る余裕はありません。ヒント:どのタイプの再発が望ましい上限を与えるかを考えてください。)

番号は順不同なので、O(n以外のことをどのようにして回避できるかわかりません。2)比較。

26
Rohit Pandey

ジャレッドの答えのように、それがどのようにうまくいかないかを見ることで、言葉を適応させることができます。

その答えのアイデア-これは良いことです-は「下り坂を転がる」ことです。これは、要素を使用している場合、それが極小値であるかどうかを確認することを意味します。その場合は、完了です。それ以外の場合は、最近傍の最小値に進みます。最終的にこれは終了する必要があります。これは、すべてのステップがより小さな要素に対するものであり、有限配列では永遠に続くことができないためです。

このアプローチの問題は、「ローリング」がいたるところに蛇行する可能性があることです。

20 100 12  11 10 100  2
19 100 13 100  9 100  3
18 100 14 100  8 100  4
17  16 15 100  7   6  5

左上から「下り坂」を開始すると、配列内の要素の約半分にアクセスします。それは多すぎるので、少し制限する必要があります。

中央の列と中央の行を調べることから始めます。それらすべての中で最小の要素を見つけて、そこから始めます。

そこから1歩「下り坂」を転がり、4つの象限の1つに入ります。中央の列または行、あるいはその両方の隣接要素が大きいため、象限の1つを入力します。したがって、隣接する2つの象限のうちの1つだけが "下り坂"になります。

ここから「下り坂」を走ったらどうなるかを考えてみましょう。明らかに、最終的には極小値に達します。 (時間がかかりすぎるので、実際にはこれを行いません。)しかし、動き回る過程で、その象限を離れることは決してありません。 。そうするためには、中央の列または中央の行のいずれかを横切る必要があり、これらの要素はどれも最初の場所よりも小さくありません。したがって、その象限には極小値が含まれています。

したがって、線形時間で、極小値を含む必要がある象限を特定し、nを半分に削減しました。今すぐ再帰します。

このアルゴリズムには2n + 2n/2 + 2n/4 + ...の時間がかかります。これは4nに等しく、O(n)なので、これで完了です。

興味深いことに、重要な部分を除いて、「ローリングダウンヒル」をほとんど使用しませんでした。アルゴリズムが機能することを証明します。

[更新]

Incassatorが指摘 のように、この答えは完全に正しくはありません。「再帰した」後、象限から再びロールアウトする可能性があるためです...

最も簡単な修正は、「下り坂を転がる」前に、中央の行、中央の列および境界の中で最小の要素を見つけることです。

33
Nemo

Nemoが承認した答えはいいですが、完全には正しくありません。

したがって、線形時間で、極小値を含む必要がある象限を特定し、nを半分に削減しました。今すぐ再帰します。

私は「ただの再帰」ビットについて言及しています。問題は、次の反復でnot元のグリッドの極小値(次のxは任意の大きな数を意味する)である極小値を見つける可能性があるため、直接実行できないためです。

 x  x 39  x  x 50  x  x  x  x  x
 x  x 38  x  x 49  x  x  x  x  x
37 36 33 34 35 48  x  x  x  x  x
 x  x 32  x  1 10  x  x  x  x  x
 x  x 31  x  x 47  x  x  x  x  x
46 45 30 44 43 60 51 52 53 54 55
 x  x  2  x  x 56  x  x  x  x  x
 x  x  x  x  x 57  x  x  x  x  x
 x  x  x  x  x 58  x  x  x  x  x
 x  x  x  x  x 59  x  x  x  x  x

最初の反復では、10が中央の行と中央の列の最小値であることがわかります。左に移動します(1は10未満なので)。したがって、次の反復は左上の象限です。しかし、現在、中央の行と列の最小値は31(象限の境界がその一部と見なされる場合は30)になります。次に、それは極小値であると結論付けます。しかし、それは完全なグリッドのためではありません。

この不幸な欠陥はさまざまな方法で修正できます。私はそれを次のように解決しました:

グリッド自体に加えて各反復で、現在の最小候補を追跡します(最初の反復後の上記の例では1です。初期状態では、最小候補はプラス無限大と言えます)。中央の行と列の最小値を計算し、最小候補と比較します。後者が小さい場合は、象限最小候補を含むに再帰します。そうでない場合は、前の候補を忘れてから、新しい中央の行/列の最小値が実際に局所的な最小値であるかどうかを確認します。そして、そうでない場合は、通常のように再帰して、そこから下降する象限に移動します(そして新しい最小候補を追跡します)。

または、 これはおそらくMIT講義 で説明されているように手順を変更できます:各行で、中央の行/列ではなく中央の行/列を見ることができますandグリッド境界次に、アルゴリズムが再び正しくなります。

あなたはあなたが好きな方法を選択します。

14
Incassator

これは本当に簡単だと思います。

問題を3Dに変換して、アルゴリズムが機能する理由を確認します。マトリックスをテーブルに置きます。各セルから伸びている柱があり、柱の高さがその値に正比例しているとしましょう。任意の柱にボールを置きます。極小になるまで、ボールが常に最も低い高度である隣接する柱に落ちるようにします。

7

まあ、これはあなたがそれを分割して征服する方法です。

1)n x n行列を4つのn/2 x n/2サブ行列に分割します。

2)2 x 2マトリックスになるまで、サブマトリックスを再帰的に分割し続けます

3)2 x 2行列の要素が局所的な最小値かどうかを確認します。

漸化式は次のとおりです:T(n)= 4 * T(n/2)+ O(1)

4 * T(n/2)は4 n/2 x n/2サブ行列、O(1) 2 x 2サブ行列に極小があるかどうかをチェックするため

マスター定理は、これはO(n ^ 2)最悪の場合の限界であると述べています。

しかし、私は最良のケースO(n)バウンドを取得できると思います、

( "RED ALERT!--- BEST CASE IS BOGUS、JUST BOGUS --- RED ALERT!")。

手順3で極小値を見つけた後に再帰スタックを終了した場合。

疑似コード:

private void FindlocalMin(matrix,rowIndex,colIndex,width){
    if(width == 1){ checkForLocalMinimum(matrix,rowIndex,colIndex); return;} //2x2 matrix
    FindlocalMin(matrix,rowIndex,colIndex,width/2);  
    FindlocalMin(matrix, (rowIndex + (width/2) + 1) ,colIndex,width/2);
    FindlocalMin(matrix,rowIndex, (colIndex + (width/2) + 1) ,width/2);
    FindlocalMin(matrix,(rowIndex + (width/2) + 1), (colIndex + (width/2) + 1) ,width/2);
}

private void checkForLocalMinimum(.........){
    if(found Local Minimum in 2x2 matrix){ exit recursion stack;} 
}

これが Java実装 です。

1
Charan

Algorithms 4th Edition Book Exerciseの演習に従って開発された、3 * 3以上のマトリックス用のコード機能。

それは次のように機能します

  1. 与えられた行列の中央の行を探し、それから最小値を見つけます
  2. その最小値については、上下の要素を探して、それが極小であることを確認してください
  3. そうでない場合は、検索マトリックスをで制限します
    1. 行:中央の行の要素よりも要素が少ない行の半分を選択します。
    2. colmun:行の最小値が見つかったインデックスを選択します。
  4. 手順1〜3を繰り返す

パブリッククラスMinimumOfMatrix {

private static int findLocalMinimum(int[][] matrix, int rowStart, int rowEnd, int colStart, int colEnd) {

    int midRow = (rowStart + rowEnd) / 2;
    int minPos = findMin(matrix, midRow, colStart, colEnd);

    if (minPos >= (colStart + colEnd) / 2)
        colStart = (colStart + colEnd) / 2;
    else
        colEnd = (colStart + colEnd) / 2;

    if (matrix[midRow][minPos] < matrix[midRow + 1][minPos]
            && matrix[midRow][minPos] < matrix[midRow - 1][minPos]) {
        return matrix[midRow][minPos];
    } else if (matrix[midRow][minPos] > matrix[midRow + 1][minPos]) {
        return findLocalMinimum(matrix, midRow, rowEnd, colStart, colEnd);
    } else {
        return findLocalMinimum(matrix, rowStart, midRow, colStart, colEnd);
    }

}

private static int findMin(int[][] matrix, int midRow, int colStart, int colEnd) {
    int min = Integer.MAX_VALUE;
    int pos = -1;

    for (int i = colStart; i < colEnd; i++) {
        if (matrix[midRow][i] < min) {
            min = matrix[midRow][i];
            pos = i;
        }

    }

    return pos;
}

public static void main(String[] args) {
    // Best Case
    /*
     * int[][] matrix= { {1,-2,4,-6,1,8}, {-3,-6,-8,8,1,3}, {1,2,6,-2,-8,-6},
     * {-2,9,6,3,0,9}, {9,-1,-7,1,2,-6}, {-9,0,8,7,-6,9} };
     */

    // Two Iteration Down Case
    /*
     * int[][] matrix= { { 1,-2, 4,-6, 1, 8}, {-3,-6,-8, 8, 1, 3}, { 1, 2, 6, 9, 0,
     * 6}, {-2, 9, 6,-1,-1, 9}, { 9,-1,-7, 1, 2,-6}, {-9, 0, 8, 7,-6, 9} };
     */

    /*
     * //Left Down Case int[][] matrix= { { 1,-2, 4,-6, 0, 8}, {-3,-6,-8, 8,-2, 3},
     * {-2, 9, 6,-1, 1, 9}, { 1, 0, 6, 9, 2, 6}, { 9,-1,-7, 1, 2,-6}, {-9, 0, 8,
     * 7,-6, 9} };
     */

    int[][] matrix = { { 1, -2, 4, }, { -3, -6, -8, }, { -2, 9, 6, }

    };

    System.out.println(findLocalMinimum(matrix, 0, matrix.length, 0, matrix.length));

}

}

1
Vivek Bhojawala