如何在 C# 中使用 Random 类对数组进行洗牌 [重复]
Posted
技术标签:
【中文标题】如何在 C# 中使用 Random 类对数组进行洗牌 [重复]【英文标题】:How to use Random class to shuffle array in C# [duplicate] 【发布时间】:2021-11-28 21:02:45 【问题描述】:每个人。所以我今天想练习 C#,但我一直在想如何使用 Random 类来简单地打乱数组
例如这样:
using System;
public class Program
public static void Main()
int[] arr = 1,2,3,4,5;
Random rand = new Random();
for(int i =0; i < arr.Length; i++)
int shuffle = rand.Next(arr[i]);
Console.WriteLine(arr[shuffle]);
如您所见,我尝试将 int shuffle = rand.Next(arr[i]);
用作洗牌器,但是
我猜它只是复制了数组中的一些元素。我还是菜鸟,在此先感谢您的回复。
【问题讨论】:
随机不保证你不会得到重复的数字,因为它的名字说它是随机的 您可能想使用 Fisher-Yates shuffle 算法。 【参考方案1】:您可以尝试实现 Fisher-Yates 洗牌算法:https://en.wikipedia.org/wiki/Fisher%E2%80%93Yates_shuffle
Random random = new Random();
...
for (int i = 0; i < array.Length - 1; ++i)
int r = random.Next(i, array.Length);
(array[r], array[i]) = (array[i], array[r]);
我建议为此提取一个方法:
// Easiest, but not thread safe
private static Random random = new Random();
private static void Shuffle<T>(T[] array)
if (array is null)
throw new ArgumentNullException(nameof(array));
for (int i = 0; i < array.Length - 1; ++i)
int r = random.Next(i, array.Length);
(array[r], array[i]) = (array[i], array[r]);
您可以通过Main
拨打电话:
Shuffle(array);
您可以使用算法fiddle。
【讨论】:
这很好。我特别喜欢你如何交换数组元素(array[r], array[i]) = (array[i], array[r]);
【参考方案2】:
有一些解决方案,但我用它来创建一个随机数组并根据它进行排序
static class Program
static void Main(string[] args)
var array = new[] "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" ;
Shuffle(ref array);
foreach (var item in array)
Console.WriteLine(item);
// Fri
// Wed
// Sat
// Thu
// Mon
// Sun
// Tue
static readonly Random rng = new Random();
public static void Shuffle<T>(ref T[] array)
double[] key = new double[array.Length];
for (int i = 0; i < key.Length; i++)
key[i] = rng.NextDouble();
Array.Sort(key, array);
另一种方法是使用以下 1 行 LINQ 语句
static void Main(string[] args)
var array = new[] "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" ;
var result = array.OrderBy((item) => rng.NextDouble()).ToArray();
【讨论】:
为什么要分配一个新数组以及为什么调用NextDouble()
而不是Next()
?在我看来,两者都效率低下。
@Enigmativity - NextDouble()
是相等概率所必需的,因为它们在[0,1)
之间具有平坦的概率分布。不幸的是,除非范围是int.MaxValue
,否则你不能这样做,这正是NextDouble()
所做的。
A double
使用 8 个字节和 int
4。生成随机 double
的成本会更高 - 并且 Next()
确实将随机值返回给 int.MaxValue
。
@Enigmativity - 你可以选择你想要的,但是你对.Next()
的限制。此外,这似乎是问题中未讨论的微优化。【参考方案3】:
最简单的方法是每次获取两个随机数(例如a,b)。然后交换 arr[a] 和 arr[b]。它的质量取决于您交换元素的次数。
【讨论】:
这不是真的。您建议的算法会引入偏差,并且不会均匀分布元素。您需要使用 Fisher-Yates 来正确执行您的建议。 见***.com/questions/67888049/bug-in-nets-random-class【参考方案4】:这种类型的改组将需要seeding the Randomn 类来调整数组的大小。并且在 for 循环中的每一次通过,它都会将当前数字与数组中的其他随机数字交换。
以下是随机播放的示例:
int[] arr = 1,2,3,4,5;
Random rand = new Random();
for(int i = 0; i < arr.Length; i++)
int shuffle = rand.Next(arr.Length);
int n = arr[i];
arr.SetValue(arr[shuffle], i);
arr.SetValue(n, shuffle);
Console.Write('[' + arr[0].ToString());
for(int i = 1; i < arr.Length; i++)
Console.Write("," + arr[i]);
Console.Write("]");
【讨论】:
您可以使用更短的代码:Console.Write($"[string.Join(", ", arr[i])]");
来提供演示输出
该算法引入了偏差。您必须使用 Fisher-Yates 才能使其正确。 shuffle
的值必须始终等于或大于循环值(在本例中为 i
)。
见***.com/questions/67888049/bug-in-nets-random-class
@Enigmativity 这对我来说是关于种子 Random 固有缺陷的新闻。感谢分享。这种偏见似乎在非常大的系列中更为普遍……而不是我们在这个年轻小伙子的硬件任务中处理的这组 5 个。 Yates 算法对我来说不够洗牌。随着集合的每一次通过,它的可用数字池会减少,直到最终 n = 集合大小并且没有随机性。该算法有效,但它的设计更多是为了在非常大的集合上节省 CPU。
@JosephTroy - 我想你误解了我的意思。您的算法是错误实现的 Fisher-Yates - 除非您更正它,否则它有偏差。您只需要一次 Fisher-Yates 即可完全随机化 - 无需多次通过。最后,Random
类的偏差与您在答案中的不正确的 Fisher-Yates 引入的偏差相比是微不足道的。以上是关于如何在 C# 中使用 Random 类对数组进行洗牌 [重复]的主要内容,如果未能解决你的问题,请参考以下文章