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

发布于 2025-01-01 14:53:13 字数 446 浏览 2 评论 0原文

我试图根据以下要求找到一种更好的方法来处理数字序列: 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)。

I'm trying to find a better way of processing a sequence of numbers based on the following requirement:
the value of sequence[i] is the sum of its own value plus the accumulation from sequence[0] to sequence[i-1].

For example:
if the sequence is a list

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

the output result should be

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

I know the brute force way which uses multiple iterations, but I wonder there must be some better solution (maybe with LINQ).

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

滥情空心 2025-01-08 14:53:13

假设您有权访问 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;
});

Assuming you have access to 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;
});
紅太極 2025-01-08 14:53:13

我的版本是对我在注释中添加的内容的修改,并返回 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 的内容。

My version which is a modification of what I put in comments and returns an IEnumerable rather than a List but a ToList() will sort that out.

Should be pretty efficient. And who doesn't like using 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);
}

Main advantage of this is that it only does one loop over the input array. and if you don't actually use all of the stuff returned (ie you only look at the first three) then it won't calculate the rest. Potentially useful in longer lists, etc. The same will be said of things like Chris Doggett's answer but not all those here using linq.

夏末的微笑 2025-01-08 14:53:13

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

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

Use the relatively-unknown overload of Select that lets you see the index:

var result = list.Select((val, index) => list.Take(index + 1).Sum())
梦行七里 2025-01-08 14:53:13

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

A non-LINQ version, just to be different:

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;
}
甩你一脸翔 2025-01-08 14:53:13

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

// 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 作为上限,并读取最终的累积结果,则代码在大约 450 毫秒内执行最终输出为 Corei5:

50000005000000

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

Here is another way similar to one of the posts, but this one is self-contained:

// 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

If we switch to int64, and use 10,000,000 as the upper bound, and read the final accumulated result, the code executes in about 450 milli-seconds on a Corei5 with a final output of:

50000005000000

Incidentally, for an upper bound of 1,000,000 the execution is about 40 milli-seconds, so increasing the size by 10 increased the time by 10.

久夏青 2025-01-08 14:53:13

返回操作序列的中间结果:

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

我基于 APL 扫描运算符(类似于 Aggregate 本质上是 APL 归约运算符)构建了一个通用(有很多变体)扩展方法,该方法 然后可以像这样使用:

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

I built a general purpose (with lots of variations) extension method based on the APL scan operator (analogous to how Aggregate is essentially the APL reduce operator) that returns the intermediate results of the sequence of operations:

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

Which can then be used like:

var ans = list.Scan(0.0, (a, d) => a+d).ToList();
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文