Kadane的算法找到具有最大总和的子阵列

时间:2012-03-27 13:12:38

标签: c# .net algorithm kadanes-algorithm

我有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]

对于我的生活,我找不到错误的位置。也许它是算法设计的缺陷?

提前致谢。

6 个答案:

答案 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 Belitskianswer和评论:

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