【问题标题】:Min Average Two Slice Codility最小平均两片可塑性
【发布时间】:2014-02-07 18:42:35
【问题描述】:

给出了一个由 N 个整数组成的非空零索引数组 A。一对整数 (P, Q),使得 0 ≤ P 例如,数组 A 满足:

A[0] = 4
A[1] = 2
A[2] = 2
A[3] = 5
A[4] = 1
A[5] = 5
A[6] = 8

包含以下示例切片:

  • 切片(1, 2),其平均值为(2 + 2) / 2 = 2;
  • 切片(3, 4),其平均值为(5 + 1) / 2 = 3;
  • 切片 (1, 4),其平均值为 (2 + 2 + 5 + 1) / 4 = 2.5。

目标是找到平均值最小的切片的起始位置。

写一个函数:

class Solution { public int solution(int[] A); }

给定一个由 N 个整数组成的非空零索引数组 A,返回具有最小平均值的切片的起始位置。如果有多个切片具有最小平均值,则应返回此类切片的最小起始位置。
例如,给定数组 A 使得:

A[0] = 4
A[1] = 2
A[2] = 2
A[3] = 5
A[4] = 1
A[5] = 5
A[6] = 8

该函数应返回 1,如上所述。

假设:

  • N 是 [2..100,000] 范围内的整数;
  • 数组 A 的每个元素都是 [−10,000..10,000] 范围内的整数。

复杂性:

  • 预计最坏情况时间复杂度为 O(N);
  • 预计最坏情况下的空间复杂度为 O(N),超出输入存储(不包括输入参数所需的存储)。

输入数组的元素可以修改。


这是我最好的解决方案,但在时间复杂度方面显然不是最优的。
有什么想法吗?

public int solution(int[] A) {
    int result = 0;
    int N = A.length;
    int [] prefix = new int [N+1];
    for (int i = 1; i < prefix.length; i++) {
        prefix[i] = prefix[i-1] + A[i-1];
    }
    double avg = Double.MAX_VALUE;
    for (int i = 1; i < N; i++) {
        for (int j = i+1; j <=N; j++) {
            double temp = (double)(prefix[j]-prefix[i-1]) /(double)(j-i+1);
            if (temp < avg) {
                avg = temp;
                result = i-1;
            }
        }
    }
    return result;
}

https://codility.com/demo/results/demo65RNV5-T36/

【问题讨论】:

  • 我认为您缺少切片 (0,1),因为您从 i = 1j=i+1 开始计数。 j 应该给你 IndexOutOfBounds 异常。
  • 为什么所有解决方案都检查 2 或 3 个元素的切片?有人可以向我解释一下吗?我怎样才能得出这个结论?谢谢
  • 如果任何一个值为负数怎么办?表示元素 A[k],其中 0
  • @SimpleApp 为什么所有解决方案都检查 2 或 3 个元素的切片?...我尝试在我的回答中解释这一点。希望这会有所帮助。
  • @user3534759,解决方案适用于负数和 0。如果我们以您的示例为例,最小平均值为 A[3] + A[4] => 0.5。任何其他组合都比这更大。

标签: java


【解决方案1】:

基于 Kadane 算法的 C++ 100% 得分

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;
}

我对 2/3 元素子切片解决方案不满意(来吧!谁会在采访期间想到这个?),也不满意解释(或缺乏解释),所以我继续寻找其他方法。然后我发现了 Kadane's algorithm 的最大子数组问题 (MSP),这导致我找到了不同的解决方案。

基本问题是(类似于 MSP 的问题):包含第 i 个元素的切片的最小平均值是多少?

为了回答这个问题,我们将在第 i 个元素中查找 end 的切片,仅更新其左侧索引。也就是说,我们必须检查切片A[lft_idx : i]

假设我们知道切片A[lft_idx : i - 1]lft_idx 具有最小平均值,那么我们有两种可能性:

  1. A[lft_idx : i] 的平均值很小。
  2. A[i - 1 : i] 的平均值是最小的(最短的可能切片有 2 个元素)。

在情况 1 中发生的情况是我们继续增长从 lft_idx 开始的切片。

然而,在情况 2 中,我们发现增加前一个切片实际上会增加平均值。因此,我们重新开始并将切片的开头(lft_idx)“重置”为前一个元素(i - 1)。现在我们有了一个新的 best 大小为 2 的切片,从这一点开始增长。

最后,我们想要 global 最小平均值,所以我们需要跟踪到目前为止的最小值以及它开始的位置(问题只要求这样做,但我们也可以保存正确的索引)。

注意:我在这里使用前缀和来计算切片平均值,因为这是问题出现在 Codility 中的地方,但它可以很容易地被一个具有前一个切片大小的变量和另一个乘法。

【讨论】:

  • 很棒的解决方案 - 我在 go 中实现了它并且也得到了 100%。我认为它被称为“Kadane 算法”。
  • 一个很好的方法,而不是猜测 需要 2、3 切片长度。谢谢指点。
  • 我认为这种方法是错误的。该解决方案有效,但我认为具有以i-1 结尾的最小平均值的切片并不能帮助您计算以i 结尾的切片的最小平均值。例如。在序列 1, 1, 1, 1, 1, 1, 1, 0, 0, 1000 - 当计算以数字 1000 结束的切片的最小平均值时。先前位置的切片平均为 0。但切片之间的最小平均值结束于1000 不是 500,也不是 1000/3。现在是 1007/10。
  • 谢谢,非常感谢。我认为 2,3 切片长度的解决方案没有任何问题,但从问题在“前缀和”下的分类中可以清楚地看出作者打算采用不同的解决方案。理解次优解决方案也很有价值,尤其是在面试的背景下,这样您就可以讨论为什么一种解决方案比另一种更好。
【解决方案2】:

我几天前发过这个:

看看这个:

