【问题标题】:Finding minimal absolute sum of a subarray查找子数组的最小绝对和
【发布时间】:2014-11-15 22:31:37
【问题描述】:

有一个数组A 包含(正负)整数。查找元素绝对和最小的(连续)子数组,例如:

A = [2, -4, 6, -3, 9]
|(−4) + 6 + (−3)| = 1 <- minimal absolute sum

我首先实现了一个蛮力算法,它是O(N^2)O(N^3),尽管它产生了正确的结果。但任务规定:

complexity:
- expected worst-case time complexity is O(N*log(N))
- expected worst-case space complexity is O(N)

经过一番搜索,我认为也许可以修改 Kadane 的算法以适应这个问题,但我没有做到。

我的问题是 - Kadane 的算法是正确的方法吗?如果没有,您能否指出我正确的方向(或在这里命名一个可以帮助我的算法)?我不想要现成的代码,我只需要帮助找到正确的算法。

【问题讨论】:

    标签: algorithm sum dynamic-programming absolute-value kadanes-algorithm


    【解决方案1】:

    这是python中的迭代解决方案。这是 100% 正确的。

     def solution(A):
        memo = []
        if not len(A):
            return 0
    
        for ind, val in enumerate(A):
            if ind == 0:
                memo.append([val, -1*val])
            else:
                newElem = []
                for i in memo[ind - 1]:
                    newElem.append(i+val)
                    newElem.append(i-val)
                memo.append(newElem)
        return min(abs(n) for n in memo.pop())
    

    【讨论】:

      【解决方案2】:

      Short Sweet 像魅力一样工作。 JavaScript / NodeJs 解决方案

       function solution(A, i=0, sum =0 ) {
          //Edge case if Array is empty
          if(A.length == 0) return 0;
          // Base case. For last Array element , add and substart from sum
          //  and find min of their absolute value
          if(A.length -1 === i){
              return Math.min( Math.abs(sum + A[i]), Math.abs(sum - A[i])) ;
          }
          // Absolute value by adding the elem with the sum.
          // And recusrively move to next elem
          let plus = Math.abs(solution(A, i+1, sum+A[i]));
          // Absolute value by substracting the elem from the sum
          let minus = Math.abs(solution(A, i+1, sum-A[i]));
          return Math.min(plus, minus);
      }
      
      console.log(solution([-100, 3, 2, 4]))
      

      【讨论】:

        【解决方案3】:

        您可以运行 Kadane's algorithm两次(或一次性完成)找到最小和最大和,其中找到最小值的工作方式与使用 反转符号,然后通过比较它们的绝对值来计算新的最大值。

        来源-某人(不记得是谁)在本站发表的评论。

        【讨论】:

          【解决方案4】:

          如果您计算部分和 比如

          2, 2 +(-4), 2 + (-4) + 6, 2 + (-4) + 6 + (-3)...
          

          那么任何连续子数组的和是两个部分和的差。所以要找到绝对值最小的连续子数组,我建议你对部分和进行排序,然后找到最接近的两个值,并使用这两个部分和在原始序列中的位置来找到开始和结束绝对值最小的子数组。

          这里比较昂贵的是排序,所以我认为这是及时的O(n * log(n))

          【讨论】:

          • 我没有关注您如何从部分和中识别子数组。例如数组 [-4,5,-1] 有部分和 [-4,1,0],你似乎暗示子数组应该是 [5,-1]=4,而实际的解决方案是[-4,5,-1]=0。
          • 我没有考虑整个数组,将其视为子数组。您可以单独考虑具有小部分总和的子数组,或者确保在对所有内容进行排序时包含其中包含零元素的子数组-总和为零,因此在您的示例中,您将有部分总和 [-4, 1,0,0] 并找出通过考虑两个零和相加的项结尾之间的跨度得出的解决方案 - 整个数组的开头和结尾。从两个部分和中识别的子数组是部分和中的项目集合,其中大多数项目相加,但另一个不相加。
          • 考虑 3,3,3,4,5 吗?也许我很困惑。
          • 已排序的部分总和 0,3,6,9,13,18(包括前 0 个元素的部分总和),3,6 是一对最接近的数字,因此一个答案为具有最小绝对部分和的连续子数组是[3]。据我所知,这是正确的答案,除非您允许零长度部分和,如果您允许,这将是每个输入的正确答案。
          【解决方案5】:
          def min_abs_subarray(a):
              s = [a[0]]
              for e in a[1:]:
                  s.append(s[-1] + e)
              s = sorted(s)
              min = abs(s[0])
              t = s[0]
              for x in s[1:]:
                  cur = abs(x)
                  min = cur if cur < min else min
                  cur = abs(t-x)
                  min = cur if cur < min else min
                  t = x
              return min
          

          【讨论】:

            【解决方案6】:

            这是 Saksow 算法的 C++ 实现。

            int solution(vector<int> &A) {
                vector<int> P;
                int min = 20000 ;
                int dif = 0 ;
                P.resize(A.size()+1);
                P[0] = 0;
                for(int i = 1 ; i < P.size(); i ++)
                {
                    P[i] = P[i-1]+A[i-1];
            
                }
                sort(P.begin(),P.end());
                for(int i = 1 ; i < P.size(); i++)
                {
                     dif = P[i]-P[i-1];
                     if(dif<min)
                     {
                         min = dif;
                     }
                }
                return min;
            }
            

            【讨论】:

              【解决方案7】:
              public static int solution(int[] A) {
                  int minTillHere = A[0];
                  int absMinTillHere = A[0];
                  int minSoFar = A[0];
                  int i;
                  for(i = 1; i < A.length; i++){
                      absMinTillHere = Math.min(Math.abs(A[i]),Math.abs(minTillHere + A[i]));
                      minTillHere = Math.min(A[i], minTillHere + A[i]);
                      minSoFar = Math.min(Math.abs(minSoFar), absMinTillHere);
                      }
                  return minSoFar;
              }
              

              【讨论】:

                【解决方案8】:

                这是一个基于 Kadane 算法的 C 解决方案。 希望对您有所帮助。

                #include <stdio.h>
                int min(int a, int b)
                {
                  return (a >= b)? b: a;
                }
                
                int min_slice(int A[], int N) {
                if (N==0 || N>1000000) 
                return 0;
                
                int minTillHere = A[0];
                int minSoFar = A[0];
                int i;
                for(i = 1; i < N; i++){
                    minTillHere = min(A[i], minTillHere + A[i]);
                    minSoFar = min(minSoFar, minTillHere);
                    }
                return minSoFar;
                }
                
                
                int main(){
                int A[]={3, 2, -6, 4, 0}, N = 5;
                //int A[]={3, 2, 6, 4, 0}, N = 5;
                //int A[]={-4, -8, -3, -2, -4, -10}, N = 6;
                printf("Minimum slice = %d \n", min_slice(A,N));
                return 0;
                }
                

                【讨论】:

                • 这无法解决绝对问题
                【解决方案9】:

                我在 Codility 上做这个测试,我发现 mcdowella 的答案很有帮助,但我不得不说的还不够:所以这里是 2015 年的答案,伙计们!

                我们需要构建数组 A(这里称为 P)的前缀和,例如:P[0] = 0, P[1] = P[0] + A[0], P[2] = P[1 ] + A[1], ..., P[N] = P[N-1] + A[N-1]

                A 的“最小绝对总和”将是 P 中 2 个元素之间的最小绝对差值。所以我们只需要 .sort() P 并循环遍历它,每次取 2 个连续的元素。这样我们就有 O(N + Nlog(N) + N) 等于 O(Nlog(N))。

                就是这样!

                【讨论】:

                • 我很好奇你是如何实现的。
                • 我用 Python 实现了它,但我没有代码了...您最感兴趣的部分是什么?我可以解释更多。
                • 这个数组[-5,8,-1]怎么样? P 为 [0,-5,3,2],因此 P 元素之间的最小绝对差异为 1 (2,3),但 A 的最小绝对和为 2 (-5,8,-1)。或者这个:[14,-4,5] 给出 P [0,12,10,15],所以 P 的最小差异是 2 (10,12),但 A 的最小差异是 1 (-4,5)
                • 你在第一个例子中是对的。但是对于 [14,-4,5],你错误地构建了 P,它应该是 [0,14,10,15],它给出了最小差异 1。你能找到一个完全有效的解决方案吗?
                • @Salivan 在某些情况下它不起作用,但我认为它与正确的解决方案相距不远
                【解决方案10】:

                答案是肯定的,Kadane 的算法绝对是解决你问题的方法。

                http://en.wikipedia.org/wiki/Maximum_subarray_problem

                来源 - 我与一位博士生密切合作,他的整个博士论文都致力于最大子阵列问题。

                【讨论】:

                  猜你喜欢
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 2017-03-21
                  • 2021-12-28
                  • 2013-04-24
                  • 1970-01-01
                  相关资源
                  最近更新 更多