0

(n log n) を示すアルゴリズムを使用して、最大サブ配列問題を実装したいと考えています。

最大連続サブ配列、または配列内の連続要素の最大合計を見つけます。

仮定:すべての要素が負の数ではない


私はいくぶん実用的な解決策を持っています。問題は、重なっている中央の配列と、重なっているサブの問題を指定する適切なインデックスにあり、一部の配列は他の配列で正しい答えを得ていません。


比較と正確さの確認のために、Kadane のアルゴリズムとして知られるソリューションを実装しました (複雑さは Omega(n) だと思います)。

これはKandaneのアルゴリズムです(http://en.wikipedia.org/wiki/Maximum_subarray_problem):


 public static void Kadane(int array[]) {
        int max_ending_here = 0;
        for (int i = 0; i < array.length; i++) {
            max_ending_here = max_ending_here + array[i];
            max_so_far = Math.max(max_so_far, max_ending_here);
        }
        System.out.println("Kadane(int array []): " + max_so_far);
    }

分割と征服を使用してサブ配列の最大値を比較し、再帰が終了するまで、最大値を持つサブ配列で再帰呼び出しを行う私の再帰的実装

public static void findMaxSubArray(int[] array, int lowIndex, int highIndex) {

        int mid = 0;
        int arrayLength = 0;
        int maxEndingHere = 0;

        if (array == null) {
            throw new NullPointerException();
        } else if 
                //base condition 
           (array.length < 2 || (highIndex==lowIndex)) {
            maxLowIndex = lowIndex;
            maxHighIndex = highIndex;
            System.out.println("findMaxSubArray(int[] array, int lowIndex, int highIndex)");
            System.out.println("global Max Range, low:" + maxLowIndex + " high: " + maxHighIndex);
            System.out.println("global Max Sum:" + globalMaximum);
        } else {
            System.out.println();
            int lowMidMax = 0;
            int midHighMax = 0;
            int centerMax = 0;
            //array length is always the highest index +1 
            arrayLength = highIndex + 1;

            //if even number of elements in array 
            if (array.length % 2 == 0) {
                mid = arrayLength / 2;
                System.out.println("low: " + lowIndex + " mid: " + mid);
                for (int i = lowIndex; i < mid; i++) {
                    System.out.print(array[i] + ",");
                }
                //calculate maximum contigous array encountered so far in low to mid indexes 
                for (int i = lowIndex; i < mid; i++) {
                    maxEndingHere = maxEndingHere + array[i];
                    if (maxEndingHere < 0) {
                        maxEndingHere = 0;
                    }

                    if (globalMaximum < maxEndingHere) {
                        //new maximum found 
                        globalMaximum = lowMidMax = maxEndingHere;
                        lowIndex = i;
                    }

                }
                //end low mid calc 

                for (int i = mid; i <= highIndex; i++) {
                    System.out.print(array[i] + ",");
                }
                System.out.println("mid: " + mid + " high: " + highIndex);
                //calculate maximum contigous array encountered so far in mid to high indexes 
                for (int i = mid; i <= highIndex; i++) {
                    maxEndingHere = maxEndingHere + array[i];
                    if (maxEndingHere < 0) {
                        maxEndingHere = 0;
                    }

                    if (globalMaximum < maxEndingHere) {
                        //new maximum found 
                        globalMaximum = midHighMax = maxEndingHere;
                        mid = i;
                    }

                }
//end mid high calc
                //calculate maximum contigous array encountered so far in center array
                int lowCenter = mid -1;
                int highCenter = highIndex -1;

                System.out.println("lowCenter: " + lowCenter + " highCenter: " + highCenter);
                for (int i = lowCenter; i < highCenter; i++) {
                    System.out.print(array[i] + ",");
                }
                //calculate maximum contigous array encountered so far in low to mid indexes 
                for (int i = lowCenter; i < highCenter; i++) {
                    maxEndingHere = maxEndingHere + array[i];
                    if (maxEndingHere < 0) {
                        maxEndingHere = 0;
                    }

                    if (globalMaximum < maxEndingHere) {
                        //new max found
                        globalMaximum = centerMax = maxEndingHere;
                        lowCenter = i;

                    }

                }
                //end center calc 
                //determine which range contains the maximum sub array 
                //if lowMidMax <= midHighMax and centerMax
                if (lowMidMax >= midHighMax && lowMidMax >= centerMax) {
                    maxLowIndex = lowIndex;
                    maxHighIndex = mid;
                    //recursive call
                    findMaxSubArray(array, lowIndex, mid);
                }
                if (midHighMax >= lowMidMax && midHighMax >= centerMax) {
                    maxLowIndex = mid;
                    maxHighIndex = highIndex;
                    //recursive call
                    findMaxSubArray(array, mid, highIndex);
                }
                if (centerMax >= midHighMax && centerMax >= lowMidMax) {
                    maxLowIndex = lowCenter;
                    maxHighIndex = highCenter;
                    //recursive call
                    findMaxSubArray(array, lowCenter, highCenter);
                }

            }//end if even parent array 
            //else if uneven array 
            else {
                mid = (int) Math.floor(arrayLength / 2);
                System.out.println("low: " + lowIndex + " mid: " + mid);
                for (int i = lowIndex; i < mid; i++) {
                    System.out.print(array[i] + ",");
                }
                //calculate maximum contigous array encountered so far in low to mid indexes 
                for (int i = lowIndex; i < mid; i++) {
                    maxEndingHere = maxEndingHere + array[i];
                    if (maxEndingHere < 0) {
                        maxEndingHere = 0;
                    }

                    if (globalMaximum < maxEndingHere) {
                        //new maximum found 
                        globalMaximum = lowMidMax = maxEndingHere;
                        lowIndex = i;
                    }

                }
                //end low mid calc
                System.out.println("mid+1: " + (mid + 1) + " high: " + highIndex);
                for (int i = mid + 1; i <= highIndex; i++) {
                    System.out.print(array[i] + ",");
                }
                //calculate maximum contigous array encountered so far in mid to high indexes 
                for (int i = mid + 1; i <= highIndex; i++) {
                    maxEndingHere = maxEndingHere + array[i];
                    if (maxEndingHere < 0) {
                        maxEndingHere = 0;
                    }

                    if (globalMaximum < maxEndingHere) {
                        //new maximum found 
                        globalMaximum = midHighMax = maxEndingHere;
                        mid = i;
                    }

                }
                //end mid high calc
                //calculate maximum contigous array encountered so far in center array
                int lowCenter =  mid;
                int highCenter = highIndex -1;

                System.out.println("lowCenter: " + lowCenter + " highCenter: " + highCenter);
                for (int i = lowCenter; i < highCenter; i++) {
                    System.out.print(array[i] + ",");
                }
                //calculate maximum contigous array encountered so far in low to mid indexes 
                for (int i = lowCenter; i < highCenter; i++) {
                    maxEndingHere = maxEndingHere + array[i];
                    if (maxEndingHere < 0) {
                        maxEndingHere = 0;
                    }

                    if (globalMaximum < maxEndingHere) {
                        //new max
                        globalMaximum = centerMax = maxEndingHere;
                        lowCenter = i;
                    }

                }
                //end center calc 

                //determine which range contains the maximum sub array 
                //if lowMidMax <= midHighMax and centerMax
                  if (lowMidMax >= midHighMax && lowMidMax >= centerMax) {
                    maxLowIndex = lowIndex;
                    maxHighIndex = mid;
                    //recursive call
                    findMaxSubArray(array, lowIndex, mid);
                }
                if (midHighMax >= lowMidMax && midHighMax >= centerMax) {
                    maxLowIndex = mid;
                    maxHighIndex = highIndex;
                    //recursive call
                    findMaxSubArray(array, mid, highIndex);
                }
                if (centerMax >= midHighMax && centerMax >= lowMidMax) {
                    maxLowIndex = lowCenter;
                    maxHighIndex = highCenter;
                    //recursive call
                    findMaxSubArray(array, lowCenter, highCenter);
                }


            }//end odd parent array length 
        }//end outer else array is ok to computed 

    }//end method

結果: 配列 subArrayProblem1 を使用 = {1, 2, 3, 4, 5, 6, 7, 8};

Kadane(int array []): 36 low: 0 mid: 4 1,2,3,4,5,6,7,8,mid: 4 high: 7 lowCenter: 6 highCenter: 6

findMaxSubArray(int[] array, int lowIndex, int highIndex) global Max Range, low:7 high: 7 global Max Sum:36 BUILD SUCCESSFUL (合計時間: 0 秒)

Kadane と比較するとグローバルな最大合計は正確ですが、低インデックスと高インデックスの範囲は最後の再帰呼び出しを反映しています。

結果: 配列 subArrayProblem を使用 = {100, 113, 110, 85, 105, 102, 86, 63, 81, 101, 94, 106, 101, 79, 94, 90, 97};

Kadane(int array []): 1607 low: 0 mid: 8 100,113,110,85,105,102,86,63,mid+1: 9 high: 16 101,94,106,101,79,94,90,97,lowCenter: 16 highCenter: 15

findMaxSubArray(int[] array, int lowIndex, int highIndex) グローバル最大範囲、低:16 高: 16 グローバル最大合計:1526

グローバル最大値が正しくありません。違いは実際には 1 要素であり、要素 81 であることに注意してください

4

3 に答える 3

2

最大合計で部分配列を見つけるためのよりクリーンな方法 (D/C 再帰的な方法):

// A Divide and Conquer based Java solution
// To find a subarray with the maximum sum

import java.util.Arrays;
import java.util.Scanner;

class MaxSubArray {

    private static Result maxCrossingSum(int arr[], int l, int m, int h) {

        int sum = 0;
        int maxLeft = 0;
        int leftSum = Integer.MIN_VALUE;
        for (int i = m; i >= l; i--) {
            sum = sum + arr[i];
            if (sum > leftSum) {
                leftSum = sum;
                maxLeft = i;
            }
        }

        sum = 0;
        int rightSum = Integer.MIN_VALUE;
        int maxRight = arr.length - 1;
        for (int i = m + 1; i <= h; i++) {
            sum = sum + arr[i];
            if (sum > rightSum) {
                rightSum = sum;
                maxRight = i;
            }
        }

        return new Result(maxLeft, maxRight, leftSum + rightSum);
    }

    private static Result maxSubArray(int[] A, int low, int high) {

        if (low == high) {
            return new Result(low, high, A[low]);
        }

        int mid = (low + high) / 2;

        Result leftSubArray = maxSubArray(A, low, mid);
        Result rightSubArray = maxSubArray(A, mid + 1, high);
        Result maxCrossingSubArray = maxCrossingSum(A, low, mid, high);

        int leftSum = leftSubArray.sum;
        int rightSum = rightSubArray.sum;
        int crossSum = maxCrossingSubArray.sum;

        if (leftSum > rightSum && leftSum > crossSum) {
            return new Result(leftSubArray.low, leftSubArray.high, leftSubArray.sum);
        } else if (rightSum > leftSum && rightSum > crossSum) {
            return new Result(rightSubArray.low, rightSubArray.high, rightSubArray.sum);
        } else {
            return new Result(maxCrossingSubArray.low, maxCrossingSubArray.high,
                maxCrossingSubArray.sum);
        }
    }

    public static class Result {

        public int low;
        public int high;
        public int sum;
        public Result(int low, int high, int sum) {
            this.low = low;
            this.high = high;
            this.sum = sum;
        }

    }

    /* Driver program to test maxSubArray */
    public static Result maxSubArray(int[] arr) {
        return maxSubArray(arr, 0, arr.length - 1);
    }

    public static void main(String[] args) {

        Scanner sc = new Scanner(System.in);
        String[] arrString = sc.nextLine().split(" ");

        int n = arrString.length;

        int[] arr = new int[n];
        for (int i = 0; i < n; i++) {
            arr[i] = Integer.parseInt(arrString[i]);
        }

        Result result = maxSubArray(arr);

        int[] subArray = new int[result.high - result.low + 1];
        int j = 0;
        for (int i = result.low; i <= result.high; i++) {
            subArray[j++] = arr[i];
        }

        System.out.println(Arrays.toString(subArray));
        System.out.println("Sum : " + result.sum);
    }
}
于 2018-07-29T17:06:23.667 に答える
1

1. Kadane のアルゴリズムの実装が間違っています。負の数を含む配列では失敗します。正しいものは次のようになります。

 public static void Kadane(int array[]) {
        int max_ending_here = 0;
        for (int i = 0; i < array.length; i++) {
            max_ending_here = Math.max(array[i], max_ending_here + array[i]);
            max_so_far = Math.max(max_so_far, max_ending_here);
        }
        System.out.println("Kadane(int array []): " + max_so_far);
    }

そして、あなたのコードには次のような多くのバグがあります:

2.maxEndingHere次の答えを計算する前に、0 に初期化する必要があります。

[lowIndex,mid)
[mid, highIndex]
[lowCenter, highCenter]

現在は、最初の反復の前にのみ初期化されます。

3.lowCenterに初期化する必要がありますlowIndex

4.プログラムが不必要に長く複雑です...バグを見逃したかどうかわかりません...

于 2012-08-15T08:55:59.130 に答える
1

解決策は非常に単純pvです。これは、0<= k <= n-1 の k から i 番目の反復でアクセスしている変数までのサブ配列の合計を示す変数です。配列 s は最大のサブ配列を追跡し、s[i] は i 回目の反復でも見つかった最大のサブ配列です。c は元の配列です。p は、i 番目の反復で見つかった最大部分配列の開始点へのポインターです。tp は、p を正しい値に更新するための一時的なポインターです。

/**
 * @author : Yash M. Sawant
 */




#include <stdio.h>
#include <stdlib.h>
#include <time.h>

#define MAXLENGTH 17
#define MINVALUE -999



int main() {
    int i;

    int s[MAXLENGTH]; s[0] = 0;
    int c[MAXLENGTH] = {0, 13, -3, -25, 20, -3, -16, -23, 18, 20, -7, 12, -5, -22, 15, -4, 7};
    int pv = 0, p = -1, tp = -1;
    for(i = 1 ; i < MAXLENGTH ; i ++) {
        printf("%4d ", c[i]);
        if(s[i - 1] < pv + c[i]) {
            s[i] = pv + c[i];
            pv = pv + c[i];
            if(tp > p) {
                p = tp;
            }
        } else {
            s[i] = s[i - 1];
            pv = pv + c[i];
            if(pv < 0) {
                pv = 0; tp = i;
            }
        }
    }
    printf("\n");
    for(i = 0 ; i < MAXLENGTH ; i ++) {
        printf("%4d ", s[i]);
    }
    printf("\n");
    printf("Max Sub Array = %d and Starts at %d ", s[MAXLENGTH - 1], p);
    return 0;

}
于 2015-08-12T21:36:20.957 に答える