web-dev-qa-db-ja.com

なぜ最大合計サブアレイブルートフォースO(n ^ 2)なのですか?

最大サブアレイ合計 はコンピュータサイエンスで有名な問題です。

少なくとも2つの解決策があります。

  1. ブルートフォース、可能なすべてのサブアレイを見つけて、最大値を見つけます。
  2. Karanes Algorithm のバリエーションを使用して、配列の最初のパスを通過するときにグローバル最大値を計算します。

ビデオの中で チュートリアル 作者は力ずくの方法がO(n^2)であると述べ、読んでいる 別の答え ある人はそれをO(n^2)と考えているO(n^3)だと思います

強引な力はO(n^2)またはO(n^3)ですか?さらに重要なことに、ブルートフォースメソッドで実行した分析を説明して、それがO(?)であることを確認できますか?

9
mbigras

まあ、それは力がどれほど野蛮であるかに依存します。

すべての_(i, j): i <= j_ペアを生成し、その間の合計を計算すると、O(n^3)になります。

_....
for (int i = 0; i < n; i++)
    for (int j = i; j < n; j++) {
        int sum = 0;
        for (int k = i; k <= j; k++)
            sum += a[k];
        if (sum > max)
            max = sum;
    }
_

すべての位置から開始して現在の合計を計算すると、それはO(n^2)です。

_....
for(int i = 0; i < n; i++) {
    int sum = 0;
    for (int j = i; j < n; j++) {
        sum += a[j];
        if (sum > max)
            max = sum;
    }
}
_
23
AlexD

最大部分和問題の3つの解決策を次に示します。 solve1()はO(N)時間で実行され、solve2()はO(N ^ 2)で実行され、solve3()はO(N ^ 3)。solve1()はKadaneのアルゴリズムとして知られていることに注意してください。

O(N ^ 2)関数とO(N ^ 3)関数の違いは、O(N ^ 2)関数では、endインデックスがインクリメントされるたびに、合計が暗黙的に計算されることです。 O(N ^ 3)関数の場合、合計はstartendの間の3番目の明示的なループで計算されます。

すべての入力値が負の場合を処理するために、3つのアプローチすべてにコードを追加しました。

public class MaximumSubarraySum {

    /**
     * Solves the maximum subarray sum in O(N) time.
     */
    public static int solve1(int[] input) {

        int sum = input[0];
        int bestSum = sum;

        for (int i = 1; i < input.length; i++) {
            sum = Math.max(input[i], input[i] + sum);
            bestSum = Math.max(sum, bestSum);
        }

        return bestSum;
    }

    /**
     * Solves the maximum subarray sum in O(N^2) time. The two indices
     * 'start' and 'end' iterate over all possible N^2 index pairs, with 
     * the sum of input[start, end] always computed for every 'end' value. 
     */
    public static int solve2(int[] input) {

        int bestSum = -Integer.MAX_VALUE;

        for (int start = 0; start < input.length; start++) {

            // Compute the sum of input[start, end] and update
            // 'bestSum' if we found a new max subarray sum.

            // Set the sum to initial input value to handle Edge case
            // of all the values being negative.
            int sum = input[start];
            bestSum = Math.max(sum, bestSum);

            for (int end = start+1; end < input.length; end++) {
                sum += input[end];
                bestSum = Math.max(sum, bestSum);
            }
        }

        return bestSum;
    }

    /**
     * Solves the maximum subarray sum in O(N^3) time. The two indices
     * 'start' and 'end' iterate over all possible N^2 index pairs, and
     * a third loop with index 'mid' iterates between them to compute 
     * the sum of input[start, end].
     */
    public static int solve3(int[] input) {

        int bestSum = -Integer.MAX_VALUE;

        for (int start = 0; start < input.length; start++) {

            for (int end = start; end < input.length; end++) {

                // Compute the sum of input[start, end] using a third loop
                // with index 'mid'. Update 'bestSum' if we found a new 
                // max subarray sum.

                // Set the sum to initial input value to handle Edge case
                // of all the values being negative.
                int sum = input[start];
                bestSum = Math.max(sum, bestSum);

                for (int mid = start+1; mid < end; mid++) {
                    sum = Math.max(input[mid], input[mid] + sum);
                    bestSum = Math.max(sum, bestSum);
                }
            }
        }

        return bestSum;
    }


    public static void runTest(int[] input) {

        System.out.printf("\n");
        System.out.printf("Input: ");
        for (int i = 0; i < input.length; i++) {
            System.out.printf("%2d", input[i]);
            if (i < input.length-1) {
                System.out.printf(", ");
            }
        }
        System.out.printf("\n");

        int result = 0;

        result = MaximumSubarraySum.solve1(input);
        System.out.printf("solve1 result = %d\n", result);

        result = MaximumSubarraySum.solve2(input);
        System.out.printf("solve2 result = %d\n", result);

        result = MaximumSubarraySum.solve3(input);
        System.out.printf("solve3 result = %d\n", result);

    }


    public static void main(String argv[]) {

        int[] test1 = { -2, -3,  4, -1, -2, -1, -5, -3 };
        runTest(test1);

        int[] test2 = { -2, -3, -4, -1, -2, -1, -5,  3 };
        runTest(test2);

        int[] test3 = { -2, -3, -4, -1, -2, -1, -5, -3 };
        runTest(test3);

        int[] test4 = { -2, -3,  4, -1, -2,  1,  5, -3 };
        runTest(test4);  
    }
}

出力は次のとおりです。

Input: -2, -3,  4, -1, -2, -1, -5, -3
solve1 result = 4
solve2 result = 4
solve3 result = 4

Input: -2, -3, -4, -1, -2, -1, -5,  3
solve1 result = 3
solve2 result = 3
solve3 result = 3

Input: -2, -3, -4, -1, -2, -1, -5, -3
solve1 result = -1
solve2 result = -1
solve3 result = -1

Input: -2, -3,  4, -1, -2,  1,  5, -3
solve1 result = 7
solve2 result = 7
solve3 result = 7