在c#中随机化泛型列表顺序的最佳方法是什么?我在一个列表中有一个有限的75个数字集,我想随机分配一个顺序,以便为彩票类型的应用程序绘制它们。
当前回答
实现:
public static class ListExtensions
{
public static void Shuffle<T>(this IList<T> list, Random random)
{
for (var i = list.Count - 1; i > 0; i--)
{
int indexToSwap = random.Next(i + 1);
(list[indexToSwap], list[i]) = (list[i], list[indexToSwap]);
}
}
}
例子:
var random = new Random();
var array = new [] { 1, 2, 3 };
array.Shuffle(random);
foreach (var item in array) {
Console.WriteLine(item);
}
.NET小提琴演示
其他回答
想法是获得匿名对象的项目和随机顺序,然后按此顺序重新排序项目,并返回值:
var result = items.Select(x => new { value = x, order = rnd.Next() })
.OrderBy(x => x.order).Select(x => x.value).ToList()
可以使用morelinq包中的Shuffle扩展方法,它适用于IEnumerables
安装包morelinq
using MoreLinq;
...
var randomized = list.Shuffle();
通过使用元组进行交换,可以使Fisher-Yates shuffle更加简洁和富有表现力。
private static readonly Random random = new Random();
public static void Shuffle<T>(this IList<T> list)
{
int n = list.Count;
while (n > 1)
{
n--;
int k = random.Next(n + 1);
(list[k], list[n]) = (list[n], list[k]);
}
}
这里有一个线程安全的方法来做到这一点:
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());
}
}
这里是Fisher-Yates shuffle的实现,允许指定返回的元素数量;因此,在获取所需数量的元素之前,没有必要首先对整个集合进行排序。
交换元素的顺序与默认值相反;从第一个元素到最后一个元素,因此检索集合的一个子集与洗牌整个集合产生相同的(部分)序列:
collection.TakeRandom(5).SequenceEqual(collection.Shuffle().Take(5)); // true
该算法基于Durstenfeld在维基百科上的(现代)Fisher-Yates shuffle。
public static IList<T> TakeRandom<T>(this IEnumerable<T> collection, int count, Random random) => shuffle(collection, count, random);
public static IList<T> Shuffle<T>(this IEnumerable<T> collection, Random random) => shuffle(collection, null, random);
private static IList<T> shuffle<T>(IEnumerable<T> collection, int? take, Random random)
{
var a = collection.ToArray();
var n = a.Length;
if (take <= 0 || take > n) throw new ArgumentException("Invalid number of elements to return.");
var end = take ?? n;
for (int i = 0; i < end; i++)
{
var j = random.Next(i, n);
(a[i], a[j]) = (a[j], a[i]);
}
if (take.HasValue) return new ArraySegment<T>(a, 0, take.Value);
return a;
}
推荐文章
- 驻留在App_Code中的类不可访问
- 在链式LINQ扩展方法调用中等价于'let'关键字的代码
- dynamic (c# 4)和var之间的区别是什么?
- Visual Studio: ContextSwitchDeadlock
- 返回文件在ASP。Net Core Web API
- 自定义HttpClient请求头
- 如果我使用OWIN Startup.cs类并将所有配置移动到那里,我是否需要一个Global.asax.cs文件?
- VS2013外部构建错误"error MSB4019: The imported project <path> was not found"
- 从另一个列表id中排序一个列表
- 等待一个无效的异步方法
- 无法加载文件或程序集…参数不正确
- c#中枚举中的方法
- 如何从字符串中删除新的行字符?
- 如何设置一个默认值与Html.TextBoxFor?
- 检查属性是否有属性