在c#中随机化泛型列表顺序的最佳方法是什么?我在一个列表中有一个有限的75个数字集,我想随机分配一个顺序,以便为彩票类型的应用程序绘制它们。
当前回答
这是我最喜欢的shuffle方法,当不需要修改原始的时候。它是Fisher-Yates“由内到外”算法的变体,适用于任何可枚举序列(源的长度不需要从一开始就知道)。
public static IList<T> NextList<T>(this Random r, IEnumerable<T> source)
{
var list = new List<T>();
foreach (var item in source)
{
var i = r.Next(list.Count + 1);
if (i == list.Count)
{
list.Add(item);
}
else
{
var temp = list[i];
list[i] = item;
list.Add(temp);
}
}
return list;
}
该算法还可以通过分配一个从0到length - 1的范围来实现,并通过将随机选择的索引与最后一个索引交换来随机耗尽索引,直到所有索引都被选中一次。上面的代码完成了完全相同的事情,但没有额外的分配。非常简洁。
With regards to the Random class it's a general purpose number generator (and If I was running a lottery I'd consider using something different). It also relies on a time based seed value by default. A small alleviation of the problem is to seed the Random class with the RNGCryptoServiceProvider or you could use the RNGCryptoServiceProvider in a method similar to this (see below) to generate uniformly chosen random double floating point values but running a lottery pretty much requires understanding randomness and the nature of the randomness source.
var bytes = new byte[8];
_secureRng.GetBytes(bytes);
var v = BitConverter.ToUInt64(bytes, 0);
return (double)v / ((double)ulong.MaxValue + 1);
生成随机双精度(仅在0和1之间)的目的是用于扩展到整数解。如果你需要从一个基于随机双x的列表中选择一个东西,它总是0 <= x && x < 1是很简单的。
return list[(int)(x * list.Count)];
享受吧!
其他回答
这里是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;
}
您可以使用这个简单的扩展方法来实现这一点
public static class IEnumerableExtensions
{
public static IEnumerable<t> Randomize<t>(this IEnumerable<t> target)
{
Random r = new Random();
return target.OrderBy(x=>(r.Next()));
}
}
你可以通过下面的步骤来使用它
// use this on any collection that implements IEnumerable!
// List, Array, HashSet, Collection, etc
List<string> myList = new List<string> { "hello", "random", "world", "foo", "bar", "bat", "baz" };
foreach (string s in myList.Randomize())
{
Console.WriteLine(s);
}
肯定是旧帖子,但我只是使用GUID。
Items = Items.OrderBy(o => Guid.NewGuid().ToString()).ToList();
GUID总是唯一的,因为它每次都会重新生成,所以每次结果都会改变。
如果我们只需要以完全随机的顺序洗牌项目(只是在一个列表中混合项目),我更喜欢这个简单而有效的代码,按guid排序项目…
var shuffledcards = cards.OrderBy(a => Guid.NewGuid()).ToList();
正如人们在评论中指出的那样,guid不能保证是随机的,所以我们应该使用真正的随机数生成器:
private static Random rng = new Random();
...
var shuffledcards = cards.OrderBy(a => rng.Next()).ToList();
如果您有一个固定的数字(75),您可以创建一个包含75个元素的数组,然后枚举您的列表,将元素移动到数组中的随机位置。您可以使用Fisher-Yates shuffle生成列表号到数组索引的映射。
推荐文章
- HTTP POST返回错误:417“期望失败。”
- 如何在。net中创建和使用资源
- 为什么Path。以Path.DirectorySeparatorChar开头的文件名合并不正确?
- 如何在c#中获得正确的时间戳
- Linq选择列表中存在的对象(A,B,C)
- c# .NET中的App.config是什么?如何使用它?
- c#:如何获得一个字符串的第一个字符?
- String类中的什么方法只返回前N个字符?
- 更好的方法将对象转换为int类型
- 我可以将c#字符串值转换为转义字符串文字吗?
- 在c#中转换char到int
- c#中朋友的对等物是什么?
- 关键字使用virtual+override vs. new
- 在ASP中选择Tag Helper。NET Core MVC
- 如何在没有任何错误或警告的情况下找到构建失败的原因