Algorithm 在实数列表中查找最大间隔和

Algorithm 在实数列表中查找最大间隔和,algorithm,math,complexity-theory,Algorithm,Math,Complexity Theory,下面是一位同事为获得编程职位而提出的面试问题。我认为这对于观察被采访者仔细思考是很好的。我很想得到社会对它的反应 给定长度为N的实数列表,例如[a_1,a_2,…,a_N],找到存在索引1的最大值M的复杂性是什么复杂性是: 该算法跟踪中的暂定最大子序列(maxSum、maxStartIndex、maxEndIndex)。它在currentMaxSum中累加部分和,并在该部分和大于maxSum时更新最佳范围 这可能是错误的,因为它非常简单 开始将0到n之间的所有元素相加,并确定滚动总和最高的索引。

下面是一位同事为获得编程职位而提出的面试问题。我认为这对于观察被采访者仔细思考是很好的。我很想得到社会对它的反应

给定长度为N的实数列表,例如
[a_1,a_2,…,a_N]
,找到存在索引1的最大值M的复杂性是什么复杂性是:

该算法跟踪
中的暂定最大子序列(maxSum、maxStartIndex、maxEndIndex)
。它在
currentMaxSum
中累加部分和,并在该部分和大于
maxSum
时更新最佳范围


这可能是错误的,因为它非常简单

  • 开始将0到n之间的所有元素相加,并确定滚动总和最高的索引。这将是间隔的上限
  • 同样的向后操作,得到你的下边界。(从上边界开始就足够了。)
  • 这看起来像O(n)。

    它是
    O(n)

    int和=0;
    int M=0;//这是输出
    foreach(输入中的int n){
    总和+=n;
    如果(总和>M)
    M=总和;
    如果(总和<0)
    总和=0;
    }
    

    其思想是保留自上次重置以来遇到的所有整数的总和。当总和低于零时会发生重置-即当前间隔中有太多负数,使其可能成为最佳值。

    请尝试此代码。。在数组中至少有一个+ve数字可以正常工作。。O(n)仅使用一个循环

    public static void main(String[] args) {
        int length ;
        int a[]={-12, 14, 0, -4, 61, -39};  
        length=a.length;
    
        int absoluteMax=0, localMax=0, startIndex=0, lastIndex=0, tempStartIndex=0;
        for (int index=0;index<length;index++) {
            localMax= localMax + a[index];
            if(localMax < 0){ localMax=0; tempStartIndex = index + 1;}
            if(absoluteMax < localMax) {
                absoluteMax = localMax; 
                lastIndex =index; 
                startIndex=tempStartIndex;
            }
        }
    
        System.out.println("startIndex  "+startIndex+"  lastIndex "+ lastIndex);    
        while (startIndex <= lastIndex) {
            System.out.print(" "+a[startIndex++]);
        }
    }
    
    publicstaticvoidmain(字符串[]args){
    整数长度;
    INTA[]={-12,14,0,-4,61,-39};
    长度=a.长度;
    int absoluteMax=0,localMax=0,startIndex=0,lastIndex=0,tempStartIndex=0;
    
    对于(int index=0;index,这是一个经典的、众所周知的问题,在任何算法课程中都是一个极好的开眼界。很难找到一个更好/更简单的初学者。 您可以找到n*3-、n*2-、nlogn-甚至简单的n-算法

    我发现约翰·本特利(John Bentley)1986年的《编程珍珠》(Programming Pearls)中讨论/解决了这个问题- 多年来,我们在NTNU/Trondheim的算法课程中一直使用它。 大约20年前,我第一次在一次约250名学生的考试中使用它,当时只有一名学生发现了上述4种解决方案。他比约恩·奥尔斯塔德(Bjørn Olstad)成为特隆赫姆NTNU的“有史以来最年轻的教授”,除了在奥斯陆领导MSFT搜索部门外,他仍然保持着这种地位。 比约恩也接受了挑战,找到了该算法的良好实际应用。你看到了一些吗

    • 阿恩·哈拉斯

    我想详细解释一下卡丹算法的工作原理。该算法是在我目前参加的一个课程中介绍的,但只是一个模糊的解释。下面是哈斯克尔算法的一个实现:

    maxCont l = maxCont' 0 0 l
    
    maxCont' maxSum _ [] = maxSum
    maxCont' maxSum thisSum (x:xs)
      | newSum > maxSum = maxCont' newSum newSum xs
      | newSum < 0      = maxCont' maxSum 0 xs
      | otherwise       = maxCont' maxSum newsum xs
      where
        newSum = thisSum + x
    
    这个疯狂的函数是什么?
    maxCont'?让我们来制定一个简单的规范,说明它应该做什么。我们希望以下内容保持不变,前提是
    0≤ 蓟马≤ 最大和

    maxCont l = maxCont' 0 0 l
    
    maxCont' maxSum _ [] = maxSum
    maxCont' maxSum thisSum (x:xs)
      | thisSum + x > maxSum = maxCont' (thisSum + x) (thisSum+x) xs
      | thisSum + x < 0      = maxCont' maxSum 0 xs
      | otherwise            = maxCont' maxSum (thisSum+x) xs
    
    maxCont' maxSum thisSum [] = maxSum
    maxCont' maxSum thisSum l  = maximum [maxSum, thisSum + maxInit l, maxCont l]
    
    其中,
    maxInit l
    l
    的初始段的最大和,
    maxCont
    l
    的最大连续和

    琐碎但重要的事实:对于所有
    l
    maxInit l≤ maxCont l
    。显然,上述规范保证了
    maxCont l=maxCont'0 l
    ,这正是我们想要的。与其试图直接解释为什么maxCont'的最终版本实现了上述规范(我真的不知道怎么做),我将展示如何从中派生它,一步一步地转换规范,直到它成为代码,这肯定是正确的。正如所写的,此规范没有给出一个实现:如果
    maxCont
    按照上面描述的
    maxCont'
    定义,它将作为
    maxCont>永远循环“
    调用
    maxCont
    调用
    maxCont”
    使用相同的列表。因此,让我们将其展开一点,以显示我们需要的部分:

    maxCont' maxSum thisSum (x:xs) =
                         maximum [maxSum, thisSum + maxInit (x:xs), maxCont (x:xs)]
    
    这还没有解决任何问题,但它暴露了一些问题。让我们使用它。
    thisSum+maxInit(x:xs)
    thisSum
    thisSum+x+maxInit xs
    。但是
    thisSum≤ maxSum是一个先决条件,因此在计算最大值时可以忽略这个可能性。
    maxCont(x:xs)
    是一个包含或不包含
    x
    的和。但是如果它包含
    x
    ,则它与
    maxInit(x:xs)相同< /代码>,它被前面所覆盖,所以我们可以忽略这种可能性,只考虑“代码> Max CONT(x:xs)=Max CONT XS的情况。因此,我们到达下一个版本:

    maxCont' maxSum thisSum (x:xs) = maximum [maxSum, thisSum+x+maxInit xs, maxCont xs]
    
    最后,这一个是正确的递归,但我们有办法获得高效的代码,特别是因为神秘的maxInit太慢了

    现在我们几乎没有足够的优势来扭转局面。现在我们已经消除了不可能的情况,我们将添加一些重复的情况,这将把这三个情况放回相同的形式,以便我们可以替换原始规范中的
    maxCont'
    。在第一种情况下,我们没有第一项,因此我们需要使用我们知道不会超过其他条件的东西。保持
    thisSum的不变量≤ maxSum
    ,我们需要使用
    thisSum+x
    。在第二种情况下,我们没有第二个看起来像
    something+maxInit xs
    ,但我们知道
    maxInit xs≤ maxCont xs
    ,因此我们可以安全地坚持使用
    0+maxInit xs

    maxCont' maxSum thisSum (x:xs)
      | maxSum < thisSum + x     = maximum [(thisSum+x), (thisSum+x)+maxInit xs, maxCont xs]
      | thisSum + x < 0          = maximum [maxSum,      0+maxInit xs,           maxCont xs]
      | 0 ≤ thisSum + x ≤ maxSum = maximum [maxSum,      thisSum+x+maxInit xs,   maxCont xs]
    
    得到

    maxCont' maxSum thisSum (x:xs)
      | maxSum < thisSum + x     = maxCont' (thisSum+x) (thisSum+x) xs
      | thisSum + x < 0          = maxCont' maxSum 0 xs
      | 0 ≤ thisSum + x ≤ maxSum = maxCont' maxSum (thisSum+x) xs
    
    maxCont'maxSum
    
    maxCont' maxSum thisSum (x:xs)
      | maxSum < thisSum + x     = maximum [        thisSum+x+maxInit xs, maxCont xs]
      | thisSum + x < 0          = maximum [maxSum,                       maxCont xs]
      | 0 ≤ thisSum + x ≤ maxSum = maximum [maxSum, thisSum+x+maxInit xs, maxCont xs]
    
    maxCont' maxSum thisSum (x:xs)
      | maxSum < thisSum + x     = maximum [(thisSum+x), (thisSum+x)+maxInit xs, maxCont xs]
      | thisSum + x < 0          = maximum [maxSum,      0+maxInit xs,           maxCont xs]
      | 0 ≤ thisSum + x ≤ maxSum = maximum [maxSum,      thisSum+x+maxInit xs,   maxCont xs]
    
    maxCont' maxSum thisSum l = maximum [maxSum, thisSum + maxInit l, maxCont l]
    
    maxCont' maxSum thisSum (x:xs)
      | maxSum < thisSum + x     = maxCont' (thisSum+x) (thisSum+x) xs
      | thisSum + x < 0          = maxCont' maxSum 0 xs
      | 0 ≤ thisSum + x ≤ maxSum = maxCont' maxSum (thisSum+x) xs
    
    maxCont :: (Num a, Ord a) => [a] -> a
    maxCont = fst . foldl maxCont' (0,0)
      where
        maxCont' (maxSum, thisSum) x
          | maxSum < newSum = (newSum, newSum)
          | newSum < 0      = (maxSum, 0)
          | otherwise       = (maxSum, newSum)
          where newSum = thisSum + x
    
    {-5, -1, -2, -3, -4}
    { 12, 14, 0, -4, 61, -39}
    {2, -8, 3, -2, 4, -10}
    
    public int FindLargestSum(int[] arr)
    {
        int max = Integer.MIN_VALUE;
        int sum = 0;
    
            for(int i=0; i < arr.length; i++)
            {   
                if(arr[i] > max) max = arr[i];
    
                sum += arr[i];
    
                if(sum < 0)
                    sum = 0;
                else if(sum > max)
                    max = sum;
            }
    
        return max;
    }
    
    public class MaxSubSum {
        /**
         * Find max sub array, only include sub array with positive sum.
         * <p>For array that only contains non-positive elements, will choose empty sub array start from 0.
         * <p>For empty input array, will choose empty sub array start from 0.
         *
         * @param arr input array,
         * @return array of length 3, with elements as: {maxSum, startIdx, len};
         * <p>tips: should use 'len' when loop the returned max sub array, so that it could also work for empty sub array,
         */
        public static int[] find(int[] arr) {
            if (arr.length == 0) return new int[]{0, 0, 0}; // empty array, no sub array,
    
            int maxSum = 0;    // max sum, found so far,
            int maxStart = 0;  // start of max sum,
            int maxLen = 0;    // length of max subarray,
    
            int sum = 0;  // current sum,
            int start = 0;    // current start,
    
            for (int i = 0; i < arr.length; i++) {
                if (arr[i] > 0) { // get a positive,
                    if (sum <= 0) {  // should restart,
                        start = i;
                        sum = arr[i];
                    } else sum += arr[i];
    
                    if (sum > maxSum) { // get a larger sum,
                        maxSum = sum;
                        maxStart = start;
                        maxLen = i - start + 1;
                    }
                } else sum += arr[i]; // 0 or negative number,
            }
    
            return new int[]{maxSum, maxStart, maxLen};
        }
    
        /**
         * Find max sub array, also include sub array with non-positive sum.
         * <p>For array that only contains non-positive elements, will choose first smallest element.
         * <p>For empty input array, will choose empty sub array start from 0.
         *
         * @param arr input array,
         * @return array of length 3, with elements as: {maxSum, startIdx, len};
         * <p>tips: should use 'len' when loop the returned max sub array, so that it could also work for empty sub array,
         */
        public static int[] findIncludeNonPositive(int[] arr) {
            if (arr.length == 0) return new int[]{0, 0, 0}; // empty array, no sub array,
    
            int maxSum = arr[0];    // max sum, found so far,
            int maxStart = 0;    // start of max sum,
            int maxLen = 1;    // length of max subarray,
    
            int sum = arr[0];  // current sum,
            int start = 0;    // current start,
    
            for (int i = 1; i < arr.length; i++) {
                if (sum <= 0) { // should restart,
                    start = i;
                    sum = arr[i];
                } else sum += arr[i];
    
                if (sum > maxSum) { // get a larger sum,
                    maxSum = sum;
                    maxStart = start;
                    maxLen = i - start + 1;
                }
            }
    
            return new int[]{maxSum, maxStart, maxLen};
        }
    }
    
    import org.testng.Assert;
    import org.testng.annotations.Test;
    
    import java.util.Arrays;
    
    public class MaxSubSumTest {
        @Test
        public void test_find() {
            Assert.assertTrue(Arrays.equals(MaxSubSum.find(new int[]{-2, -3, 4, -1, -2, 1, 5, -3}), new int[]{7, 2, 5})); // max sub array: {4, -1, -2, 1, 5}
            Assert.assertTrue(Arrays.equals(MaxSubSum.find(new int[]{12, 14, 0, -4, 61, -39}), new int[]{83, 0, 5})); // max sub array: {12, 14, 0, -4, 61}
    
            // corner
            Assert.assertTrue(Arrays.equals(MaxSubSum.find(new int[]{}), new int[]{0, 0, 0})); // empty array,
    
            Assert.assertTrue(Arrays.equals(MaxSubSum.find(new int[]{-5, -4, 0, 0, -7, 0, -2}), new int[]{0, 0, 0})); // array with all elements <= 0,
            Assert.assertTrue(Arrays.equals(MaxSubSum.find(new int[]{-5, -4, -2, -7, -2, -9}), new int[]{0, 0, 0})); // array with all elements < 0,
        }
    
        @Test
        public void test_findIncludeNonPositive() {
            Assert.assertTrue(Arrays.equals(MaxSubSum.findIncludeNonPositive(new int[]{-2, -3, 4, -1, -2, 1, 5, -3}), new int[]{7, 2, 5})); // max sub array: {4, -1, -2, 1, 5}
            Assert.assertTrue(Arrays.equals(MaxSubSum.findIncludeNonPositive(new int[]{12, 14, 0, -4, 61, -39}), new int[]{83, 0, 5})); // max sub array: {12, 14, 0, -4, 61}
    
            // corner
            Assert.assertTrue(Arrays.equals(MaxSubSum.findIncludeNonPositive(new int[]{}), new int[]{0, 0, 0})); // empty array,
    
            Assert.assertTrue(Arrays.equals(MaxSubSum.findIncludeNonPositive(new int[]{-5, -4, 0, 0, -7, 0, -2}), new int[]{0, 2, 1})); // array with all elements <= 0,
            Assert.assertTrue(Arrays.equals(MaxSubSum.findIncludeNonPositive(new int[]{-5, -4, -2, -7, -2, -9}), new int[]{-2, 2, 1})); // array with all elements < 0,
        }
    }
    
    int maxSubArraySum(int a[], int size) 
    { 
        int max_so_far = a[0]; 
        int curr_max = a[0]; 
    
        for (int i = 1; i < size; i++) 
        { 
             curr_max = max(a[i], curr_max+a[i]); 
             max_so_far = max(max_so_far, curr_max; 
        }  
        return max_so_far; 
     }