繁体   English   中英

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

[英]Fastest way to find max sum range in int[]

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

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

我的要求如下:

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

预期成绩:

给定输入2(2想要)和所提到的数组应该返回[8,[5,6]],其中8是索引5和6处的整数之和

给定输入3(3想要)与所提到的数组应返回[9,[5,6,7]]其中9是索引5,6和7的整数之和(注意,即使索引3处的整数由于索引4为0,结果无效,4,5的总和较高

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

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

然后:

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变量!

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

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

“易读”代码被认为是“优化”吗?

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()

这是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)

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

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

想法是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);
  }
}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM