使用C#/Linq对序列的子序列进行累加

使用C#/Linq对序列的子序列进行累加,c#,linq,sequence,C#,Linq,Sequence,我正试图根据以下要求找到更好的处理数字序列的方法: sequence[i]的值是其自身值加上从sequence[0]到sequence[i-1]的累加之和 例如: 如果序列是一个列表 List<double> list = new List<double> { 10.0, 20.0, 30.0, 40.0 }; 我知道蛮力方法使用多次迭代,但我想一定有更好的解决方案(可能是使用LINQ)。使用相对未知的Select重载,让您可以查看索引: var result = li

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

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

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

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

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

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

假设您可以访问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;
});
使用System.Linq;
名单编号=新名单{10,20,30,40};
列表运行总计=新列表(number.Count);
数字。聚合(0,(总和,值)=>{
总和+=数值;
运行总计。添加(总和);
回报金额;
});

非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;
}
List GetSums(列表值)
{
列表总和=新列表();
双倍合计=0;
foreach(值为双d)
{
总数+=d;
加上(总数);
}
回报金额;
}

我的版本,它是我在注释中添加的内容的修改,并返回一个IEnumerable而不是列表,但ToList()将对此进行排序

应该很有效率。还有谁不喜欢使用
收益率
?;-)

public IEnumerable GetCumulativeSequence(IEnumerable输入)
{
var runningTotal=0.0;
foreach(输入端双电流)
{
runningTotal+=电流;
收益率-收益率-总收益率;
}
}
void Main()
{
列表=新列表{10.0,20.0,30.0,40.0};
var foo=GetCumulativeSequence(列表);
}

它的主要优点是只在输入数组上执行一个循环。如果你没有实际使用所有返回的内容(即你只看前三个),那么它就不会计算其余的内容。在较长的列表等中可能有用。Chris Doggett的答案也是如此,但并非所有使用linq的答案都是如此。

这里有另一种类似于其中一篇文章的方法,但这是一种独立的方法:

// 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
/C#
可枚举范围(1100)
.Aggregate(新列表{0}),
(acc,x)=>{acc.Add(acc.Last()+x);返回acc;})
.Dump();//使用LINQPad进行转储;答复:5050
如果我们切换到int64,并使用10000000作为上限,并读取最终累积结果,代码将在大约450毫秒后在Corei5上执行,最终输出为:

50000005000000

顺便说一句,对于1000000的上限,执行时间约为40毫秒,因此将大小增加10会将时间增加10。

我基于APL扫描操作符构建了一个通用(有很多变化)扩展方法(类似于
聚合
本质上是APL reduce操作符)返回操作序列的中间结果:

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

我不确定我是否理解你的暴力方式<代码>新建列表[0]=列表[0];对于(i=1到n){newList[i]=List[i]+newList[i-1];}
对我来说似乎是显而易见的暴力方法,并且不使用多次迭代……但这不会使用多次迭代吗?它在选择列表上迭代,然后在求和时再次迭代…这将比Joe的答案更好。我会这样做,甚至更好,使用所谓的暴力技术!是的。这是linq答案中唯一一个只在列表中迭代一次的答案,我认为。。。我不喜欢linq声明在外面更新运行总数,因为它们往往没有副作用。我不知道这是一个最佳实践/好的代码,还是个人喜好……我自己也不是副作用的粉丝。也许最好让它做一个收益率回报,因为这个总和从来没有在其他地方使用过。看到了吗?我知道人们都喜欢我
// 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
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();