在int []中找到最大和范围的最快方法

时间:2010-02-17 17:53:52

标签: c# algorithm

优化算法并归结为最后一部分。我有一个这样的整数数组:

  

[1,1,2,5,0,5,3,1,1]

我的要求如下:

  1. 输入:要求总和的数字
  2. 最大总和应该由彼此相邻的整数组成
  3. 如果整数的值为0,则范围中的总和将无效
  4. 返回整数的最大值和每个整数的索引
  5. 预期结果:

    如果提到的数组输入2(2想要),那么应该返回 [8,[5,6]]其中8是索引5和6的整数之和

    如上所述,如果输入3(3个想要的)数组应该返回 [9,[5,6,7]]其中9是索引5,6和7的整数之和(注意,即使索引3,4,5的整数具有更高的总和,结果也是无效的,因为索引4是0)

    我目前通过做很多循环来管理这个,但是想知道是否有人有更好的方法来实现这一点。我选择的编码语言目前是C# - 如果可能的回复将在C#中,我会很感激。任何使用linq和其他花哨的数学特征都是可以的,只要它是最快的方式。

6 个答案:

答案 0 :(得分:7)

我认为你可以在线性时间内解决这个问题。首先,用非常小的数字替换所有0(例如-2 ^ 30),这样就不会影响我们的总和。

然后:

let s[i] = sum of first i integers
let k = number of integers required
let max = -inf    
for ( int i = k; i <= N; ++i )
  if ( s[i] - s[i - k - 1] > max )
     max = s[i] - s[i - k - 1]

您可以避免在一些额外条件下替换零。如果s [i] =前i个整数之和,那么s [i] - s [k - 1]给出整数k到i之和。

修改: 您可以在O(1)额外空间中执行此操作,如下所示:首先替换所有0。

然后:

max = cr = sum of first k integers.
for ( int i = k + 1; i <= N; ++i )
{
  cr = cr + numbers[i] - numbers[i - k] 
  if ( cr > max )
    max = cr; // also update positions
}

为避免在第一个解决方案中替换零,只需在遇到零时跳过k空格。在第二个解决方案中,跳过k或k + 1(取决于你如何选择实现这种特殊情况)前面的空格,但一定要在跳过时重建你的cr变量!

答案 1 :(得分:1)

以下代码应该可以解决问题。性能将取决于要求和的范围的大小。与在每次迭代中添加子集的天真相比,元素越多,表现越好

 int getSum(int[] arr, int wanted)
        {
            var pre = new int[arr.Length];
            pre[0] = 0;
            pre[1] = arr[0];
            int max = 0;
            int skip = 1;
            for (var i = 1; i < arr.Length; i++)
            {
                skip--;
                //if the sum is marked invalid with a zero skip
                var current = arr[i];
                //calculate the index once
                int preIndex = i + 1;
                if (current == 0)
                {
                    skip = wanted;
                    pre[preIndex] = pre[i];
                    continue;
                }
                //store the sum of all elements until the current position
                pre[preIndex] = pre[i] + current;
                //find the lower end of the range under investigation now
                var lower = i - wanted;
                //if we haven't reached the wanted index yet we have no sum or if 
                //it's less than wanted element since we met a 0 
                //just go to the next element
                if (lower < 0 || skip > 0)
                    continue;
                var sum = pre[preIndex] - pre[lower];
                if (sum > max)
                    max = sum;
            }
            return max;
        }

答案 2 :(得分:1)

“易于阅读”的代码被视为“优化”吗?

int numberOfElements = 4;   //parameter
int[] numbers = new[] { 1, 1, 2, 5, 0, 5, 3, 1, 1 };    //numbers


int[] result =
     //cicle each element
    (from n in Enumerable.Range(0, numbers.Length - numberOfElements + 1)
     //keep the (n + numberOfElements) elements
     let myNumbers = from p in Enumerable.Range(n, numberOfElements)
                     select numbers[p]
     //keep the sum (if we got a 0, sum is 0)
     let sum = myNumbers.Contains(0) ? 0 : myNumbers.Sum()
     orderby sum descending     //order by sum
     select myNumbers)          //select the list
        .First().ToArray();     //first is the highest

考虑在.NET 4用完时为性能添加.AsParallel()

答案 3 :(得分:1)

这是O(n)时间和O(1)空间,并且只通过数组一次。

static public int[] FindMaxSumRange(int[] data, int n)
{
    // two pointers showing the lower and upper bound of current running sum
    int upper = 0, lower = 0;
    // best result found yet
    int max_found = 0;
    int max_position = -1;

    while (lower <= data.Length - n) {
        int running_sum = 0;
        // prefill running sum with n items
        for (upper = lower; upper - lower < n && upper < data.Length; upper++) {
            if (data[upper] == 0) {
                // found a zero, set lower pointer to the next item
                lower = upper + 1;
                break;
            }
            running_sum += data[upper];
        }
        if (upper - lower != n) {
            // found a zero, restart at new lower position
            continue;
        }
        if (running_sum > max_found) {
            max_found = running_sum;
            max_position = lower;
        }
        while (upper < data.Length) {
            if (data[upper] == 0) {
                // found a zero, set lower pointer to the next item
                lower = upper;
                break;
            }
            running_sum += data[upper] - data[lower];
            if (running_sum > max_found) {
                max_found = running_sum;
                max_position = lower;
            }
            upper++; lower++;
        }
        lower++;
    }
    return new int[]{max_found, max_position};
}

返回最大总和及其找到的位置。如果您需要获取索引列表,只需构建范围[max_position,max_position + n)

答案 4 :(得分:0)

以下是咆哮,完全没有经过考验。甚至不确定它是否会编译。我把它留给别人改进它。

using System;
using System.Linq;
public int[] max(int[] a, int amount) {
    var max = int.MinValue;
    var maxPos = 0;
    if (a.length < amount) return null;
    var c = 0;
    while (c == 0) {
        for (int i = 0; i < amount; i++) {
            if (a[i + maxPos] == 0) {
                c = 0;
                break; // try again
            }
            c += a[i];
        }
        if (c != 0) maxPos = i - amount;
    }
    if (c == 0) return null;
    max = c;
    for (int i = maxPos; i + amount < a.length; i++) {
        if(a[i] == 0) {
            i += amount - 1;
            continue;
        }
        c -= a[i];
        c += a[i + amount];
        if (c > max) {
            c = max;
            maxPos = i;
        }
    }
    if (c == 0) return null;
    var result = new int[amount + 1];
    result[0] = max;
    for (int i = 0; i < amount; i++)
        result[i + 1] = maxPos + i;
    return result;
}

答案 5 :(得分:0)

想法是 1.将数组拆分为组以测量总和 2.计算每个组的总和 3.计算出最大金额

这是代码

private Result GetMax(ICollection<int> items, int itemCount)
{
  return items.
    Take(items.Count - (itemCount - 1)).
    Select((value, index) => items.Skip(index).Take(itemCount)).
    Select((group, index) =>
      new Result
      {
        Index = index,
        Sum = group.Aggregate(0, (sum, i) => sum + (i == 0 ? int.MinValue : i))
      }).
    Max();
}

private struct Result : IComparable<Result>
{
  public int Index { get; set; }
  public int Sum { get; set; }

  public int CompareTo(Result other)
  {
    return Sum.CompareTo(other.Sum);
  }
}