LINQ方法的运行时复杂性(Big-O)有什么保证?

LINQ方法的运行时复杂性(Big-O)有什么保证?

我最近开始使用LINQ了,我还没有真正看到任何LINQ方法的运行时复杂性。显然,这里有很多因素在起作用,所以让我们将讨论局限于普通的IEnumerableLINQ-to-Objects提供者。此外,假设任何Func作为选择器/ mutator /等传入的是廉价的O(1)操作。

它似乎很明显,所有的单次操作(SelectWhereCountTake/SkipAny/All,等)将是O(n)的,因为他们只需要步行的顺序一次; 虽然这甚至会受到懒惰的影响。

对于更复杂的运营来说,事情变得更加模糊; 集合类运算符(UnionDistinctExcept等)使用工作GetHashCode在默认情况下(据我所知),所以它似乎是合理的假设他们使用一个哈希表内,使这些操作为O(n)为好,一般。使用的版本怎么样IEqualityComparer

OrderBy需要排序,所以很可能我们正在看O(n log n)。如果它已经排序了怎么办?如果我说OrderBy().ThenBy()并为两者提供相同的密钥怎么样?

我可以看到GroupBy(和Join)使用排序或散列。这是什么?

Contains将是O(n)on a List,但是O(1)on a HashSet- LINQ检查基础容器以查看它是否可以加快速度?

真正的问题 - 到目前为止,我一直坚信运营是高效的。但是,我可以依靠吗?例如,STL容器清楚地指定了每个操作的复杂性。.NET库规范中的LINQ性能是否有类似的保证?

更多问题(回应评论):
没有真正想过开销,但我没想到对于简单的Linq-to-Objects有很多。CodingHorror帖子讨论的是Linq-to-SQL,在那里我可以理解解析查询并使SQL增加成本 - 对象提供者也有类似的成本吗?如果是这样,如果您使用声明性或功能性语法,它会有所不同吗?


Cats萌萌
浏览 742回答 3
3回答

当年话下

保证非常非常少,但有一些优化:使用索引访问扩展方法,比如ElementAt,Skip,Last或者LastOrDefault,将检查底层式工具与否IList<T>,让你得到O(1)访问,而不是O(N)。该Count方法检查ICollection实现,以便该操作是O(1)而不是O(N)。Distinct,GroupBy&nbsp;Join我相信set-aggregation方法(Union,Intersect和Except)也使用散列,所以它们应该接近O(N)而不是O(N²)。Contains检查ICollection实现,因此如果底层集合也是O(1),它可能是O(1),例如a&nbsp;HashSet<T>,但这取决于实际的数据结构,并且不能保证。散列集覆盖了该Contains方法,这就是它们为O(1)的原因。OrderBy&nbsp;方法使用稳定的快速排序,因此它们是O(N log N)平均情况。我认为这涵盖了大多数(如果不是全部)内置扩展方法。实际保证很少;&nbsp;Linq本身将尝试利用高效的数据结构,但编写可能效率低下的代码并不是免费的。

胡说叔叔

