我正在尝试找到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]的平均值,或者是元素计数器的平均值和MinA中的元素[counter + 1]e)如果d不为真,那么这意味着MinA [counter + 1]不是从计数器+ 1到从计数器+ 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;
}
答案 0 :(得分:4)
要解决您的问题,您可以替换代码
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算法。但它应该运作良好。
答案 1 :(得分:0)
怎么样
的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));
上
答案 2 :(得分:0)
虽然盛的修正确实有帮助,但算法在所有情况下仍然不起作用。
例如,算法为2
返回[-18, 65, -11, 73, -22, 90, 21, 10, 47, 87]
。预期值为0
。
考虑算法的中间步骤。如果A [i..j]具有以i开头的切片的最小平均值,那么要在i-1处包含元素,则仅考虑以下选项:
不幸的是,可能存在索引k
,avg(A[i...k]) > avg(A[i...j])
,但avg(A[i-1...k]) < 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%。但是,这不是正确的解决方案。
答案 3 :(得分:0)
在first attempt上,我有一个O(2 N N)算法,这很容易,但只有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
}
解决之后,我环顾四周,看看别人是怎么做到的。恕我直言,我上面的解决方案是最容易理解和调试的。
通过知道条纹的平均值不会降低,如果条纹本身不包含较低的条纹,就可以了。
这可能看起来很奇怪,因为你可能会喜欢 - 如果我有一个平均连胜,然后是超低数字会发生什么。那条连线中最高的数字绝不是最后一个数字,因为这会增加连胜的平均值,打破它。所以最后一个数字要么是对条纹有益的数字,在这种情况下,下一个数字也可能是有益的,并且可能形成一对更好的条纹,或者最后一个或当前数字是条纹断路器并且可以被丢弃。
答案 4 :(得分:0)
在另一篇文章中,我对我的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的示例提供了正确的答案。