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
等都会缓冲他们的“次要”输入序列。这不是问题,依我看