http://codesays.com/2014/solution-to-min-avg-two-slice-by-codility/

在那里,他们非常详细地解释了为什么他们的解决方案有效。一世 我自己还没有实现,但我一定会尝试的。

希望对你有帮助!

但我刚刚看到它被版主删除了。他们说链接已失效,但我刚刚尝试过,它工作正常。我再次发布它,希望它可以验证链接是好的。

现在我还可以根据我之前提供的 codesays 链接提供我的实现:https://codility.com/demo/results/demoERJ4NR-ETT/

class Solution {
    public int solution(int[] A) {
        int minAvgIdx=0;
        double minAvgVal=(A[0]+A[1])/2; //At least two elements in A.
        double currAvg;
        for(int i=0; i<A.length-2; i++){
            /**
             * We check first the two-element slice
             */
            currAvg = ((double)(A[i] + A[i+1]))/2;
            if(currAvg < minAvgVal){
                minAvgVal = currAvg;
                minAvgIdx = i;
            }
            /**
             * We check the three-element slice
             */
            currAvg = ((double)(A[i] + A[i+1] + A[i+2]))/3;
            if(currAvg < minAvgVal){
                minAvgVal = currAvg;
                minAvgIdx = i;
            }
        }
        /**
         * Now we have to check the remaining two elements of the array
         * Inside the for we checked ALL the three-element slices (the last one
         * began at A.length-3) and all but one two-element slice (the missing
         * one begins at A.length-2).
         */
        currAvg = ((double)(A[A.length-2] + A[A.length-1]))/2;
        if(currAvg < minAvgVal){
            minAvgVal = currAvg;
            minAvgIdx = A.length-2;
        }
        return minAvgIdx;
    }
}

【讨论】:

  • @cotupha 只是想知道......这个问题没有提到最大切片长度,但这个解决方案最多只能覆盖 3 个切片。对于所有可能的切片长度,这个解决方案肯定不完整吗?
  • 如果任何一个值为负数怎么办?表示元素 A[k],其中 0
  • @Sheng 嗨,我读过你关于这个问题的博客,所以这基本上归结为数学证明最小切片必须有 2 或 3 个元素?如果是这样,这是一个数学问题,而不是一个算法问题,并且 codility 确实需要提高他们的问题质量。但仍然是分享您的解决方案的好博客。
  • @cotupha 谢谢。快速说明 - 您的解决方案中不需要第 32 行。
  • @user3534759,解决方案适用于负数和 0。如果我们以您的示例为例,最小平均值为 A[3] + A[4] = 0.5。任何其他组合都比这更大。
【解决方案3】:

棘手的部分是甚至在开始编码之前就确定肯定存在长度为 2 或 3 的平均最小切片。 从那里开始会更容易,但我有一些重要的注意事项:

  1. 您根本不需要除法,而是可以乘法,这样您就可以在长度为 6 的切片上获得相同的平均值,并完全避免浮点运算

  2. 循环中不需要除法(或者在我的情况下是乘法),最后一次就足够了。

  3. 如果你必须这样做,你应该总是像这样比较两个浮点数: EPSILON = 0.0000001(取决于您寻找的精度,这可能是一个不同的数字)如果 Math.abs(averageTwo - averageThree)

这是我在 Java 中的解决方案,它在 Codility 上得到了 100%:

public int solution(int[] A) {
    if (A.length == 2) return 0;

    int minSliceTwo = A[0] + A[1];
    int minTwoIndex = 0;

    int minSliceThree = Integer.MAX_VALUE;
    int minThreeIndex = 0;

    for (int i = 2; i < A.length; i++) {
        int sliceTwo = A[i - 1] + A[i];
        if (sliceTwo < minSliceTwo) {
            minSliceTwo = sliceTwo;
            minTwoIndex = i - 1;
        }

        int sliceThree = sliceTwo + A[i - 2];
        if (sliceThree < minSliceThree) {
            minSliceThree = sliceThree;
            minThreeIndex = i - 2;
        }
    }
    int averageMinTwo = minSliceTwo*3;
    int averageMinThree = minSliceThree*2;

    if (averageMinTwo == averageMinThree) return Math.min(minTwoIndex, minThreeIndex);
    else return averageMinTwo < averageMinThree ? minTwoIndex : minThreeIndex;
}

【讨论】:

  • 您能解释一下为什么检查 2 位和 3 位的最小平均值就足够了吗?
  • 如果任何值为负数意味着它位于左侧 a[k] 米处 A[0] = 10 A[1] = 0 A[2] = 8 A[3] = 2 A[ 4] = -1 A[5] = 12 A[6] = 11 A[7] = 3
  • @MykolaPavlov 因为“所有具有最小平均值的较长切片都是由这些 2 元素和/或 3 元素小切片构成的。”
  • @MykolaPavlov:这是因为为了获得原始两个元素切片的值,您需要添加比原始切片具有“切片值”的“东西”。但是,如果添加了多个元素,那就是另一个切片,并且仅此切片的值小于通过将其元素添加到原始切片创建的切片的值
  • @AlbertBikeev 考虑大小为 4 的切片。您始终可以将其分解为大小为 2 的两个切片。可能会发生两种情况之一。首先,两个切片的平均值相同,在这种情况下,您可以只使用第一个平均值。其次,平均值不同,在这种情况下,您的原始切片不能是最小平均切片。因此,大小为 4 的切片要么与更短的切片重复,要么不是最小切片。你不能用大小为 3 的切片来做这个参数。
【解决方案4】:

这是一个数学问题...要解决它,您必须了解切片平均值之间存在的关系。

从问题描述中我们知道切片的最小长度为 2。这个问题的诀窍是最小平均切片也不能长于 3。所以我们只需要计算长度切片的 avg 2 和 3。

要了解为什么最小平均切片不能超过 3,请考虑 长于3的情况...

ex. [-10, 3, 4, -20]

