Maximum Subsequence and Solving the Problem

Maximum Subsequence and Solving the Problem

author: twq time: 2021/9/9

Catalog

Maximum Subsequence and Problem Description

For arrays with positive and negative values such as [4,-3,5,-2,-1,2,6,-2], the subsequences of the array are [4,-3,5], [5,-2,-1,2,6], [2,6], and so on. Their sum is 6,11,8, so how can I find the maximum sequence of the array and how can I find it?

Algorithmic Solution

Five methods are listed below, and the importance of algorithm optimization is appreciated by comparing them.

Algorithm1

The most direct way is to traverse through all the subsequences and sum them to pick the largest one. So how do you traverse all the subsequences?

Two variables i, j, I can be used to represent the starting position of the subsequence, J to represent the ending position of the subsequence, and then the sum of the subsequences is obtained by adding array[i] to array[j]. By recording the sum of the subsequences with a maxSum, and comparing each subsequence traversed, pick the largest one. The code below shows that the time complexity of the algorithm is (O(n3)).

public int maxSubSum1(int[] array) {
    int maxSum = 0;
    for(int i = 0; i < array.length; ++i) {
        for(int j = i; j < array.length; ++j) {
            int thisSum = 0;
            for(int k = i; k <= j; ++k) {
                thisSum += array[k];
            }
            if(thisSum > maxSum) {
                maxSum = thisSum;
            }
        }
    }
    return maxSum;
}

Algorithm2

Algorithm1 is easy to understand, but the time complexity is too high. Can you get rid of the multiple loops? Considering that the third loop k of algorithm 1 is from i to j, if you traverse the subsequences at the same time as the J loop, you can get rid of the one loop and turn it into the following code. You can see that the time complexity of the algorithm is O(n2).

public int maxSubSum2(int[] array) {
    int maxSum = 0;
    for(int i = 0; i < array.length; ++i) {
        int thisSum = 0;
        for(int j = i; j < array.length; ++j) {
            thisSum += array[j];
            if(thisSum > maxSum) {
                maxSum = thisSum;
            }
        }
    }
    return maxSum;
}

Algorithm3

In general, recursive algorithms are generally less time-complex than multiple loops, usually n logN. Consider using recursion to solve these kinds of problems. Recursion normalization generally breaks down large problems into small ones until they can be solved directly. For arrays, it is common to use a two-part approach. For the largest subsequence and where it occurs, there are three situations: one occurs in the left half of the array, the other in the right half of the array, and the third across the array.Left and right half. Find the maximum values for each of the three cases, and then compare them to pick out the largest, which is the solution of the problem. The algorithm description can be used as a reference.

Maximum Continuous Subsequence and: Recursive and Dynamic Planning_anlian523 Blog-CSDN Blog_Maximum Continuous Subsequence

public int maxSubSum3(int[] array) {
    return maxSubRec(array, 0, array.length - 1);
}
private int maxSubRec(int[] array, int left, int right){
    if(left == right){
        return Math.max(array[left], 0);
    }

    int center = (left + right) / 2;
    int maxLeftSum = maxSubRec(array, left, center);
    int maxRightSum = maxSubRec(array, center + 1, right);
    int maxLeftBorderSum = 0, leftBorderSum = 0;
    int maxRightBorderSum = 0, rightBorderSum = 0;
    for(int i = center; i >= left; --i) {
        leftBorderSum += array[i];
        if(leftBorderSum > maxLeftBorderSum) {
            maxLeftBorderSum = leftBorderSum;
        }
    }
    for(int i = center + 1; i <= right; ++i) {
        rightBorderSum += array[i];
        if(rightBorderSum > maxRightBorderSum) {
            maxRightBorderSum = rightBorderSum;
        }
    }
    return Math.max(Math.max(maxLeftSum, maxRightSum), maxLeftBorderSum + maxRightBorderSum);
}

Algorithm4

Copy the original array, then add up the previous non-negative values to get the maximum subsequence sum that contains all the previous elements. Then find the largest one, which is the subsequence and the largest subsequence. The time complexity is O(n), and the space complexity is O(n).

The code is as follows:

public int maxSubSum4(int[] array) {
    int[] arrayCopy = Arrays.copyOf(array, array.length);
    for(int i = 0; i < array.length - 1; ++i) {
        if(arrayCopy[i] > 0) {
            arrayCopy[i+1] += arrayCopy[i];
        }
    }
    int maxSubSum = 0;
    for(int i : arrayCopy) {
        if(maxSubSum < i) {
            maxSubSum = i;
        }
    }
    return maxSubSum;
}