我早就知道如果枚举是一个.Count()返回。.CountIList但是我总是有点疲惫有关Set操作的运行时间复杂度:.Intersect(),.Except(),.Union()。这是针对.Intersect()(评论我的)反编译的BCL(.NET 4.0 / 4.5)实现:private&nbsp;static&nbsp;IEnumerable<TSource>&nbsp;IntersectIterator<TSource>(IEnumerable<TSource>&nbsp;first,&nbsp;IEnumerable<TSource>&nbsp;second,&nbsp;IEqualityComparer<TSource>&nbsp;comparer){ &nbsp;&nbsp;Set<TSource>&nbsp;set&nbsp;=&nbsp;new&nbsp;Set<TSource>(comparer); &nbsp;&nbsp;foreach&nbsp;(TSource&nbsp;source&nbsp;in&nbsp;second)&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;O(M) &nbsp;&nbsp;&nbsp;&nbsp;set.Add(source);&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;O(1) &nbsp;&nbsp;foreach&nbsp;(TSource&nbsp;source&nbsp;in&nbsp;first)&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;O(N) &nbsp;&nbsp;{ &nbsp;&nbsp;&nbsp;&nbsp;if&nbsp;(set.Remove(source))&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;O(1) &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;yield&nbsp;return&nbsp;source; &nbsp;&nbsp;}}结论:性能是O(M + N)当集合已经设置时,实现不会占用优势。(它可能不一定是直截了当的,因为使用过的也需要匹配。)IEqualityComparer<T>为了完整起见,这里都为实现.Union()和.Except()。扰流板警报:它们也具有O(N + M)&nbsp;复杂度。private&nbsp;static&nbsp;IEnumerable<TSource>&nbsp;UnionIterator<TSource>(IEnumerable<TSource>&nbsp;first,&nbsp;IEnumerable<TSource>&nbsp;second,&nbsp;IEqualityComparer<TSource>&nbsp;comparer){ &nbsp;&nbsp;Set<TSource>&nbsp;set&nbsp;=&nbsp;new&nbsp;Set<TSource>(comparer); &nbsp;&nbsp;foreach&nbsp;(TSource&nbsp;source&nbsp;in&nbsp;first) &nbsp;&nbsp;{ &nbsp;&nbsp;&nbsp;&nbsp;if&nbsp;(set.Add(source)) &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;yield&nbsp;return&nbsp;source; &nbsp;&nbsp;} &nbsp;&nbsp;foreach&nbsp;(TSource&nbsp;source&nbsp;in&nbsp;second) &nbsp;&nbsp;{ &nbsp;&nbsp;&nbsp;&nbsp;if&nbsp;(set.Add(source)) &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;yield&nbsp;return&nbsp;source; &nbsp;&nbsp;}}private&nbsp;static&nbsp;IEnumerable<TSource>&nbsp;ExceptIterator<TSource>(IEnumerable<TSource>&nbsp;first,&nbsp;IEnumerable<TSource>&nbsp;second,&nbsp;IEqualityComparer<TSource>&nbsp;comparer){ &nbsp;&nbsp;Set<TSource>&nbsp;set&nbsp;=&nbsp;new&nbsp;Set<TSource>(comparer); &nbsp;&nbsp;foreach&nbsp;(TSource&nbsp;source&nbsp;in&nbsp;second) &nbsp;&nbsp;&nbsp;&nbsp;set.Add(source); &nbsp;&nbsp;foreach&nbsp;(TSource&nbsp;source&nbsp;in&nbsp;first) &nbsp;&nbsp;{ &nbsp;&nbsp;&nbsp;&nbsp;if&nbsp;(set.Add(source)) &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;yield&nbsp;return&nbsp;source; &nbsp;&nbsp;}}

慕的地8271018

所有你能真正依赖的是,Enumerable方法是针对一般情况编写的,不会使用朴素算法。可能有第三方的东西(博客等)描述了实际使用的算法,但这些并不是官方的,也不是STL算法所保证的。为了说明,这里是Enumerable.Count来自System.Core&nbsp;的反映源代码(由ILSpy提供)://&nbsp;System.Linq.Enumerablepublic&nbsp;static&nbsp;int&nbsp;Count<TSource>(this&nbsp;IEnumerable<TSource>&nbsp;source){ &nbsp;&nbsp;&nbsp;&nbsp;checked &nbsp;&nbsp;&nbsp;&nbsp;{ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if&nbsp;(source&nbsp;==&nbsp;null) &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;throw&nbsp;Error.ArgumentNull("source"); &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;} &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ICollection<TSource>&nbsp;collection&nbsp;=&nbsp;source&nbsp;as&nbsp;ICollection<TSource>; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if&nbsp;(collection&nbsp;!=&nbsp;null) &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return&nbsp;collection.Count; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;} &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ICollection&nbsp;collection2&nbsp;=&nbsp;source&nbsp;as&nbsp;ICollection; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if&nbsp;(collection2&nbsp;!=&nbsp;null) &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return&nbsp;collection2.Count; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;} &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;int&nbsp;num&nbsp;=&nbsp;0; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;using&nbsp;(IEnumerator<TSource>&nbsp;enumerator&nbsp;=&nbsp;source.GetEnumerator()) &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;while&nbsp;(enumerator.MoveNext()) &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;num++; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;} &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;} &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return&nbsp;num; &nbsp;&nbsp;&nbsp;&nbsp;}}正如您所看到的,它需要付出一些努力来避免简单地枚举每个元素的天真解决方案。
打开App,查看更多内容
随时随地看视频慕课网APP