我有Kadane's algorithm的以下实现来解决数组最大子数组的问题:
public static decimal FindBestSubsequence
(this IEnumerable<decimal> source, out int startIndex, out int endIndex)
{
decimal result = decimal.MinValue;
decimal sum = 0;
int tempStart = 0;
List<decimal> tempList = new List<decimal>(source);
startIndex = 0;
endIndex = 0;
for (int index = 0; index < tempList.Count; index++)
{
sum += tempList[index];
if ((sum > result) ||
(sum == result && (endIndex - startIndex) < (index - tempStart)))
{
result = sum;
startIndex = tempStart;
endIndex = index;
}
else if (sum < 0)
{
sum = 0;
tempStart = index + 1;
}
}
return result;
}
当我使用以-1, 2, 3
这样的负数开头的序列时,它会失败,结果为4, [0,2]
而不是5, [1,2]
。
对于我的生活,我找不到错误的位置。也许它是算法设计的缺陷?
提前致谢。
答案 0 :(得分:6)
您的初始实施遭受主扫描周期内不必要的复杂和部分错误检查。这些检查有两个:
sum
,则将其作为临时结果存储; sum
为负数,请将其重置为0
并准备从下一个扫描位置构建新序列。重构的FindBestSubsequence
方法实现如下:
public static decimal FindBestSubsequence (this IEnumerable<decimal> source, out int startIndex, out int endIndex)
{
decimal result = decimal.MinValue;
decimal sum = 0;
int tempStart = 0;
List<decimal> tempList = new List<decimal>(source);
startIndex = 0;
endIndex = 0;
for (int index = 0; index < tempList.Count; index++)
{
sum += tempList[index];
if (sum > result)
{
result = sum;
startIndex = tempStart;
endIndex = index;
}
if (sum < 0)
{
sum = 0;
tempStart = index + 1;
}
}
return result;
}
现在不仅-1,2,3
上面的代码产生了正确的答案5,[1,2]
,而且它还正确处理了所有负数的数组而没有任何额外的代码:输入-10,-2,-3
将返回-2,[1,1]
}。
答案 1 :(得分:3)
在您的示例中,即使sum > result
在循环的第一次迭代中sum<0
因为0 > decimal.MinValue
,也总是sum > 0
。
所以你永远不会去你的第二个案例.-
如果添加条件if ((sum >0 ) & ((sum > result) ||
(sum == result && (endIndex - startIndex) < (index - tempStart))))
{
...
}
else if (sum < 0)
{
...
}
,则需要更改第一个:
decimal result = 0;
<强>更新强>
正如我在评论中所解释的那样,您只需将结果初始化更改为0:
{{1}}
来自维基百科:
此子阵列为空(在这种情况下,其总和为零)或者包含比在前一位置结束的最大子阵列多一个元素
因此,如果数组只包含负数,则解是一个空子数组,其总和为0。
答案 2 :(得分:1)
更改此行:
decimal result = decimal.MinValue;
到
decimal result = 0;
答案 3 :(得分:0)
对于每个位置,您应该使用其中的最大值(来自原始序列)和您编写的总和。如果原始数字较大,那么最好从“开始”开始求和,即sum = max(sum+tempList[index],tempList[index]);
那么你不需要求和的情况&lt;总共0。
答案 4 :(得分:0)
最后,这是我纠正算法以处理所有场景的方式,以防它对某人有所帮助:
public static decimal FindBestSubsequence (this IEnumerable<decimal> source, out int startIndex, out int endIndex)
{
decimal result = decimal.MinValue;
decimal sum = 0;
int tempStart = 0;
List<decimal> tempList = new List<decimal>(source);
if (tempList.TrueForAll(v => v <= 0))
{
result = tempList.Max();
startIndex = endIndex = tempList.IndexOf(result);
}
else
{
startIndex = 0;
endIndex = 0;
for (int index = 0; index < tempList.Count; index++)
{
sum += tempList[index];
if (sum > 0 && sum > result || (sum == result && (endIndex - startIndex) < (index - tempStart)))
{
result = sum;
startIndex = tempStart;
endIndex = index;
}
else if (sum < 0)
{
sum = 0;
tempStart = index + 1;
}
}
}
return result;
}
答案 5 :(得分:0)
基于Gene Belitski的answer和评论:
public static void Main()
{
var seq = new[] { -10M, -2M, -3M };
var stuff = seq.FindBestSubsequence();
Console.WriteLine(stuff.Item1 + " " + stuff.Item2 + " " + stuff.Item3);
Console.ReadLine();
}
public static Tuple<decimal, long, long> FindBestSubsequence(this IEnumerable<decimal> source)
{
var result = new Tuple<decimal, long, long>(decimal.MinValue, -1L, -1L);
if (source == null)
{
return result;
}
var sum = 0M;
var tempStart = 0L;
var index = 0L;
foreach (var item in source)
{
sum += item;
if (sum > result.Item1)
{
result = new Tuple<decimal, long, long>(sum, tempStart, index);
}
if (sum < 0)
{
sum = 0;
tempStart = index + 1;
}
index++;
}
return result;
}