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