这两种算法之间是否存在改变IEnumerable的性能差异?

Mat*_*hew 4 c# linq performance ienumerable

这两个问题为洗刷IEnumerable提供了类似的算法:

以下是两种方法并排:

public static IEnumerable<T> Shuffle1<T> (this IEnumerable<T> source)
{
    Random random = new Random ();
    T [] copy = source.ToArray ();

    for (int i = copy.Length - 1; i >= 0; i--) {
        int index = random.Next (i + 1);
        yield return copy [index];
        copy [index] = copy [i];
    }
}


public static IEnumerable<T> Shuffle2<T> (this IEnumerable<T> source)
{
    Random random = new Random ();
    List<T> copy = source.ToList ();

    while (copy.Count > 0) {
        int index = random.Next (copy.Count);
        yield return copy [index];
        copy.RemoveAt (index);
    }
}
Run Code Online (Sandbox Code Playgroud)

它们基本相同,除了一个使用a List,一个使用数组.从概念上讲,第二个似乎对我来说更清楚.但使用阵列是否可以获得显着的性能优势?即使Big-O时间相同,如果它快几倍,它也会产生明显的差异.

Eti*_*tel 7

由于RemoveAt,第二个版本可能会慢一点.列表实际上是在向元素添加元素时增长的数组,因此,中间的插入和删除速度很慢(事实上,MSDN声明RemoveAt具有O(n)复杂度).

无论如何,最好的方法是简单地使用分析器来比较两种方法.

  • +1回答:我想补充说`List <T> .RemoveAt`是O(n),其中n是Count - index意味着从开头删除比结束需要更多的时间.它必须在索引之后移动每个值.没有必要对此进行分析,第二个只能更慢.第一个是"Fisher-Yates Shuffle",对于拥有*Data Structures 101*知识的任何人来说都是高度认可的. (2认同)
  • 在Linqpad中运行两个计时器计数,使用300k整数列表,第一个平均为0.1秒,第二个花费22.5秒(我在3 GHz Pentium D上运行Windows XP,内存为2GB).我应该添加时间包括初始化我的列表,我使用foreach循环从shuffle中取出每个元素并将其放在HashSet中 (2认同)