【问题标题】:Codility - min average sliceCodility - 最小平均切片
【发布时间】:2014-04-03 23:32:38
【问题描述】:

我正在尝试找到a codility question on minimum slice of a subarray 的解决方案,并且我已经使用 Kadane 算法的修改版本设计了一个解决方案。我目前已经获得了 90/100 并设法通过了 O(n) 中的几乎所有测试。但是,我似乎无法通过“medium_range,增加,减少(legth = ~100)和小功能,得到 5 预期 3”,我不知道为什么。这可能是solution 的重复,但我使用的解决方法略有不同。

我的逻辑如下:

a) 如果我们有一个数组 MinA,其中 MinA[k] 表示从 k 开始且最小长度为 2 的子数组的最小平均切片

b) 那么如果我们遍历 MinA 并找到数组的最小值,那么这将是整个数组的最小平均切片(然后返回索引位置)

c) 要创建这个 MinA,我们从数组的倒数第二个元素开始,MinA[A.length -2] 是 A 的最后两个元素的平均值

d) 我们将计数器向左移动一位; MinA[counter] 必须是 A[counter] 和 A[counter + 1] 的平均值,或者是元素 counter 和 MinA[counter + 1] 中元素的平均值

e) 如果 d 不为真,那么这意味着 MinA[counter + 1] 不是从 counter + 1 到从 counter + 2 到 N 的某个元素的最小平均切片

我想知道我是否遗漏了什么?

/*
 * Using modified version of Kadane's algorithm
 * The key logic is that for an array A of length N, 
 * if M[k + 1] is the minimum slice of a subarray from k + 1 to any element
 * between k+2 to n, then M[k] is either the average of A[k] and A[k + 1], or
 * the average of the elements k and the elements in M[k + 1]
 */
function solution(A) {
    // you can use console.log for debugging purposes, i.e.
    // console.log('this is debug message');
    // write your code in JavaScript (ECMA-262, 5th edition)
    var minSliceArray = [],
        counter = A.length - 2,
        currMinSliceLength = 0,
        min = Number.POSITIVE_INFINITY,
        minIndex = -1;

    minSliceArray[counter] = (A[counter] + A[counter + 1]) / 2;
    currMinSliceLength = 2;
    counter--;

    while (counter >= 0) {
        var a = (A[counter] + A[counter + 1]) / 2,
            b = (A[counter] + minSliceArray[counter + 1] * currMinSliceLength) / (currMinSliceLength + 1) ;
        if (a < b) {
            minSliceArray[counter] = a;
            currMinSliceLength = 2;
        } else {
            minSliceArray[counter] = b;
            currMinSliceLength++;
        }
        counter--;
    }

    //loops through the minSliceArray and find the minimum slice
    for (var i = 0; i < minSliceArray.length; i++) {
        if (minSliceArray[i] < min) {
            min = minSliceArray[i];
            minIndex = i;
        }
    }
    return minIndex;
}

