关于随机抽样和潜在问题的一句话:
Random 类在生成唯一随机数序列的能力方面存在一些限制,请参阅文档的备注部分:
- 具体来说,因为 Random 的默认构造函数使用基于系统时钟的种子进行初始化,所以当 Random 实例的初始化非常快速地重复时,您可以获得重复的序列。前面引用的备注部分包含演示此问题的代码。
- 不保证对 Random 类实例的方法的调用是线程安全的。因此,使用 Random 的单个实例来避免种子问题可能会遇到其他线程问题。
使用Guid.NewGuid 方法对集合进行采样将为生成随机排序提供更好的结果,因为如果快速连续调用它不会重复序列。但是,它确实带有一些警告:
- 虽然它作为随机选择器在卡方测试中表现良好,但它不适合加密目的,也不能直接替代实际的随机数生成器。
- 只要 NewGuid 生成的 GUID 是版本 4 GUID,GUID 确实包含随机内容;但是,这可能会在未来发生变化。Microsoft 没有做出任何保证 GUID 将包含随机内容的声明,只是保证它们是唯一的,并且冲突概率极低。
这是使用 NewGuid 的样子:
string[] myRandomArray = choices.OrderBy(x => Guid.NewGuid()).Take(3).ToArray();
使用RNGCryptoServiceProvider 类是产生可靠随机抽样的最佳解决方案。它既是线程安全的,也不会受到 Random 类的种子问题的影响。然而,它确实需要更多的代码来实现。
// TODO: Add code to demonstrate using RNGCryptoServiceProvider
您可以使用 take 而不是 For 循环,因为您已经在使用 LINQ:
// Get 3 random choices in a List.
List<string> myChoices = choices.OrderBy(x => Guid.NewGuid()).Take(3).ToList();
// Add the correct answer.
myChoices.Add(theAnswer);
// Randomize the resulting set of 4 choices into an Array.
string[] myRandomizedChoices = myChoices.OrderBy(x => Guid.NewGuid()).ToArray();
this.radioButton1.Text = myRandomizedChoices[0];
this.radioButton2.Text = myRandomizedChoices[1];
this.radioButton3.Text = myRandomizedChoices[2];
this.radioButton4.Text = myRandomizedChoices[3];