Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/264.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 这两种洗牌IEnumerable的算法之间有性能差异吗?_C#_Linq_Performance_Ienumerable - Fatal编程技术网

C# 这两种洗牌IEnumerable的算法之间有性能差异吗?

C# 这两种洗牌IEnumerable的算法之间有性能差异吗?,c#,linq,performance,ienumerable,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;

这两个问题给出了洗牌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);
    }
}
公共静态IEnumerable Shuffle1(此IEnumerable源)
{
Random Random=新的Random();
T[]copy=source.ToArray();
对于(int i=copy.Length-1;i>=0;i--){
int index=random.Next(i+1);
收益率返回副本[索引];
复制[索引]=复制[i];
}
}
公共静态IEnumerable Shuffle2(此IEnumerable源)
{
Random Random=新的Random();
List copy=source.ToList();
而(copy.Count>0){
int index=random.Next(copy.Count);
收益率返回副本[索引];
copy.RemoveAt(索引);
}
}

它们基本相同,除了一个使用
列表
,另一个使用数组。从概念上讲,第二个问题对我来说似乎更清楚。但是,使用阵列是否会带来显著的性能优势?即使Big-O时间是相同的,如果快几倍,也会产生明显的不同。

第二个版本可能会因为RemoveAt而慢一些。列表是在向它们添加元素时增长的数组,因此,在中间插入和移除是缓慢的(事实上,RESVEAT的MSDN状态具有O(n)复杂度)。 无论如何,最好是简单地使用探查器来比较这两种方法。

第一种算法是O(n),因为它有一个循环,在每次迭代中执行O(1)交换。第二个算法是O(n^2),因为它在每次迭代中都执行O(n)RemoveAt操作。此外,列表上的索引器比数组上的索引器慢,因为前者是方法调用,而后者是IL指令

因此,在这两种方法中,第一种可能更快。也就是说,如果你追求的是绩效,为什么还要费心产生结果呢?它已经在转换为一个数组,所以只需将其洗牌,然后直接返回数组(或者如果您担心有人更改它,可以使用
ReadOnlyCollection
包装),这可能会更快


另一方面,这两种方法都有缺陷,即当多个线程使用时,
Random
的行为是未定义的,因此它们可能应该使用。

第一种方法没有编译,尽管很明显,您试图具体化可枚举性,然后实现Fisher-Yates;这可能是正确的方法,而且对于任何以前洗牌过阵列的人来说,这应该是不清楚的。第二种使用
RemoveAt
是不好的,原因由其他评论者陈述


编辑:您的顶级实现现在看起来是正确的,这是一个很好的方法。

+1回答:我会添加
列表。RemoveAt
是O(n),其中n是Count-index,这意味着从开始删除比从结束删除需要更多的时间。它必须在索引之后移动每个值。没有必要对此进行分析,第二个只会更慢。第一个是“Fisher-Yates Shuffle”,任何有数据结构101知识的人都能很好地识别它。在Linqpad中运行这两个程序,使用一个300k整数的列表,可以得到一个计时器计数,第一个平均耗时0.1秒,第二个耗时22.5秒(我在2GB RAM的3 GHz奔腾D上运行Windows XP)。我应该添加初始化我的列表所包含的计时,我使用了一个foreach循环,从shuffle中获取每个元素并将其放置在hashset中。如果您可能不需要所有元素,那么这是很好的。假设您想要1000个集合中的前3个随机元素。这将比洗牌所有1000个元素,然后选择前3个元素快得多。@Matthew-true,但是如果性能是一个问题,那么我将使用两种方法;一个是在一个完整的洗牌(洗牌)和一个优化采取随机抽样(抽样或随机)。我还认为这可能有点自我记录,因为人们不一定期望一个shuffle方法是懒惰的。仅供参考,它肯定是O(N^2)。随着列表的缩小,
RemoveAt
每次迭代平均需要运行N/2个项目,因此
RemoveAt
的运行时间仍然与N成正比。@mquander-是的,你是对的。这是漫长的一天。更正了我的答案。哦,修复了编译错误。我更改了我链接到的两个问题的变量名,以便它们尽可能地相互匹配,但我遗漏了一个。您以两种方式编写了代码。如果您想知道哪一个更快,请运行代码,然后您就知道了。对我来说,这是一个谜,为什么这么多人会提出“哪一个更快?”的问题。当然,坐在办公桌前,简单地以两种方式运行代码要比在互联网上等待别人为你运行代码或猜测代码更快、更准确。@Matthew总是发布自己的基准测试,然后更好地询问原因。