优化算法并归结为最后一部分。我有一个这样的整数数组:
[1,1,2,5,0,5,3,1,1]
我的要求如下:
预期结果:
如果提到的数组输入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和其他花哨的数学特征都是可以的,只要它是最快的方式。
答案 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);
}
}