C# 洗牌所需IEnumerable的一种扩展方法

C# 洗牌所需IEnumerable的一种扩展方法,c#,ienumerable,shuffle,C#,Ienumerable,Shuffle,我需要一个扩展方法来洗牌IEnumerable。还可以使用int指定返回的IEnumerable的大小。更好地保持IEnumerable的不变性。我当前针对IList的解决方案- public static IList<T> Shuffle<T>(this IList<T> list, int size) { Random rnd = new Random(); var res = new T[size]; res[0] = list

我需要一个扩展方法来洗牌
IEnumerable
。还可以使用
int
指定返回的
IEnumerable
的大小。更好地保持
IEnumerable
的不变性。我当前针对IList的解决方案-

public static IList<T> Shuffle<T>(this IList<T> list, int size)
{
    Random rnd = new Random();
    var res = new T[size];

    res[0] = list[0];
    for (int i = 1; i < size; i++)
    {
        int j = rnd.Next(i);
        res[i] = res[j];
        res[j] = list[i];
    }
    return res;
}

public static IList<T> Shuffle<T>(this IList<T> list)
{ return list.Shuffle(list.Count); }
公共静态IList Shuffle(此IList列表,int size)
{
随机rnd=新随机();
var res=新T[尺寸];
res[0]=列表[0];
对于(int i=1;i
带着一些林克的爱:

public static IEnumerable<T> Shuffle<T>(this IEnumerable<T> list, int size)
{
    var r = new Random();
    var shuffledList = 
        list.
            Select(x => new { Number = r.Next(), Item = x }).
            OrderBy(x => x.Number).
            Select(x => x.Item).
            Take(size); // Assume first @size items is fine

    return shuffledList.ToList();
}
公共静态IEnumerable Shuffle(此IEnumerable列表,int-size)
{
var r=新的随机变量();
var shuffledList=
列表
选择(x=>new{Number=r.Next(),Item=x})。
OrderBy(x=>x.Number)。
选择(x=>x.Item)。
接受(大小);//假设第一个@size项目可以
返回shuffledList.ToList();
}

安东有这个想法,但你可以把它做成两行:

public static IEnumerable<T> Shuffle<T>(this IEnumerable<T> enumerable)
{
    var r = new Random();
    return enumerable.OrderBy(x=>r.Next()).ToList();
}
公共静态IEnumerable Shuffle(此IEnumerable可枚举)
{
var r=新的随机变量();
返回可枚举的.OrderBy(x=>r.Next()).ToList();
}
不幸的是,它不能被惰性地评估,因为当它执行时,
r
将超出范围。您可以创建一个IEnumerable实现来封装此代码并返回该代码,但这会变得更复杂。

您可以使用。无需显式地将size参数传递给方法本身,如果不需要整个序列,只需附加对的调用:

var shuffled = originalSequence.Shuffle().Take(5);

// ...

public static class EnumerableExtensions
{
    public static IEnumerable<T> Shuffle<T>(this IEnumerable<T> source)
    {
        return source.Shuffle(new Random());
    }

    public static IEnumerable<T> Shuffle<T>(this IEnumerable<T> source, Random rng)
    {
        if (source == null) throw new ArgumentNullException("source");
        if (rng == null) throw new ArgumentNullException("rng");

        return source.ShuffleIterator(rng);
    }

    private static IEnumerable<T> ShuffleIterator<T>(
        this IEnumerable<T> source, Random rng)
    {
        var buffer = source.ToList();
        for (int i = 0; i < buffer.Count; i++)
        {
            int j = rng.Next(i, buffer.Count);
            yield return buffer[j];

            buffer[j] = buffer[i];
        }
    }
}
var shuffled=originalSequence.Shuffle().Take(5); // ... 公共静态类EnumerableExtensions { 公共静态IEnumerable Shuffle(此IEnumerable源) { 返回source.Shuffle(newrandom()); } 公共静态IEnumerable Shuffle(此IEnumerable源,随机rng) { 如果(source==null)抛出新的ArgumentNullException(“source”); 如果(rng==null)抛出新的ArgumentNullException(“rng”); 返回源。ShuffleIterator(rng); } 私有静态IEnumerable ShuffleIterator( 此IEnumerable源(随机rng) { var buffer=source.ToList(); for(int i=0;i
请注意,要想让
出现,它们通常必须被格式化为代码,或者内联反引号(正如我添加的那样)或者(正如您所做的那样)带有四个空格缩进,这与
OrderBy(x=>r.Next())
不一样吗?@Gulshan:是的。按随机数排序被认为不是一个很好的洗牌算法。它起作用了,但它不像可能的那么随机。不,它不一样
OrderBy(x=>r.Next())
可能会将排序放入无限循环中。这不可能。@Jim:OrderBy方法实际上在内部执行类似的操作——使用投影为每个项目生成一个键,存储它,然后使用存储的键进行排序——因此当前MS实现不会出现无限循环的危险。(虽然不能保证在不同的平台/版本上的实现是相同的。)@LukeH:你能给我一个指向更多信息的指针吗?你所说的对我来说毫无意义,尤其是在比较函数的上下文中(这里使用的是
r.Next
)。我遗漏了什么?听说这有点偏颇。@Gulshan:应该不会太糟糕,尽管
OrderBy
是一种稳定的排序可能会导致问题(而且
Random
本身可能有一些固有的偏颇)。使用OrderBy的主要潜在缺点是它的时间复杂度为O(n lgn);可以在O(n)中执行洗牌,并且没有偏差。@Jim:OrderBy的当前CLR实现只对每个元素执行一次投影,存储键,然后使用存储的键进行排序,因此当前没有无限循环的危险。(当然,这取决于理论上可能发生变化的实现细节。)这个“r将超出范围”是怎么回事?变量捕获呢?省略此代码段中的
.ToList()
应该不会有问题…-1这是一种托管语言,
r
在堆上分配,并且永远不会“超出范围”,GC将确保
r
在不再引用之前不会被垃圾收集。第二个方法的目的只是抛出异常吗?是的,因此,参数检查是迫不及待地进行的,而不是被推迟。如果将第二个和第三个方法合并在一起,则在开始迭代序列之前不会进行任何参数检查(可能如果它是Linq可枚举的,那么你正在破坏它们的延迟执行?最好是请求IList!@nawfal:是的,该方法必须缓冲源
IEnumerable
的内容,以便它可以执行洗牌。然后它会懒洋洋地生成其输出。我不确定你请求IList是什么意思,或者它是如何实现的。)t会有帮助。@nawfal:许多内置LINQ方法在内部缓冲整个序列,然后惰性地产生结果:例如,
GroupBy
OrderBy
OrderByDescending
ThenBy
ThenBy descending
Reverse
等都需要缓冲它们的源序列;
除了
GroupJoin
Intersect
Join
等都会缓冲他们的“次要”输入序列。这不是问题,依我看