Algorithm5

The core principle is that if a subsequence is the subsequence of the maximum sequence sum, then the subsequence must be accumulated in a non-negative form. If it is negative, then a larger subsequence of sequence sum can be obtained by discarding the previous negative number. Its time complexity is O(n), and its spatial complexity is constant.

The code is as follows:

public int maxSubSum5(int[] array) {
    int maxSum = 0;
    int thisSum = 0;
    for(int i : array) {
        thisSum += i;
        if(thisSum > maxSum)    maxSum = thisSum;
        else if(thisSum < 0)    thisSum = 0;
    }
    return maxSum;
}

Generation of test data

In order to visually compare the advantages and disadvantages of the algorithm from run time, a large test data is needed. So a large array including positive and negative numbers is needed to test. To generate such an array, a class is written using the Java random library, which can evenly generate a range of integers. The code is as follows: There are two methods available, and the nRepeatedGenerate method generates nNumbers from min to Max can be duplicated between number and number. The nGenerate method generates numbers from n min to max, which cannot be duplicated between number and number (be careful to satisfy max - min > n, otherwise you will end up in an infinite loop). Using these two methods, you can generate a large amount of test data.

import java.util.Random;

public class Generate {
    public int[] nRepeatedGenerate(int n, int min, int max) {
        int[] array = new int[n];
        Random rand = new Random(System.currentTimeMillis());
        int width = max - min;
        for(int i = 0; i < n; ++i) {
            array[i] = rand.nextInt(width) + min;
        }
        return array;
    }
    public int[] nGenerate(int n, int min, int max) {
        int[] array = new int[n];
        int i = 0;
        Random rand = new Random(System.currentTimeMillis());
        while(i < n) {
            boolean flag = false;
            int tmp = rand.nextInt(max-min) + min;
                for(int k = 0; k < i; ++k){
                    if(array[k] == tmp){
                        flag = true;
                        break;
                    }
                }
                if(!flag)    {array[i++] = tmp;}
        }
        return array;
    }
}

Algorithm runtime comparison

Using the above method, a number containing 10,000 numbers between -1,000 and 1,000 is generated, with the following code:

public class MaxSubSumTest {
    public static void main(String[] args) {
        System.out.println("----------test start----------\n");
        Generate generate = new Generate();
        int[] array = generate.nRepeatedGenerate(10000, -1000, 1000);
        System.out.println("Array: " + Arrays.toString(array));
        MaxSubSum maxSubSum = new MaxSubSum();
        long startTime = System.currentTimeMillis();
        System.out.println("MaxSubSum1: " + maxSubSum.maxSubSum1(array));
        long endTime1 = System.currentTimeMillis();
        System.out.println("time1     : " + (endTime1 - startTime) + "ms");
        System.out.println("MaxSubSum2: " + maxSubSum.maxSubSum2(array));
        long endTime2 = System.currentTimeMillis();
        System.out.println("time2     : " + (endTime2 - endTime1) + "ms");
        System.out.println("MaxSubSum3: " + maxSubSum.maxSubSum3(array));
        long endTime3 = System.currentTimeMillis();
        System.out.println("time3     : " + (endTime3 - endTime2) + "ms");
        System.out.println("MaxSubSum4: " + maxSubSum.maxSubSum4(array));
        long endTime4 = System.currentTimeMillis();
        System.out.println("time4     : " + (endTime4 - endTime3) + "ms");
        System.out.println("MaxSubSum5: " + maxSubSum.maxSubSum5(array));
        long endTime5 = System.currentTimeMillis();
        System.out.println("time5     : " + (endTime5 - endTime4) + "ms");
        System.out.println("\n----------test end----------");
    }
}

Computing the maximum subsequence sum of the array shows that when there are 10,000 items in the array, algorithm 1 has been running for more than two minutes, algorithm 2 only has 62 milliseconds, and the other three algorithms are all less than 1 ms.

In order to further compare the advantages and disadvantages of the following four algorithms, the amount of data is expanded again. This time 300,000-1000 to 1000 numbers are generated and calculated. The result is as follows: When the number n doubles 30 times, the time complexity is O(n2) Algorithms 4 and 5 also take less than 1 millisecond to run, so it is really important to see whether they are good or bad.

Tags: Algorithm

Posted on Sat, 11 Sep 2021 13:40:39 -0400 by rulkster2