使用C#/ Linq累积序列的子序列

时间:2012-02-01 17:09:56

标签: c# linq sequence

我正在尝试根据以下要求找到更好的处理数字序列的方法: sequence[i]的值是其自身值加上sequence[0]sequence[i-1]的累积之和。

例如: 如果序列是一个列表

List<double> list = new List<double> { 10.0, 20.0, 30.0, 40.0 };

输出结果应为

list[0] = 10.0
list[1] = 20.0 + 10.0
list[2] = 30.0 + 10.0 + 20.0
list[3] = 40.0 + 10.0 + 20.0 + 30.0

我知道使用多次迭代的蛮力方式,但我想知道必须有一些更好的解决方案(可能使用LINQ)。

6 个答案:

答案 0 :(得分:6)

假设您有权访问LINQ:

using System.Linq;

List<int> numbers = new List<int> {10, 20, 30, 40};
List<int> runningTotals = new List<int>(numbers.Count);

numbers.Aggregate(0, (sum, value) => {
    sum += value; 
    runningTotals.Add(sum); 
    return sum;
});

答案 1 :(得分:5)

我的版本是对注释中的内容进行修改并返回IEnumerable而不是List但ToList()将对其进行排序。

应该非常有效率。谁不喜欢使用yield return? ; - )

public IEnumerable<double> GetCumulativeSequence (IEnumerable<double> input)
{
    var runningTotal = 0.0;
    foreach (double current in input)
    {
        runningTotal+=current;
        yield return runningTotal;
    }
}

void Main()
{
    List<double> list = new List<double> { 10.0, 20.0, 30.0, 40.0 };
    var foo = GetCumulativeSequence(list);
}

这样做的主要优点是它只对输入数组进行一次循环。如果你实际上没有使用所有返回的东西(即你只看前三个)那么它将不会计算其余的东西。在更长的名单等方面可能有用。对于像Chris Doggett这样的答案,可能会有同样的说法,但不是所有那些使用linq的人。

答案 2 :(得分:4)

使用相对未知的Select重载,可以看到索引:

var result = list.Select((val, index) => list.Take(index + 1).Sum())

答案 3 :(得分:2)

非LINQ版本,只是为了与众不同:

List<double> GetSums(List<double> values)
{
   List<double> sums = new List<double>();
   double total = 0;
   foreach(double d in values)
   {
      total += d;
      sums.Add(total);
   }
   return sums;
}

答案 4 :(得分:2)

这是另一种类似于其中一个帖子的方法,但这个帖子是自包含的:

// C#
Enumerable.Range(1, 100)
.Aggregate(new List<int>{0},
  (acc, x) => { acc.Add(acc.Last() + x); return acc; })
.Dump(); // Dump using LINQPad; ans: 5050

如果我们切换到int64,并使用10,000,000作为上限,并读取最终累积结果,则代码在Corei5上执行大约450毫秒,最终输出为:

500000.05亿

顺便提一下,对于1,000,000的上限,执行时间约为40毫秒,因此将尺寸增加10会使时间增加10倍。

答案 5 :(得分:1)

我构建了一个基于APL扫描运算符的扩展方法(具有很多变体)的扩展方法(类似于Aggregate本质上是APL reduce运算符的方式),它返回操作序列的中间结果:< / p>

public static IEnumerable<TResult> Scan<T, TResult>(this IEnumerable<T> src, TResult seed, Func<TResult, T, TResult> combine) {
    foreach (var s in src) {
        seed = combine(seed, s);
        yield return seed;
    }
}

然后可以使用:

var ans = list.Scan(0.0, (a, d) => a+d).ToList();