C# 随机化一个 List<T>
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/273313/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Randomize a List<T>
提问by mirezus
What is the best way to randomize the order of a generic list in C#? I've got a finite set of 75 numbers in a list I would like to assign a random order to, in order to draw them for a lottery type application.
在 C# 中随机化通用列表顺序的最佳方法是什么?我在一个列表中有 75 个数字的有限集合,我想为其分配一个随机顺序,以便为彩票类型的应用程序绘制它们。
采纳答案by grenade
Shuffle any (I)List
with an extension method based on the Fisher-Yates shuffle:
(I)List
使用基于Fisher-Yates shuffle的扩展方法随机播放任何内容:
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. 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.Random 方法来选择交换候选。它很快,但不像它应该的那样随机。如果您需要更好的随机性质量,请使用 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. 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. Below is a fixed, full example based on a really useful comment received today from @weston here on SO.
编辑:自从几年前写下这个答案以来,很多人评论或写信给我,以指出我比较中的大愚蠢缺陷。他们当然是对的。如果 System.Random 以预期的方式使用,则它没有任何问题。在上面的第一个示例中,我在 Shuffle 方法内部实例化了 rng 变量,如果该方法将被重复调用,这会带来麻烦。下面是一个固定的、完整的示例,它基于今天从 @weston 收到的关于 SO 的非常有用的评论。
Program.cs:
程序.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;
}
}
}
}
回答by dmo
If you have a fixed number (75), you could create an array with 75 elements, then enumerate your list, moving the elements to randomized positions in the array. You can generate the mapping of list number to array index using the Fisher-Yates shuffle.
如果你有一个固定的数字 (75),你可以创建一个包含 75 个元素的数组,然后枚举你的列表,将元素移动到数组中的随机位置。您可以使用Fisher-Yates shuffle生成列表编号到数组索引的映射。
回答by albertein
I usually use:
我通常使用:
var list = new List<T> ();
fillList (list);
var randomizedList = new List<T> ();
var rnd = new Random ();
while (list.Count != 0)
{
var index = rnd.Next (0, list.Count);
randomizedList.Add (list [index]);
list.RemoveAt (index);
}
回答by Aleris
A very simple approach to this kind of problem is to use a number of random element swap in the list.
解决此类问题的一个非常简单的方法是在列表中使用多个随机元素交换。
In pseudo-code this would look like this:
在伪代码中,这看起来像这样:
do
r1 = randomPositionInList()
r2 = randomPositionInList()
swap elements at index r1 and index r2
for a certain number of times
回答by Adam Tegen
public static List<T> Randomize<T>(List<T> list)
{
List<T> randomizedList = new List<T>();
Random rnd = new Random();
while (list.Count > 0)
{
int index = rnd.Next(0, list.Count); //pick a random item from the master list
randomizedList.Add(list[index]); //place it at the end of the randomized list
list.RemoveAt(index);
}
return randomizedList;
}
回答by Denis
Extension method for IEnumerable:
IEnumerable 的扩展方法:
public static IEnumerable<T> Randomize<T>(this IEnumerable<T> source)
{
Random rnd = new Random();
return source.OrderBy<T, int>((item) => rnd.Next());
}
回答by user453230
If we only need to shuffle items in a completely random order (just to mix the items in a list), I prefer this simple yet effective code that orders items by guid...
如果我们只需要以完全随机的顺序打乱项目(只是为了混合列表中的项目),我更喜欢这个简单而有效的代码,它通过 guid 对项目进行排序......
var shuffledcards = cards.OrderBy(a => Guid.NewGuid()).ToList();
回答by Jodrell
EDITThe RemoveAt
is a weakness in my previous version. This solution overcomes that.
编辑这RemoveAt
是我以前版本的一个弱点。该解决方案克服了这一点。
public static IEnumerable<T> Shuffle<T>(
this IEnumerable<T> source,
Random generator = null)
{
if (generator == null)
{
generator = new Random();
}
var elements = source.ToArray();
for (var i = elements.Length - 1; i >= 0; i--)
{
var swapIndex = generator.Next(i + 1);
yield return elements[swapIndex];
elements[swapIndex] = elements[i];
}
}
Note the optional Random generator
, if the base framework implementation of Random
is not thread-safe or cryptographically strong enough for your needs, you can inject your implementation into the operation.
请注意 optional Random generator
,如果 的基本框架实现Random
不是线程安全的或加密强度不足以满足您的需要,您可以将您的实现注入到操作中。
Random
可以在此答案中找到线程安全加密强实现的合适实现。
Here's an idea, extend IList in a (hopefully) efficient way.
这是一个想法,以(希望)有效的方式扩展 IList。
public static IEnumerable<T> Shuffle<T>(this IList<T> list)
{
var choices = Enumerable.Range(0, list.Count).ToList();
var rng = new Random();
for(int n = choices.Count; n > 1; n--)
{
int k = rng.Next(n);
yield return list[choices[k]];
choices.RemoveAt(k);
}
yield return list[choices[0]];
}
public static IEnumerable<T> Shuffle<T>(this IList<T> list)
{
var choices = Enumerable.Range(0, list.Count).ToList();
var rng = new Random();
for(int n = choices.Count; n > 1; n--)
{
int k = rng.Next(n);
yield return list[choices[k]];
choices.RemoveAt(k);
}
yield return list[choices[0]];
}
回答by BSalita
Here's an efficient Shuffler that returns a byte array of shuffled values. It never shuffles more than is needed. It can be restarted from where it previously left off. My actual implementation (not shown) is a MEF component that allows a user specified replacement shuffler.
这是一个高效的 Shuffler,它返回一个经过混洗的值的字节数组。它永远不会超过需要的次数。它可以从之前停止的地方重新启动。我的实际实现(未显示)是一个 MEF 组件,它允许用户指定替换洗牌器。
public byte[] Shuffle(byte[] array, int start, int count)
{
int n = array.Length - start;
byte[] shuffled = new byte[count];
for(int i = 0; i < count; i++, start++)
{
int k = UniformRandomGenerator.Next(n--) + start;
shuffled[i] = array[k];
array[k] = array[start];
array[start] = shuffled[i];
}
return shuffled;
}
`
`
回答by Christopher Stevenson
Here's a thread-safe way to do this:
这是执行此操作的线程安全方法:
public static class EnumerableExtension
{
private static Random globalRng = new Random();
[ThreadStatic]
private static Random _rng;
private static Random rng
{
get
{
if (_rng == null)
{
int seed;
lock (globalRng)
{
seed = globalRng.Next();
}
_rng = new Random(seed);
}
return _rng;
}
}
public static IEnumerable<T> Shuffle<T>(this IEnumerable<T> items)
{
return items.OrderBy (i => rng.Next());
}
}