【问题讨论】:

    标签: javascript algorithm dynamic-programming kadanes-algorithm


    【解决方案1】:

    要解决您的问题,您可以替换代码

    if (a < b) {
    

    if (a <= b) {
    

    例如A = [-3, 3, -3, 3, -3],首先我们考虑的是A[3:5],平均值为0。然后,我们来到位置2,A [2:5]/3 = -1, A[2:4]/2 = 0。所以我们选择前者。对于位置 1,A[1:3]/2 == A[1:5]/4 == 0。在OLD答案中,我们应该继续选择 A[1:5]。最后对于位置 0,我们有 A[0:2]/2 = 0,并且 A[0:5]/5 = -0.6 我们选择后者。毕竟,总体最小平均值位于位置 3,即 A[3:5]/3=-1。 但是实际上是 A[0:3]/3 == -1 == A[3:5]/3。

    因为这样的陷阱,我没有在博客中使用修改版的 Kadane 算法。但它应该工作得很好。

    【讨论】:

    • 非常感谢,这很有道理!
    【解决方案2】:

    first attempt 上,我有一个 O(2NN) 算法,这很简单,但只有 40% 的正确性和 0% 的性能:

    function solution(A) {
        var m = null, c, n
        for ( var i = 0; i < A.length; ++i ) {
            for ( var j = i + 1; j <= A.length; ++j ) {
                c = A.slice(i, j + 1).reduce(function (a,b) { return a+b }) / (j - i + 1)
                if ( m === null || c < m ) {
                    m = c
                    n = i
                }
                else if ( c == m && i < n ) {
                    n = i
                }
            }
        }
        return n
    }
    

    睡了一觉,想出了这个second attempt,得到了一个 O(N) 算法,得到了 100% 的正确性和 100% 的性能:

    function solution(A) {
        if ( A.length < 2 )  return -1
        var result = A.reduce(function (a, b, bIndex) {
            var f = typeof a === 'number'
            var x, y, z
            z = {
                index: bIndex,
                value: b,
                couple: {
                    start: bIndex - 1,
                    sum:   x = (f ? a : a.value) + b,
                    count: 2,
                    avg:   x / 2
                },
                streak: {
                    start: a.bestStreak ? a.bestStreak.start : 0,
                    sum:   x = (f ? a : a.bestStreak.sum) + b,
                    count: y = (f ? 1 : a.bestStreak.count) + 1,
                    avg:   x / y
                }
            }
    
            z.bestStreak = z.couple.avg < z.streak.avg
                ? z.couple
                : z.streak
    
            z.best = !a.best || z.bestStreak.avg < a.best.avg
                ? z.bestStreak
                : a.best
    
            // console.log(JSON.stringify({z}, null, '  '))
    
            return z
        })
        return result.best.start
    }
    

    解决后,我环顾四周,看看其他人是如何做到的。恕我直言,我的上述解决方案是最容易理解和调试的。

    它的工作原理是知道没有一个连胜的平均值可以变得更低,如果该连胜本身不包含更低的连胜。

    这可能看起来很奇怪,就像您可能会喜欢的那样 - 如果我有一个平均的连续性,然后是一个超低的数字会发生什么。好吧,该连胜中的最高数字永远不是最后一个数字,因为这会增加连胜的平均值,从而打破它。所以最后一个数字,要么是一个对连胜有利的数字,在这种情况下,下一个数字也可能是有益的,并且可能形成一对更好的连胜,或者最后一个或当前的数字是打破连胜的数字,可以被丢弃。

    【讨论】:

      【解决方案3】:

      怎么样

      Javascript

      function solution(A) {
          var minpos = 0,
              minavg = (A[0] + A[1]) / 2,
              N = A.length,
              N1 = N - 1,
              N2 = N - 2,
              sumTwo,
              t,
              i;
      
          for (i = 0; i < N2; i += 1) {
              sumTwo = A[i] + A[i + 1];
              t = sumTwo / 2;
              if (minavg > t) {
                  minavg = t;
                  minpos = i;
              }
      
              t = (sumTwo + A[i + 2]) / 3;
              if (minavg > t) {
                  minavg = t;
                  minpos = i;
              }
      
          }
      
          t = (A[N2] + A[N1]) / 2;
          if (minavg > t) {
              minavg = t;
              minpos = N2;
          }
      
          return minpos;
      }
      
      var A = [4, 2, 2, 5, 1, 5, 8];
      
      console.log(solution(A));
      

      开启jsFiddle

      【讨论】:

      • 嗯,它说它是 N^2 复杂度 (codility.com/demo/results/demo6EV4HH-C36)
      • 对不起,感冒了,孩子们也是。我严重误读和过于复杂的事情。这是更好,还是我仍然误解了这个问题?对不起,如果我有,刚才真的很厚头。 :)
      【解决方案4】:

      虽然盛的修正确实有帮助,但该算法仍然不能在所有情况下都有效。 例如,算法为[-18, 65, -11, 73, -22, 90, 21, 10, 47, 87] 返回2。预期值为0

      可能的原因:

      考虑算法的中间步骤。如果 A[i..j] 具有以 i 开头的切片的最小平均值,则要包含 i-1 处的元素,则仅考虑以下选项:

      • A[i-1...j]
      • A[i-1...i]

      不幸的是,可能存在一个索引k,例如avg(A[i...k]) &gt; avg(A[i...j]),但avg(A[i-1...k]) &lt; avg(A[i-1...j])。虽然这可以用数学方法证明,但这里一个例子就足够了。

      [-18, 65, -11, 73, -22, 90, 21, 10, 47, 87] avg([65, -11, 73, -22]) = 26.25 avg([65, -11]) = 27 // This is ignored as avg is higher

      为了包含-18,算法考虑[-18, 65, -11, 73, -22][-18, 65]

      avg([-18, 65, -11, 73, -22]) = 17.4 avg([-18, 65]) = 23.5 avg([-18, 65, -11]) = 12 // True minimum which is not considered

      我提交了一个类似的解决方案,它在 Codelity 中的得分为 100%。但是,这不是正确的解决方案。

      【讨论】:

        【解决方案5】:

        在另一篇文章中,我提供了对我的solution 的详细描述。我不在这里包括它,因为你已经有了这个想法。

        int solution(vector<int> &A) {
        
            // Find prefix sum.
            int N = A.size();
            vector<int> ps(N + 1, 0);
        
            for (int i = 1; i <= N; i++) {
                ps[i] = A[i - 1] + ps[i - 1];
            }
        
            int lft_idx, min_lft_idx;
            double avg_here, min_avg, avg_of_two, avg_with_prev;
        
            // Initialize variables at the first possible slice (A[0:1]).
            lft_idx = min_lft_idx = 0;
            avg_here = min_avg = (A[0] + A[1]) / 2.0;
        
            // Find min average of every slice that ends at ith element,
            // starting at i = 2.
            for (int i = 2; i < N; i ++) {
        
                // average of A[lft_idx : i]
                avg_with_prev = ((double) ps[i + 1] - ps[lft_idx]) / 
                                (i - lft_idx + 1);
        
                // average of A[i - 1 : i]
                avg_of_two = (A[i - 1] + A[i]) / 2.0;
        
                // Find minimum and update lft_idx of slice
                // (previous lft_idx or i - 1).
                if (avg_of_two < avg_with_prev) {
                    avg_here = avg_of_two;
                    lft_idx = i - 1;
                }
                else
                    avg_here = avg_with_prev;
        
                // Keep track of minimum so far and its left index.
                if (avg_here < min_avg) {
                    min_avg = avg_here;
                    min_lft_idx = lft_idx;
                }
            }
        
            return min_lft_idx;
        }
        

        它在 Codility 上达到了 100%,并且还给出了@Manas Chaudhari 示例的正确答案。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2023-04-04
          • 2021-09-13
          • 2019-04-08
          • 1970-01-01
          • 2019-12-03
          • 2021-08-14
          • 2020-05-26
          • 2017-08-19
          相关资源
          最近更新 更多