avg(0,3) = -23 / 4 = -5.75 // entire array is -5.75 average
avg(0,1) = -7 / 2 = -3.5 // subslice (0,1)
avg(2,3) = -16 / 2 = -8 // subslice (2,3)

注意(avg(0,1) + avg(2,3)) / 2 = avg(0,3) 因此,如果avg(0,1) != avg(2,3) 则其中一个必须小于另一个。

无论我们以哪种方式拆分此数组,如果切片不完全相同,则其中一个切片的平均值必须低于整个切片。玩弄它,你会发现它是真的。那里有数学证明。

【讨论】:

  • 您所说的数学证明在哪里。请提供参考。
  • @unlockme 这很简单,假设您将长度为 $n$ 的数组拆分为 $n_1$ 和 $n_2 = n - n_1$。那么 $avg(0, n ) = \frac{n_1}{n} * avg(0, n_1) + \frac{n_2}{n} * avg(n_2, n) > \frac{n_1 + n_2}{ n} * \min(avg(0, n_1), avg(n_2, n)) = \min(avg(0, n_1), avg(n_2, n)) $。因此 $avg(0, n ) > \min(avg(0, n_1), avg(n_2, n))$
【解决方案5】:
   static public int solution(int[] A) {
    // write your code in Java SE 8

    float avg = 0f;
    int min_index = 0;
    int P = 0;
    //formula

    float sums[] = new float[A.length ];

    //suffix sums
    int prefix = 0;
    for (int i = 0; i < A.length; i += 1) {
        prefix += A[i];
        sums[i] += prefix;
    }
    float min_avg = Float.MAX_VALUE;
    for (int i = 1; i < A.length; i++) {
        avg = (sums[i] - sums[P] + A[P]) / (i - P + 1);
        if (avg < min_avg) {
            min_avg = avg;
            min_index = P;
        }

这个想法很简单,但不是那么简单,A[P] + A[P + 1] + ... + A[Q]) / (Q − P + 1) 就是那里的公式,先计算前缀和。

公式:min_avg = (prefix[i] - prefix[P] + A[P]) / (i - P + 1)'

        if (A[i] < min_avg) {
            P = i;
        }
    }


    return min_index;


}

【讨论】:

  • 与此处发布的 2/3 切片解决方案相比,此解决方案非常好。前提是,一旦我们计算出平均值,唯一会降低该平均值的是,如果我们遇到一个低于我们迄今为止计算的最小平均值的数字。前缀集计算用于有效地计算两个段之间的总和,而无需遍历该段中的每个元素。
  • 如果数组是[-1,0,-4] 并且i 在索引2 处,循环检查不会只检查整个数组的平均值,即-1 + 0 + -4 = -1.666667 与@987654329 @ 通过它自己? if 语句的最后一部分说如果-4 小于我们刚刚发现的-1.666667,则-4 的索引(即2)是新的P。这将如何工作?我们还没有机会检查 (1,2) 的切片。我认为更合适的问题是,如果当前指数i 拥有迄今为止发现的最小平均值而不是i-1,为什么它会成为新的P?我们还没有在这里检查该切片(在我的人为示例中)。
  • 我想我明白了,如果我错了,请纠正我。如果从Pi(包括)的平均总和是降低全球平均水平的总和,那么我们知道i 在某种程度上更小,并且是新的P 作为起点的更好候选者。非常聪明!
  • 这个解决方案是迄今为止最好的。这也是真正使用前缀和的解决方案。我很确定 Codility 练习正在寻找这种类型的答案。
【解决方案6】:

100% 得分。 Javascript。

var min_pos = 0;
var min = Number.MAX_VALUE;

function solution(A) {
  for (var a = 0; a < A.length - 2; a++) {
    process((A[a] + A[a + 1]) / 2.0, a);
    process((A[a] + A[a + 1] + A[a + 2]) / 3.0, a);
  }

  process((A[A.length - 2] + A[A.length - 1]) / 2.0, A.length - 2);
  return min_pos;
}

function process(val, a) {
  if (val < min) {
    min_pos = a;
    min = val;
  }
}

【讨论】:

  • 确实达到了 100%。我不明白的是,他们说像切片这样的问题并不局限于 2 或 3 个整数长。它说它们至少可以是 2 长,这意味着它们可以是 3、4、5 等长......我在这里遗漏了一些更精细的数学点吗?
【解决方案7】:

为什么检查长度为 2 和 3 的切片就足够了?

这确实是这个问题的归结,并且在 cmets 中被问过很多次。

(Mathjax 是 not supported here,因此您必须自己从代码格式的文本中解析数学。)

A_nn 数字的平均值,B_m 是以下m 数字的平均值。 很容易检查这些 m + n 数字组合的平均值是

(n * A_n + m * B_m) / (n + m)

假设通过扩展A_n 的切片以包含B_m 的数字,我们找到了长度为m + n 的切片,其平均值更小。 换句话说,我们假设以下不等式成立

(n * A_n + m * B_m) / (n + m) &lt; A_n.

我们可以解决B_m 的这个不等式。两边都乘以m + n,每边减去n * A_n。然后除以m后得出结论:

B_m &lt; A_n.

所以这意味着B_m 是三个切片中最小的平均值。 我们可以不断重复这个论点。

由于n &gt;= 2m &gt;= 2 仍然可以以这种方式写为其他两个之和的最小切片,因此长度必须 >= 4。 或者换句话说,一旦最后一个(因此是最小的)B_m 的长度

