Shuffle any (I)List
with an extension method based on the Fisher-Yates shuffle :
(使用基于Fisher-Yates shuffle的扩展方法对(I)List
进行随机排序 :)
private static Random rng = new Random();
public static void Shuffle<T>(this IList<T> list)
{
int n = list.Count;
while (n > 1) {
n--;
int k = rng.Next(n + 1);
T value = list[k];
list[k] = list[n];
list[n] = value;
}
}
Usage:
(用法:)
List<Product> products = GetProducts();
products.Shuffle();
The code above uses the much criticised System.Random method to select swap candidates.
(上面的代码使用备受批评的System.Random方法来选择交换候选。)
It's fast but not as random as it should be. (它速度很快,但不如应有的随机。)
If you need a better quality of randomness in your shuffles use the random number generator in System.Security.Cryptography like so: (如果您需要更好的随机性,请使用System.Security.Cryptography中的随机数生成器,如下所示:)
using System.Security.Cryptography;
...
public static void Shuffle<T>(this IList<T> list)
{
RNGCryptoServiceProvider provider = new RNGCryptoServiceProvider();
int n = list.Count;
while (n > 1)
{
byte[] box = new byte[1];
do provider.GetBytes(box);
while (!(box[0] < n * (Byte.MaxValue / n)));
int k = (box[0] % n);
n--;
T value = list[k];
list[k] = list[n];
list[n] = value;
}
}
A simple comparison is available at this blog (WayBack Machine).
(在此博客 (WayBack Machine)上可以进行简单的比较。)
Edit: Since writing this answer a couple years back, many people have commented or written to me, to point out the big silly flaw in my comparison.
(编辑:自从几年前写下这个答案以来,许多人向我发表评论或写信给我,指出我的比较中的一个愚蠢的缺陷。)
They are of course right. (他们当然是对的。)
There's nothing wrong with System.Random if it's used in the way it was intended. (如果System.Random按预期方式使用,则没有任何问题。)
In my first example above, I instantiate the rng variable inside of the Shuffle method, which is asking for trouble if the method is going to be called repeatedly. (在上面的第一个示例中,我在Shuffle方法内部实例化了rng变量,该变量询问是否要重复调用该方法。)
Below is a fixed, full example based on a really useful comment received today from @weston here on SO. (以下是一个固定的完整示例,该示例基于今天从@weston收到的关于SO的非常有用的评论。)
Program.cs:
(Program.cs:)
using System;
using System.Collections.Generic;
using System.Threading;
namespace SimpleLottery
{
class Program
{
private static void Main(string[] args)
{
var numbers = new List<int>(Enumerable.Range(1, 75));
numbers.Shuffle();
Console.WriteLine("The winning numbers are: {0}", string.Join(", ", numbers.GetRange(0, 5)));
}
}
public static class ThreadSafeRandom
{
[ThreadStatic] private static Random Local;
public static Random ThisThreadsRandom
{
get { return Local ?? (Local = new Random(unchecked(Environment.TickCount * 31 + Thread.CurrentThread.ManagedThreadId))); }
}
}
static class MyExtensions
{
public static void Shuffle<T>(this IList<T> list)
{
int n = list.Count;
while (n > 1)
{
n--;
int k = ThreadSafeRandom.ThisThreadsRandom.Next(n + 1);
T value = list[k];
list[k] = list[n];
list[n] = value;
}
}
}
}
与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…