计算一个,两个和三个连续项的SUM的更好方法

时间:2016-08-11 16:33:40

标签: c# sql linq sum

public class myItem
{
    public int ID;
    public long Value;
    public long Sum1;
    public long Sum2;
    public long Sum3;
}

数据

ID   Value
1    25
2    45
3    56
4    21

结果:

     Sum1     Sum2         Sum3
1    25     25 + 45    25 + 45 + 56
2    45     45 + 56    45 + 56 + 21
3    56     56 + 21    56 + 21 + 0
4    21     21 + 0     21 +  0 + 0

当前程序:工作但很慢(约10分钟),行数为100k。

List<myItem> list;
for (int i = 0; i < list.Count(); i++)
{
   myItem m = list[i];
   m.Sum1 = list.Where(x => x.ID == i).Sum(x => x.Value);
   m.Sum2 = list.Where(x => x.ID >= i && x.ID <= i + 2).Sum(x => x.Value);
   m.Sum3 = list.Where(x => x.ID >= i && x.ID <= i + 3).Sum(x => x.Value);
}

所以我想应该有办法在没有for的情况下加快速度。

4 个答案:

答案 0 :(得分:5)

缓慢的根本原因是你的循环是O(n ^ 2),因为对于每个元素,你搜索整个列表的后继。下面将其减少到O(n log n)(最慢的部分是排序)。

List<myItem> list;
list.Sort(<.. sort by id ..>);
for (int i = 0; i < list.Count; i++)
{
   myItem m = list[i];
   m.Sum1 = m.Value;

   m.Sum2 = m.Sum1;
   if (i < list.Count - 1)
   {
       m.Sum2 += list[i + 1].Value;
   }

   m.Sum3 = m.Sum2;
   if (i < list.Count - 2)
   {
       m.Sum3 = += list[i + 2].Value;
   }
}

答案 1 :(得分:2)

(取消删除答案,因为事实证明其他答案仍有微妙的错误)

LINQ对此没有帮助。只需根据需要引用数组索引:

List<myItem> list;

// sort list by id
list.Sort(t => t.ID);

for (int i = 0; i < list.Count; i++)
{
   myItem m = list[i];

   m.Sum1 = m.Value;
   m.Sum2 = m.Sum1 + (i + 1 < list.Count ? list[i + 1].Value : 0);
   m.Sum3 = m.Sum2 + (i + 2 < list.Count ? list[i + 2].Value : 0);
}

编辑:关于list.Count()vs list.Count的小注释

我注意到有关于不使用list.Count()Enumerable.Count())赞成list.Count(属性)的评论。我个人也喜欢这样,但应该注意的是,不像有些人可能会相信,Enumerable.Count()版本不会迭代整个列表以获得计数。相反,因为它检测到列表是ICollection<T>的实现,所以它将直接使用Count属性。实际上,在这个特定情况下 这两个选项之间的性能差异可以忽略不计。

此行为记录在案here

  

如果 source 的类型实现ICollection<T>,则该实现用于获取元素的数量。否则,此方法确定计数。

答案 2 :(得分:2)

只是对Jared的解决方案进行了一些修改,但是使用了他的想法:

        List<myItem> list;
        //list.Sort(<.. sort by id ..>);
        int count = list.Count();
        for (int i = 0; i < count; i++)
        {
            myItem m = list[i];
            m.Sum1 = m.Value;                
            m.Sum2 = (i+1 < count) ? (m.Value + list[i+1].Value) : m.Value;
            m.Sum3 = (i+2 < count) ? (m.Sum2 + list[i+2].Value) : m.Sum2;                
        }

答案 3 :(得分:0)

另一种解决方案。不需要排序。不确定它是否会运行得更快,请检查。

list.ForEach(m =>
{
      m.Sum1 = m.Value;
      m.Sum2 = ((m.ID + 1) < list.Count) ? (m.Sum1 + list[m.ID + 1].Value) : m.Sum1;
      m.Sum3 = ((m.ID + 2) < list.Count) ? (m.Sum2 + list[m.ID + 2].Value) : m.Sum2;
});