【讨论】:

    【解决方案8】:

    我认为检查切片长度

    向切片添加更多元素(例如长度为 4 的切片)将为我们提供 2 对子切片,其平均值将始终小于或等于长度为 4 的切片的平均值。同样适用于任何切片大小超过 3。因此,我们只需要检查长度为 2 和 3 的切片。我们不需要超过长度为 3 的 Slice。

    举个例子吧。考虑以下数组

    -30,-1,-31,-1,-31,-1,-1, 1, 2, 3,4,45

    选择任何长度超过 3 的切片。计算平均值,比如说 切片 (0, 4) 的平均值为 -18.8,但它有一个长度为 3 的子切片 (2,4),其平均值为 -21.0。顺便说一句,这也是给定数组中的最小平均值。

    尝试所有其他组合(长度超过 3)并围绕它进行游戏。你会明白的。

    不使用前缀和的解决方案(100% 得分)

        public int solution(int[] A)    {
        float tempAverage,finalAverage = Float.MAX_VALUE;
        int startLocation = 0;
        int lengthOfTheSlice = 0;
        for(int i=0; i < A.length -1; ++i)  {
            tempAverage = (float)(A[i] + A[i+1])/2;
            if(tempAverage < finalAverage)    {
                finalAverage = tempAverage;
                startLocation = i;
                lengthOfTheSlice =2;
            }
        }
        for(int i=0; i < A.length -2; ++i)  {
            tempAverage = (float)(A[i] + A[i+1]+ A[i+2])/3;
            if(tempAverage < finalAverage)    {
                finalAverage = tempAverage;
                startLocation = i;
                lengthOfTheSlice =3;
            }
        }
        System.out.print("Length of the slice \t"+lengthOfTheSlice);
        return startLocation;
    }
    

    【讨论】:

    • 您能解释一下为什么要停在第三个元素上吗?为什么是 3 而不是 4 或 5?
    • 顺便说一句,在您的 -30,-1,-1,-1,-30, ... 示例中,最小值为 0 -> 1,最小平均值为 -15.5(低于 -12.6)
    • @AlbertBikeev 我已经更新了答案和示例。希望这会有所帮助。
    【解决方案9】:

    我了解http://www.rationalplanet.com/php-related/minavgtwoslice-demo-task-at-codility-com.html 提供了迄今为止我所知道的最佳解释和解决方案。 由于这是在前缀和部分下,以下是我尝试熟悉的前缀和方法。

    function solution($A) {
            // write your code in PHP5.3
    
            $N = count($A);
            if ($N > 100000 || $N < 2 ) {
                return -1;
            } elseif ($N === 2) {
                return 0;
            }
    
            $presum = array();
            $presum[] = 0;
    
            $mavg = PHP_INT_MAX;
    
            $index = 0;
            for ($i = 0; $i < $N; $i++) {                
                $presum[$i+1] = $A[$i] + $presum[$i];
            }
    
            for ($i = 0; $i < $N-2; $i++) {
                for ($j = $i+1; $j < $i + 3; $j++ ) {
                    $avg = ($presum[$j+1] - $presum[$i]) / ($j - $i + 1);
    
                    if ($mavg > $avg) {
                        $mavg = $avg;
                        $index = $i;
                    }
                }
            }
            $avg = ($presum[$N] - $presum[$N-2]) / 2;
            if ($mavg > $avg) {
                $index = $N - 2;
            }
    
            return $index;
    }
    

    【讨论】:

      【解决方案10】:
       public int solution(int[] A)
              {
      //C# solution thats getting 100%. Once you find min avg with always within 2   //or 3 elements of moving index its much simpler sol
                  int minIndex = 0;
                  double minAvgVal = Double.MaxValue;
      
                  for (int i = 0; i < A.Length-2; i++)
                  {
                      double twoDigitMin = (A[i] + A[i + 1])/2.0;
                      if (minAvgVal > twoDigitMin)
                      {
                          minAvgVal = twoDigitMin;
                          minIndex = i;
                      }
      
                      double threDigitMin = (A[i] + A[i + 1] + A[i+2]) / 3.0;
                      if (minAvgVal > threDigitMin)
                      {
                          minAvgVal = threDigitMin;
                          minIndex = i;
                      }
                  }
      
                  double last2Avg = (A[A.Length - 2] + A[A.Length - 1])/2.0;
                  if (minAvgVal > last2Avg)
                  {
                      minIndex = A.Length - 2;
                  }
      
                  return minIndex;
              }
      

      【讨论】:

        【解决方案11】:

        这是另一个使用前缀 sum 的 Java 解决方案 100/100:

        public int solution(int[] A) {
                int len = A.length, result = len - 1, sum = 0;
                int[] prefixSums = new int[len + 1];
        
                for (int i = 1; i <= len; ++i) {
                    prefixSums[i] = prefixSums[i-1] + A[i-1];
                }
        
                double min = Double.MAX_VALUE, average = 0d;
        
                for (int P = 0, Q = 1; Q + 1 < prefixSums.length; ++P, ++Q ) {
                    sum = prefixSums[Q + 1] - prefixSums[P];
                    average = (sum)/(double) 2;
        
                    if (average < min) {
                        min = average;
                        result = P;
                    }
        
                    if ( Q + 2 < prefixSums.length ) {
                        sum = prefixSums[Q + 2] - prefixSums[P];
                        average = (sum)/(double) 3;
        
                        if (average < min) {
                            min = average;
                            result = P;
                        }
                    }
        
                }
        
                return result;
            }
        

        这里是代码链接:https://codility.com/demo/results/demo4S4VJX-WMJ/

        【讨论】:

        • 如果数组也有负值怎么办......就像你在下面看到的那样 A[0] = 10 A[1] = 0 A[2] = 8 A[3] = 2 A[ 4] = -1 A[5] = 12 A[6] = 11 A[7] = 3
        • 问题的定义说数组不包含负值:..."一对整数 (P, Q),使得 0 ≤ P
        • "数组 A 的每个元素都是 [−10,000..10,000] 范围内的整数。" P 和 Q 是指标。
        【解决方案12】:

        基于 Kanade 算法的 C++,带有前缀和的:https://app.codility.com/demo/results/training66V2HC-V6S/

        int solution(std::vector<int> &A)
        {
            int minIndex = 0;
            auto minAvgVal = std::numeric_limits<int>::max();
        
            int sumOfTwo = 0;
            int twoDigitSum = 0;
            int threeDigitSum = 0;
        
            size_t size = A.size();
            for (size_t i = 0; i < (size - 2); ++i) {
                sumOfTwo = A[i] + A[i + 1];
                twoDigitSum = 3 * sumOfTwo;
                if (minAvgVal > twoDigitSum) {
                    minAvgVal = twoDigitSum;
                    minIndex = i;
                }
        
                threeDigitSum = (sumOfTwo + A[i + 2]) << 1;
                if (minAvgVal > threeDigitSum) {
                    minAvgVal = threeDigitSum;
                    minIndex = i;
                }
            }
        
            sumOfTwo = A[size - 2] + A[size - 1];
            twoDigitSum = 3 * sumOfTwo;
            if (minAvgVal > twoDigitSum) {
                minIndex = (size - 2);
            }
        
            return minIndex;
        }
        

        如您所见,sumOfTwo 是使用前缀和的示例。

        【讨论】:

          【解决方案13】:

          关键是要注意这个问题类似于找到给定数组的长度为 2 和 3 的所有切片的最小平均值。

          100% Java 解决方案:

              public int solution(int[] A) {
              int N = A.length;
              double minAvg = Integer.MAX_VALUE;
              double sum = 0;
              int result = 0;
              for (int i = 0; i < N - 1; ++i) {
                  sum = A[i];
                  for (int j = i + 1; j < i + 3 && j < N; ++j) {
                      sum += A[j];
                      double avg = (sum / (j-i+1));
                      if (avg < minAvg) {
                          minAvg = avg;
                          result = i;
                      }
                  }
              }
              return result;
          }
          

          【讨论】:

            【解决方案14】:

            这是一个有效的前缀和实现(100% 在 Codility 中):

            import sys
            
            def solution(A):
            
                n             = len(A)
                pre_sum       = [0] * (n+1)
                min_slice_avg = sys.maxint
                min_slice_idx = 0
            
                for i in xrange(1,n+1):
                    pre_sum[i] = pre_sum[i-1] + A[i-1]
            
                    # calculate at least 2 prefix sums
                    if i-2 < 0: continue
            
                    # check prev 3 slices if we have calculated 3 prefix sums
                    if i>=3:
                        prev_3_slice_avg = (pre_sum[i] - pre_sum[i-3]) / 3.0
            
                        if prev_3_slice_avg < min_slice_avg:
                            min_slice_avg = prev_3_slice_avg
                            min_slice_idx = i-3
            
                    # check prev 2 slices
                    prev_2_slice_avg = (pre_sum[i] - pre_sum[i-2]) / 2.0
            
                    if prev_2_slice_avg <  min_slice_avg:
                        min_slice_avg = prev_2_slice_avg
                        min_slice_idx = i-2
            
                return min_slice_idx
            

            【讨论】:

              【解决方案15】:

              100 % 正确性和性能(java)

              void sumArray(int[] A, int[] sum) {
                  for (int i = 1; i < sum.length; i++) {
                      sum[i] = sum[i - 1] + A[i - 1];
                  }
              }
              
              int getTotalSum(int[] sum, int start, int last) {
                  return sum[last + 1] - sum[start];
              }
              
              double minav = Double.MAX_VALUE;
              int minind = Integer.MAX_VALUE;
              
              public int solution(int[] A) {
                  int[] sum = new int[A.length + 1];
                  sumArray(A, sum);
                  int startpos = 0;
                  for (int i = 1; i <= 2; i++) {
                      startpos = 0;
                      while (startpos + i < A.length) {
                          double suma = getTotalSum(sum, startpos, startpos + i);
                          double size = (startpos + i) - startpos + 1;
                          double av = suma / size;
                          if (av <= minav) {
              
                              if (av < minav || startpos < minind) {
                                  minind = startpos;
                              }
                              minav = av;
              
              
                          }
                          startpos += 1;
                      }
                  }
                  return minind;
              }
              

              【讨论】:

                【解决方案16】:

                100% Scala 解决方案

                  def solution(a: Array[Int]): Int = {
                    val n = a.length
                    var min = Double.MaxValue
                    var minPos = 0
                    for (i <- 0 to n - 2) {
                        val sum2: Double = a(i) + a(i+1)
                        var avg = sum2 / 2
                        if (i < n - 2) {
                            avg = Math.min(avg, (sum2 + a(i+2)) / 3)
                        }
                        if (avg < min) {
                            min = avg
                            minPos = i
                        }
                    }
                    minPos
                  }
                

                【讨论】:

                  【解决方案17】:

                  获得 100% 分数的更具可读性的 Python 解决方案:

                  预先计算前缀和:

                  def prefix_sum(A):
                      n = len(A)
                      P = [0] * (n + 1)
                      for k in range(1, n + 1):
                          P[k] = P[k - 1] + A[k - 1]
                      return P
                  

                  这需要像之前提出的那样采用 2 和 3 个元素切片,并存储当前的最小平均值和起始索引

                  def solution(A):
                      pref = prefix_sum(A)
                      min_mean = 10000
                      min_idx_from = 0
                      for i in range(len(A)):
                          for j in range(i+1,min(i+3,len(A))):
                              mean = (pref[j+1] - pref[i]) / (j-i+1)
                              if mean < min_mean:
                                  min_mean = mean
                                  min_idx_from = i
                      return min_idx_from
                  

                  测量时间:

                  import random
                  random.seed(123)
                  n = 1000
                  A = [random.randint(-10000, 10000) for _ in range(n)]
                  %timeit solution(A)
                  # 1.01 ms ± 3.89 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
                  

                  Codility 链接:

                  https://app.codility.com/demo/results/trainingAK963Y-3NR/

                  【讨论】:

                    【解决方案18】:
                    int solution(int A[], int N) {
                        // write your code in C99 (gcc 6.2.0)
                        int i,j,k;
                        int min=A[0];
                        int index=0;
                        float min_ave=10000;// take the maximum value present in an array
                        float temp,ave,req;
                        
                        for(i=0;i<N;i++)
                        {
                            if(min>A[i])
                            min=A[i];
                        }
                        
                        for(i=0;i<N-1;i++)
                        {
                            ave=(float)(A[i]+A[i+1])/2;
                            
                            if(min_ave>ave) // for positive integers minimum slice start point is nothing but start point of two elements with minimum average
                            {
                                min_ave=ave;
                                index=i;
                            }
                            
                            if(A[i]<0)// for negative integers we calculate up to the maximum numbers till there is possibility
                            {
                                k=1;// number of elements up to current average
                                temp=A[i];// sum of number of elements up to current average
                                for(j=i+1;j<N;j++)
                                {
                                    k++; 
                                    req=(A[i]*k)-temp;// 'req' required value of element so that average can be less than current minimum average
                                    temp+=A[j];
                                    if((temp/k)<min_ave) // average of k elements
                                    {
                                        min_ave=temp/k;
                                        index=i;
                                    }
                                    if(req<min) // A[j] cannot be less than minimum so break
                                    break;
                                }
                            }
                            if(min_ave==min)
                            break;
                        }
                        return index;
                    }
                    

                    【讨论】:

                      【解决方案19】:

                      我对 python 解决方案的看法,没有前缀和。我只对数组进行一次传递

                      def solution(A):
                          min_avg = 10**5
                          index = -1
                          for i in range(len(A)-1):
                              # slice of size 2
                              elem_avg = (A[i] + A[i+1])/2
                              if elem_avg < min_avg:
                                  min_avg = elem_avg
                                  index = i
                                  
                              # Escape checking slice of size 3 if only 2 elements are left
                              if i == len(A)-2: continue
                              
                              # slice of size 3
                              elem_avg_3 = (A[i] + A[i+1]+ A[i+2])/3
                              if elem_avg_3 < min_avg:
                                  min_avg = elem_avg_3
                                  index = i
                                  
                          return index
                      

                      【讨论】:

                        【解决方案20】:
                        【解决方案21】:

                        我的答案是 100 分

                        public class MinAvgTwoSlice {
                        
                        public static void main(String[] args) {
                        
                            System.out.println(new MinAvgTwoSlice().solution(new int[] {4, 2, 2, 5, 1, 5, 8} ));    
                        }
                        
                        public int solution(int[] A) {
                        
                            double minAvg = 100000;
                            int index=0;
                        
                            if(A.length<=2) {
                        
                                return 0;
                            }
                        
                            for(int i=0;i<A.length-2;i++) {
                        
                                if((A[i]+A[i+1])/2.0<minAvg) {
                                    minAvg=(A[i]+A[i+1])/2.0;
                                    index=i;
                                }
                        
                                if((A[i]+A[i+1]+A[i+2])/3.0<minAvg)  {
                        
                                    minAvg=(A[i]+A[i+1]+A[i+2])/3.0;
                                    index=i;
                                }
                            }
                        
                            int aMax = A.length-2;
                        
                            if((A[aMax]+A[aMax+1])/2.0<minAvg) {
                        
                                minAvg=(A[aMax]+A[aMax+1])/2.0;
                                index=aMax;
                            }
                        
                            return index;
                        }
                        }
                        

                        谢谢:基于 codesays.com 中提供的逻辑

                        【讨论】:

                        • 我总是得到错误:错误答案(预期为 1 9)有什么想法吗?
                        【解决方案22】:

                        这是我用 C 编写的解决方案(得分 100%)

                        #include <string.h>
                        
                        int solution(int A[], int N) {
                            // write your code in C99
                        
                            int *p, i;
                            float minAvg, tmpAvg;
                            int index=0;
                        
                            p=malloc(sizeof(int)*(N+1));
                            memset(p, 0, sizeof(int)*(N+1));
                        
                            if(N == 2) {
                                return 0;
                            }
                        
                            *p=0;
                        
                            //Building prefixes vector
                            for(i=0;i<N;i++) {
                                *(p+i+1)=*(p+i)+A[i];
                            }
                        
                            minAvg=*(p+N)/(float)(N);
                        
                            for(i=0; i<N-1; i++) {
                                tmpAvg=(*(p+i+2)-*(p+i))/(float)(2);
                                if (tmpAvg < minAvg) {
                                    minAvg=tmpAvg;
                                    index=i;
                                }
                            }
                        
                            for(i=0; i<N-2; i++) {
                                tmpAvg=(*(p+i+3)-*(p+i))/(float)(3);
                                if (tmpAvg < minAvg) {
                                    minAvg=tmpAvg;
                                    index=i;
                                }
                            }
                        
                            free(p);
                            return index;
                        }
                        

                        【讨论】:

                          【解决方案23】:

                          我在 C# 中获得了 100% 的成绩。试试这个https://codility.com/demo/results/demoV25DUE-9A8

                              public static int solution(int[] A)
                              {
                                  float min_avg = (A[0] + A[1]) / 2;
                                  int minpos = 0;
                          
                                  for (int i = 0; i < A.Length-2; i++)
                                  {
                                      float firsttwo = (float)(A[i] + A[i+1])/2;
                          
                                      if (firsttwo < min_avg)
                                      {
                                          min_avg = firsttwo;
                                          minpos = i;
                                      }
                          
                                      float three = (float)(A[i] + A[i+1] + A[i+2])/3;
                                      if (three < min_avg)
                                      {
                                          min_avg = three;
                                          minpos = i;
                                      }
                          
                                      float lasttwo = (float)(A[i + 1] + A[i + 2]) / 2;
                                      if (lasttwo < min_avg)
                                      {
                                          min_avg = lasttwo;
                                          minpos = i+1;
                                      }
                                  }
                          
                                  return minpos;
                              }
                          

                          【讨论】:

                            【解决方案24】:

                            这是一个 Go 实现:

                            func Solution(A []int) int {
                                if len(A) < 2 {
                                    return -1
                                }
                            
                                result := 0
                                minAvg := float64(A[0]+A[1]) / 2
                                var curAvg float64
                                for index := 0; index < len(A)-2; index++ {
                                    curAvg = float64(A[index]+A[index+1]) / 2
                                    if curAvg < minAvg {
                                        minAvg = curAvg
                                        result = index
                                    }
                            
                                    curAvg = float64(A[index]+A[index+1]+A[index+2]) / 3
                                    if curAvg < minAvg {
                                        minAvg = curAvg
                                        result = index
                                    }
                                }
                            
                                curAvg = float64(A[len(A)-2]+A[len(A)-1]) / 2
                                if curAvg < minAvg {
                                    minAvg = curAvg
                                    result = len(A) - 2
                                }
                            
                                return result
                            }
                            

                            【讨论】:

                              【解决方案25】:
                              private static int solution(int[] a) {
                                  // TODO Auto-generated method stub
                              
                                  int sum=0;
                                  int absSum=0;
                                  int minAbsSlice=10000;
                              
                                  for(int i=0;i<a.length;i++){
                                      sum=a[i];
                                      for(int j=1;j<a.length;j++){
                                          if(j>=i){
                                              absSum=sum+a[j];
                                              if(absSum<sum){
                                                  sum=absSum;
                                                  absSum=Math.abs(absSum);
                              
                                              }else{
                              
                                                  absSum=Math.abs(sum);
                                                  sum=absSum;
                                              }
                                          }
                                      }
                                      sum=0;
                                      if(minAbsSlice<absSum){
                                          minAbsSlice=minAbsSlice;
                                      }else{
                                          minAbsSlice=absSum;
                                      }
                              
                                  }
                                  return minAbsSlice;
                              }
                              

                              【讨论】:

                              • 适用于超过四个数组元素
                              【解决方案26】:

                              这是我在 C++ 中的解决方案,基于证明必须存在长度为 2 或 3 的最小切片,请参阅 https://codesays.com/2014/solution-to-min-avg-two-slice-by-codility/ 的证明

                              在 Codility 的正确性和速度方面获得 100% 的分数。不需要分配任何内存来解决这个问题,所以算法的空间是O(1),不包括输入向量。

                              int solution(vector<int> &A) {
                                  int N             = A.size();
                                  int minSliceStart = 0;
                              
                                  if (N > 2) {
                                      // Min Slice must be of length 2 or 3.
                                      // If Min Slice is longer, 
                                      //    it must be divisible into smaller parts of equal average
                                      int min_2_slice_start = 0;
                                      int min_3_slice_start = 0;
                                      int min_2_slice_sum   = A[0]+A[1];
                                      int min_3_slice_sum   = A[0]+A[1]+A[2];
                              
                                      for(int i=0; i<(N-1); i++) {
                                          int cur_2_slice_sum = A[i]+A[i+1];
                              
                                          // check if the current 2-slice sum is smaller
                                          if (cur_2_slice_sum < min_2_slice_sum) {
                                              min_2_slice_sum   = cur_2_slice_sum;
                                              min_2_slice_start = i;
                                          }
                              
                                          // check if the current 3-slice sum is smaller
                                          if (i<(N-2)) {
                                              int cur_3_slice_sum = A[i]+A[i+1]+A[i+2];
                                              if (cur_3_slice_sum < min_3_slice_sum) {
                                                  min_3_slice_sum   = cur_3_slice_sum;
                                                  min_3_slice_start = i;
                                              }
                                          }
                                      }
                              
                                      #ifdef Want_Debug_Statements
                                          cout << "2-Slice: start=" << min_2_slice_start << ", sum=" << min_2_slice_sum <<endl;
                                          cout << "3-Slice: start=" << min_3_slice_start << ", sum=" << min_3_slice_sum <<endl;
                                      #endif
                              
                                      // If 2-slice or 3-slice are equal, take the first one.
                                      // Note: rather than computing averages by dividing,
                                      // multiple each side by the other denominator instead & compare.
                                      if (min_2_slice_sum*3 == min_3_slice_sum*2) {
                                          if (min_2_slice_start < min_3_slice_start)
                                              minSliceStart = min_2_slice_start;
                                          else
                                              minSliceStart = min_3_slice_start;
                                      } else {
                                          if (min_2_slice_sum*3 < min_3_slice_sum*2)
                                              minSliceStart = min_2_slice_start;
                                          else
                                              minSliceStart = min_3_slice_start;
                                      }
                                  }
                              
                                  return minSliceStart;
                              }
                              

                              【讨论】:

                                【解决方案27】:

                                100% 在 python 中:

                                def solution(A):
                                 if(len(A) < 2):
                                     return 0;
                                 MinAvg=float(A[0]+A[1])/float(2);
                                 Index=0;
                                 for nn in range(1,len(A)-1):
                                     Avg=float(A[nn]+A[nn+1])/float(2);
                                     if(Avg<MinAvg):
                                         MinAvg=Avg;
                                         Index=nn;     
                                 for nn in range(0,len(A)-2):
                                     Avg=float(A[nn]+A[nn+1]+A[nn+2])/float(3);
                                     if(Avg<MinAvg):
                                         MinAvg=Avg;
                                         Index=nn;                    
                                 return Index;     
                                pass 
                                

                                解释:

                                对 2 个连续元素进行平均和比较几乎可以得到最高分。因为当一个元素添加一个元素时,如果平均值较小,那么接下来的 2 个元素将具有较小的平均值。

                                之后仅保留具有 3 个元素的例外,例如 [-1, 2, 4, -1, 2, -1]。

                                【讨论】:

                                • 请补充说明
                                • 基于大写,这不是很pythonic。但是你有一个很好的观点,即 2 元组将永远是赢家,因为 3 元组包含 2 个 2 元组和一个重叠的成员。
                                【解决方案28】:

                                这是我在 Java 中的实现。我得到了 100%。 该算法是相同的(2 个和 3 个连续值的总和),除了我没有为前缀总和使用额外的内存。 我们不需要一次所有的和,因此我们只能保持当前的和,然后在向右移动时从和中减去最左边的元素。

                                public int solution(int vect[]) {
                                
                                    double minAvg = (vect[0] + vect[1]) / 2.;
                                    int minIndex = 0;
                                
                                    double tempAvg = minAvg;
                                    int tempSum = vect[0] + vect[1];
                                    int tempIndex = 0;
                                    int tempLength = 2;
                                
                                    double newAvg;
                                    int newSum, newIndex;
                                
                                    for(int j=2; j<vect.length; ++j) {
                                        ++tempLength;
                                        tempSum += vect[j];
                                        tempAvg = (double)tempSum / tempLength;
                                
                                
                                        newSum = tempSum - vect[tempIndex];
                                        newIndex = tempIndex+1;
                                        newAvg = newSum/(j-newIndex+1.);
                                
                                        while(newAvg < tempAvg && newIndex < j) {
                                            tempIndex = newIndex;
                                            tempSum = newSum;
                                            tempAvg = newAvg;
                                            --tempLength;
                                
                                            newSum = tempSum - vect[tempIndex];
                                            newIndex = tempIndex+1;
                                            newAvg = newSum/(j-newIndex+1.);
                                        }
                                
                                        if (tempAvg < minAvg) {
                                            minIndex = tempIndex;
                                            minAvg = tempAvg;
                                        }
                                        else if(tempAvg > minAvg && j-tempIndex>1) {
                                            tempIndex = j;
                                            tempLength = 1;
                                            tempSum = vect[j];
                                        }
                                    }
                                    return minIndex;
                                }
                                

                                【讨论】:

                                • 复杂度是O(2*N),还是O(N)。
                                【解决方案29】:

                                C++ 解决方案,对我来说很好用

                                int solution(vector<int> &A) {
                                    // write your code in C++14 (g++ 6.2.0)
                                    if(A.size() < 2) return 0;
                                    int min_index = 0;
                                    double min = (A[0] + A[1]) / 2;
                                
                                    for(unsigned int i = 2; i < A.size(); i++){
                                
                                        double temp_three = (A[i - 2] + A[ i - 1] + A[i]) / 3.0;
                                        double temp_two = (A[ i - 1] + A[i]) / 2.0;
                                
                                        if(temp_three < min){
                                          min = temp_three;  
                                          min_index = i - 2;
                                        } 
                                        if(temp_two < min){
                                            min = temp_two;
                                            min_index = i - 1;
                                        }
                                    }
                                
                                    return min_index;
                                }
                                

                                【讨论】:

                                  【解决方案30】:

                                  在函数式编程语言FP(如 Scala 或 Haskell)中,前缀 Sums 通过Scan 高阶函数提供。

                                  在我们的例子中,加法 HOF 函数 _ + _ 等价于 lambda 函数 (x, y) =&gt; x + y

                                  https://en.wikipedia.org/wiki/Prefix_sum

                                  FP 提供了一种更简单的算法来表达我觉得更容易理解的算法。

                                  即使有recursion 和一些解释,该解决方案的得分也达到了 100%。

                                  https://app.codility.com/demo/results/trainingBZJU4M-NXE/

                                    def solution(a: Array[Int]): Int = {
                                  
                                      val n: Int = a.length
                                  
                                      /**
                                        * Let's initialize the prefixSums Array
                                        */
                                      val prefixSums: Array[Int] = a.scan(0)(_ + _)
                                  
                                      /**
                                        * Let's look for a Minimum that has:
                                        * a position and an average
                                        * (i.e. Product Type Data Structure)
                                        */
                                      case class Minimum(position: Int, average: Float)
                                  
                                      /**
                                        * Based on a Slice: Position [P] and [Q]
                                        * Q here is represented by its size [P - Q] (2 or 3)
                                        * CAPTURE only if there is a change in the minimum average
                                        */
                                      def average(p: Int, q: Int, min: Minimum): Minimum = {
                                        val average: Float = (prefixSums(p + q) - prefixSums(p)) / q.toFloat
                                        if (average < min.average) min.copy(position = p, average = average) else min
                                      }
                                  
                                      /**
                                        * Let's start with the first position
                                        * and a max average value
                                        */
                                      val init = Minimum(position = 0, average = Float.MaxValue)
                                  
                                      /**
                                        * Let's fold through the A Array indices minus one
                                        * (paying attention to the slice size)
                                        *
                                        * while capturing the minimum average.
                                        * If Q size reaches 3 (instead of regular slice tuple:
                                        *    capture the last minimum average
                                        *    otherwise return the current minimum average
                                        * give the minimal position
                                        */
                                      (0 until n - 1).toList
                                        .foldLeft(init) { (min, p) =>
                                          val currentMin = average(p, 2, min)
                                          if (p < n - 2) average(p, 3, currentMin) else currentMin
                                        }.position
                                  
                                    }
                                  
                                  

                                  【讨论】:

                                    猜你喜欢
                                    • 2014-04-03
                                    • 1970-01-01
                                    • 2017-08-19
                                    • 1970-01-01
                                    • 2022-11-16
                                    • 1970-01-01
                                    • 1970-01-01
                                    • 2011-01-26
                                    • 2013-12-24
                                    相关资源
                                    最近更新 更多