【问题标题】:In LINQ, does orderby() execute the comparing function only once or execute it whenever needed?在 LINQ 中,orderby() 是只执行一次比较函数还是在需要时执行它?
【发布时间】:2011-04-26 20:37:44
【问题描述】:

我在网上找到了一种洗牌数组的方法。

Random rand = new Random();
shuffledArray = myArray.OrderBy(x => rand.Next()).ToArray();

不过,我有点担心这种方法的正确性。如果 OrderBy 对同一个项目多次执行 x => rand.Next(),结果可能会发生冲突并导致奇怪的事情(可能是异常)。

我试过了,一切都很好,但我仍然想知道这是否绝对安全并且总是按预期工作,我无法通过谷歌找到答案。

谁能给我一些解释?

提前致谢。

【问题讨论】:

  • 问得好,我很好奇你会得到什么答案。

标签: c# .net linq


【解决方案1】:

您的方法应该可行,但速度很慢。

之所以有效,是因为OrderBy 首先使用键选择器计算每个项目的键,然后对键进行排序。所以每个项目只调用一次键选择器。

在 .NET Reflector 中查看 ComputeKeys 类中的方法 EnumerableSorter

this.keys = new TKey[count];
for (int i = 0; i < count; i++)
{
    this.keys[i] = this.keySelector(elements[i]);
}
// etc...

这是否绝对安全并始终按预期工作

它没有记录,因此理论上它将来可能会改变。

对于随机洗牌,您可以使用Fisher-Yates shuffle。这也更有效 - 仅使用 O(n) 时间并就地改组,而不是 O(n log(n)) 时间和 O(n) 额外内存。

相关问题

【讨论】:

  • 感谢您的详细解答和建议。我不知道有类似的问题。
【解决方案2】:

我假设您正在谈论 LINQ-to-Objects,在这种情况下,用于比较的键只为每个元素生成一次。 (请注意,这只是当前实现的一个细节,可能会发生变化,尽管不太可能,因为这样的变化会引入您提到的错误。)

回答您更一般的问题:您的方法应该有效,但有更好的方法可以做到这一点。使用OrderBy 的性能通常为 O(n log n),而Fisher-Yates-Durstenfeld shuffle 的性能通常为 O(n)。

(如果您愿意,可以使用 example Shuffle extension for IEnumerable&lt;T&gt; herein-place equivalent for IList&lt;T&gt; here。)

【讨论】:

  • 非常感谢。实际上我在 C++ 中遇到过这样的错误。
【解决方案3】:

使用shufflebag 肯定会起作用。

至于您的 orderby 方法,我认为它不是完全随机的,因为保持了相等元素的顺序。因此,如果您有一个随机数组 [5 6 7 2 6],那么两个 6 处的元素将始终保持相同的顺序。

我必须进行频率测试才能确定。

【讨论】:

  • 感谢您的评论。如果我需要更认真地做,我会考虑其他方法。好吧,至少它只需要几行。
  • 虽然OrderBy 执行稳定的排序,但键是按索引生成的,而不是按值生成的。因此,当您生成随机密钥时,可能会打乱相同的值。
猜你喜欢
  • 1970-01-01
  • 2018-04-07
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-11-01
  • 2021-04-19
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多