使用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();