【问题标题】:What is the most efficient structure to work with datestamped data处理带日期戳的数据最有效的结构是什么
【发布时间】:2019-09-17 20:44:46
【问题描述】:

各位程序员,

我有一个返回 IEnumerable(T) 的方法,其中 T 包含一个 DateTime 属性。

我需要从这组数据中执行许多基于日期的提取:例如,在 Date1 和 Date2 之间的所有项目。

随着数据集越来越大,我面临一个性能问题:这些提取需要一段时间。我觉得可以通过选择更适合枚举的数据结构来优化它。

我现在正在做的是:

              public class Foo
    {
        public DateTime Date { get; set; }
        public double Value { get; set; }
    }


    public class DoSomething
    {
        public IEnumerable<Foo> Foos { get;}

        public IEnumerable<Foo[]> DoStuff(DateTime[] dates)
        {
            var foos = Foos.
                OrderBy(x=>x.Date)
                .ToArray(); //Prevents multiple enumeration later on, Any better suited structure ? 

            for (int i = 0; i < dates.Length-1; i++)
            {
                yield return foos
                    .Where(x => x.Date > dates[i])
                    .Where(y=>y.Date<dates[i+1])
                    .ToArray();
            }
        }
    }

我读过 LINQ 方法 OrderBy 创建了一个 IOrderEnumerable,但我觉得将它枚举到一个数组会破坏逻辑顺序项目。如何防止多次枚举保持顺序关系以供进一步使用?

【问题讨论】:

  • 你的问题是那个循环。假设 foos 包含 10,000 个项目。每个项目一次,循环遍历foos整个 集合。你这样做 10,000 次。您似乎没有在我能看到的任何地方使用循环索引参数i。我看不出那个循环的目的......它的预期目的是什么?
  • 这个循环只是一个例子,它是一个人工制品,用于解释我需要多次执行此操作,如果有误导,请见谅。
  • 这非常具有误导性。您的示例应该是说明性的或代表您的实际情况。 (这就是为什么我没有把它写成答案 - 感觉有点off
  • 当您ToArray 时,每次枚举时,都会保证生成的 IEnumerable(数组)的顺序正确。顺便说一句,您似乎没有使用循环变量i。从外观上看,您生成了几个 (foos.Length) 数组,每个数组的内容完全相同。我错过了什么?
  • @XavierAM 通过编辑,这个问题更有意义。谢谢。

标签: c# linq sorting ienumerable


【解决方案1】:

到目前为止,您的算法中最慢的点是 Where 的 2 倍。永远记住:Where 对于大集合和更复杂的比较函数总是很慢。

所以这里有一个更好的算法:我会用自定义二进制搜索替换这两个WhereWhere的时间复杂度是O(n),而二分查找的复杂度是O(log n)。二进制搜索的目的是找到最接近边缘日期的元素,换句话说,您将在foo 集合中找到比dates[i] 大的最小日期,然后分别找到最大日期比小于dates[i+1]

参考:https://en.wikipedia.org/wiki/Binary_search_algorithm

因此,您编写了两个帮助方法来查找 foo 中的下限和上限项,然后您可以像现在一样简单地生成区间。

此外,您还可以通过将Foos.OrderBy.ToArray 替换为Foos.SortFoos.Clone.Sort 来获得另一个微小的改进。您只需要提供一个比较功能。 (但是这个重构没有上面的那么重要。)

通过使用这种方法,您可以获得 O(m.log n) 的时间复杂度,而不是当前的 O(m.n),其中 n 是集合的大小,m 是日期对的数量。

【讨论】:

  • 嗯,好的,我去看看。实际上,为了更好的可读性,我编写了两个顺序 where 语句,但它在代码中的实现方式是:.Where(x =&gt; x.Date &gt; dates[i] &amp;&amp; x.Date&lt;dates[i+1]) 我猜它不会改变 O(n) 复杂度,是吗?而且,还有一个问题,是否有任何现成的结构可以实现二进制搜索算法,或者我应该创建一个自定义的结构吗?
  • @XavierAM 好旧的List&lt;T&gt; 包含一个BinarySearch 方法。
  • @spender 是的,但这是基于简单的相等性。这里我们需要找到最接近的日期,不相等,所以实现需要不同。
猜你喜欢
  • 2020-09-19
  • 1970-01-01
  • 2016-12-15
  • 2010-09-05
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-11-04
  • 2023-03-31
相关资源
最近更新 更多