在一次采访中,我的一位朋友被要求找到一个最大数量的数组的子阵列,这是我对问题的解决方案,我如何改进解决方案使其更加优化,我应该考虑以递归方式进行?
def get_max_sum_subset(x):
max_subset_sum = 0
max_subset_i = 0
max_subset_j = 0
for i in range(0,len(x)+1):
for j in range(i+1,len(x)+1):
current_sum = sum(x[i:j])
if current_sum > max_subset_sum:
max_subset_sum = current_sum
max_subset_i = i
max_subset_j = j
return max_subset_sum,max_subset_i,max_subset_j
答案 0 :(得分:24)
您的解决方案是O(n ^ 2)。最佳解决方案是线性的。它的工作原理是您从左到右扫描数组,记下最佳总和和当前总和:
def get_max_sum_subset(x):
bestSoFar = 0
bestNow = 0
bestStartIndexSoFar = -1
bestStopIndexSoFar = -1
bestStartIndexNow = -1
for i in xrange(len(x)):
value = bestNow + x[i]
if value > 0:
if bestNow == 0:
bestStartIndexNow = i
bestNow = value
else:
bestNow = 0
if bestNow > bestSoFar:
bestSoFar = bestNow
bestStopIndexSoFar = i
bestStartIndexSoFar = bestStartIndexNow
return bestSoFar, bestStartIndexSoFar, bestStopIndexSoFar
Programming Pearls: Algorithm Design Techniques(强烈推荐)中也讨论了这个问题。在那里你也可以找到一个递归解决方案,它不是最优的(O(n log n)),但优于O(n ^ 2)。
答案 1 :(得分:18)
这是一个众所周知的问题,它显示重叠的最佳子结构,这表明了动态编程(DP)解决方案。虽然DP解决方案通常非常棘手(至少我认为如此!),但这个解决方案是一个很好的例子,可以介绍整个概念。
首先要注意的是,在位置j结束的最大子阵列(必须是给定数组A的连续部分)或者由在位置j-1结束的最大子阵列加上A [j]组成,或者是空(这仅在A [j] <0)时出现。换句话说,我们要问的是元素A [j]是否对当前在位置j-1结束的最大和有贡献。如果是,请将其包含在最大子阵列中;如果没有,不要。因此,通过解决重叠的较小子问题,我们可以建立一个最优的解决方案。
然后可以通过以下关系递归地给出在位置j结束的最大子阵列的总和:
sum[0] = max(0, A[0])
sum[j] = max(0, sum[j-1] + A[j])
我们可以通过从左到右扫描A,以自下而上的方式建立这些答案。我们考虑A [j]时更新sum [j]。我们也可以通过这个过程跟踪最大子阵列的总体最大值和位置。这是我在Ruby中写的一个快速解决方案:
def max_subarray(a)
sum = [0]
max, head, tail = sum[0], -1, -1
cur_head = 0
(0...a.size).each do |j|
# base case included below since sum[-1] = sum[0]
sum[j] = [0, sum[j-1] + a[j]].max
cur_head = j if sum[j-1] == 0
if sum[j] > max
max, head, tail = sum[j], cur_head, j
end
end
return max, head, tail
end
如果您想亲自测试一下,请查看我的gist。
这显然是线性O(N)算法,因为只需要通过列表一次。希望这有帮助!
答案 2 :(得分:5)
通过考虑最大和子数组必须包含的条件,可以得出更好的解决方法:任何一端的第一项(未包括)(如果有的话)必须为负数,最后一项必须为包含的结尾必须是非负的。除了原始数据中发生这些更改之外,您不需要考虑子数组的任何其他端点。
答案 3 :(得分:4)
让n
- 元素数,a(i)
- 数组f(i)
- 在位置i
结束的子数组的最大总和(最小长度为1)。然后:
f(0) = a(i);
f(i) = max(f(i-1), 0) + a(i); //f(i-1) when we continue subarray, or 0 - when start at i position
max(0, f(1), f(2), ... , f(n-1))
- 答案
答案 4 :(得分:4)
麻省理工学院有一段简短的视频可以帮助您理解这个动态编程问题。 http://people.csail.mit.edu/bdean/6.046/dp/ 点击“问题”部分下的第一个链接,您将看到它。
答案 5 :(得分:2)
这是来自http://en.wikipedia.org/wiki/Maximum_subarray_problem
的简单O(N)算法int maxsofar=0;
int maxendinghere=0;
for i=[0 n] {
maxendinghere=max(maxendinghere+x[i],0);
maxsofar=max(maxsofar,maxendinghere);
}
答案 6 :(得分:1)
除非我遗漏一些重要的东西,否则如果它们是正整数,则子集将包括整个数组,如果它们是整数,则它将仅包括正整数。那里有另一个限制吗?
答案 7 :(得分:0)
Java解决方案:
对于具有所有底片的数组不起作用。
public static int[] maxsubarray(int[] array) {
//empty array check
if (array.length == 0){
return new int[]{};
}
int max = 0;
int maxsofar = 0;
//indices
int maxsofarstart = 0;
int maxsofarend = 0;
int maxstartindex = 0;
for (int i = 0; i < array.length; i++) {
if (array[i] > 0) {
if (max == 0) {
maxstartindex = i;
}
max = max + array[i];
if (max > maxsofar) {
maxsofar = max;
maxsofarstart = maxstartindex;
maxsofarend = i;
}
} else {
max = 0;
}
}
return Arrays.copyOfRange(array, maxsofarstart, maxsofarend + 1);
}
答案 8 :(得分:0)
这是解释最充分,经过测试的工作解决方案之一 - http://rerun.me/blog/2012/08/30/maximum-continuous-subarray-problem-kandanes-algorithm/
package me.rerun;
public class Kadane {
public static void main(String[] args) {
int[] intArr={3, -1, -1, -1, -1, -1, 2, 0, 0, 0 };
//int[] intArr = {-1, 3, -5, 4, 6, -1, 2, -7, 13, -3};
//int[] intArr={-6,-2,-3,-4,-1,-5,-5};
findMaxSubArray(intArr);
}
public static void findMaxSubArray(int[] inputArray){
int maxStartIndex=0;
int maxEndIndex=0;
int maxSum = Integer.MIN_VALUE;
int cumulativeSum= 0;
int maxStartIndexUntilNow=0;
for (int currentIndex = 0; currentIndex < inputArray.length; currentIndex++) {
int eachArrayItem = inputArray[currentIndex];
cumulativeSum+=eachArrayItem;
if(cumulativeSum>maxSum){
maxSum = cumulativeSum;
maxStartIndex=maxStartIndexUntilNow;
maxEndIndex = currentIndex;
}
else if (cumulativeSum<0){
maxStartIndexUntilNow=currentIndex+1;
cumulativeSum=0;
}
}
System.out.println("Max sum : "+maxSum);
System.out.println("Max start index : "+maxStartIndex);
System.out.println("Max end index : "+maxEndIndex);
}
}
答案 9 :(得分:0)
这是正确的Java代码,它将处理包括所有负数的方案。
public static long[] leftToISumMaximize(int N, long[] D) {
long[] result = new long[N];
result[0] = D[0];
long currMax = D[0];
for (int i = 1; i < N; i++) {
currMax = Math.max(D[i], currMax + D[i]);
result[i] = Math.max(result[i - 1], currMax);
}
return result;
}
答案 10 :(得分:0)
对于在O(N)中运行的最大子阵列问题,存在一种简单的动态编程算法:Kadane's algorithm。对基本算法的轻微修改将允许您处理所有负数:是否应返回最大数字或0。
答案 11 :(得分:0)
我已经为一个更普遍的问题做了一个函数:
功能基于Kadane算法,并在O(n)时间内运行。基本上就是这样:
function MaxSumSubarray(a, n, start out, len out)
-- a - Array
-- n - Length of the array
-- start - On output starting position of largest subarray
-- len - On output length of largest subarray
-- Returns sum of the largest subarray
begin
start = 0
len = 1
int sum = a[0]
curStart = 0
curLen = 1
curSum = a[0]
for i = 2 to n
begin
if a[i] >= curSum + a[i] then
begin
curStart = i
curLen = 1
curSum = a[i]
end
else
begin
curLen = curLen + 1
curSum = curSum + a[i]
end
if (curSum > sum) OR
(curSum = sum AND curLen < len) OR
(curSum = sum AND curLen = len AND curStart < start) then
begin
start = curStart
len = curLen
sum = curSum
end
end
return sum
end
我已在C#中上传了整个解决方案,其中包含分析和示例:Maximum Sum Subarray
答案 12 :(得分:0)
不确定,但接受的解决方案并不适用于所有场景(可能是我误解了) 所以我做了一些小修改,而不是 if(值> 0) 我改变了哟 if(value&gt; bestNow)
.....(I wrote it in Scala)
它适用于所有场景
def findMaxSubArray(list: List[Int]): (Int, Int, Int) = {
var (bestNow,bestSoFar) = (0, 0)
var ( startIndexNow, startIndexSoFar, endIndex) = (-1, -1, -1)
for (i <- 0 until list.length) {
var value = bestNow + list(i)
if (value > bestNow) {
if (bestNow == 0)
startIndexNow = i
bestNow = value
} else
bestNow = 0
if (bestNow > bestSoFar) {
bestSoFar = bestNow
startIndexSoFar = startIndexNow
endIndex = i
}
}
return (bestSoFar, startIndexSoFar, endIndex)
}
def main(args: Array[String]) {
println(findMaxSubArray(List(3, -1, 5, 3, -6, -9, 6, 1)).toString)
println(findMaxSubArray(List(3, -1, 5, 3, -6, -9, 6, 3)).toString)
println(findMaxSubArray(List(20, -1, 5, 3, -6, -9, 6)).toString)
}
Output.....
(max =8, start=2, end=3)
(max=9, start=6, end=7)
(max=20, start=0, end= 0)