使用C#在给定范围内生成N个唯一数字的有效方法是什么?例如,在1到50之间生成6个唯一数字。懒惰的方法是在循环中简单地使用Random.Next()
并将该数字存储在数组/列表中,然后重复并检查它是否已存在等。有没有更好的方法来生成一组随机但唯一的数字?
要添加更多上下文,我想使用它们的索引从集合中选择N个随机项。
感谢
答案 0 :(得分:13)
获取50个元素的数组:{1, 2, 3, .... 50}
使用随机混洗阵列的任何标准算法对阵列进行混洗。修改后的数组的前六个元素就是您要寻找的。 HTH
答案 1 :(得分:10)
对于6比6,我不太确定我是否担心效率,因为重复的可能性相对较低(总体来说,30%来自我的背后计算)。你可以很容易地记住你生成的以前的数字并扔掉它们,比如(伪代码):
n[0] = rnd(50)
for each i in 1..5:
n[i] = n[0]
while n[1] == n[0]:
n[1] = rnd(50)
while n[2] == any of (n[0], n[1]):
n[2] = rnd(50)
while n[3] == any of (n[0], n[1], n[2]):
n[3] = rnd(50)
while n[4] == any of (n[0], n[1], n[2], n[3]):
n[4] = rnd(50)
while n[5] == any of (n[0], n[1], n[2], n[3], n[4]):
n[5] = rnd(50)
然而,当你从6点从50点移动到48点48点,或者从6点6点移动时会出现这种情况,因为重复数据开始变得更容易远。那是因为可用数字池变得越来越小,你最终会丢掉越来越多。
对于一个非常有效的解决方案,它为您提供了值的子集,其中零重复的可能性(并且没有不必要的前期排序),Fisher-Yates是可行的方法。
dim n[50] // gives n[0] through n[9]
for each i in 0..49:
n[i] = i // initialise them to their indexes
nsize = 50 // starting pool size
do 6 times:
i = rnd(nsize) // give a number between 0 and nsize-1
print n[i]
nsize = nsize - 1 // these two lines effectively remove the used number
n[i] = n[nsize]
只需从池中选择一个随机数,将其替换为该池中的顶部数字,然后减小池的大小,就可以获得一个随机播放,而无需担心前面的大量交换。
如果数字很高,这很重要,因为它不会引入不必要的启动延迟。
例如,检查以下基准检查,选择10比10:
<------ n[] ------>
0 1 2 3 4 5 6 7 8 9 nsize rnd(nsize) output
------------------- ----- ---------- ------
0 1 2 3 4 5 6 7 8 9 10 4 4
0 1 2 3 9 5 6 7 8 9 7 7
0 1 2 3 9 5 6 8 8 2 2
0 1 8 3 9 5 6 7 6 6
0 1 8 3 9 5 6 0 0
5 1 8 3 9 5 2 8
5 1 9 3 4 1 1
5 3 9 3 0 5
9 3 2 1 3
9 1 0 9
你可以看到游泳池减少了,因为你总是用未使用过的游泳池替换旧游泳池,你将永远不会重复。
将从其返回的结果作为索引添加到您的集合中将保证不会选择重复的项目。
答案 2 :(得分:6)
对于大量唯一数字,请将它们放在List ..
中 Random random = new Random();
List<int> uniqueInts = new List<int>(10000);
List<int> ranInts = new List<int>(500);
for (int i = 1; i < 10000; i++) { uniqueInts.Add(i); }
for (int i = 1; i < 500; i++)
{
int index = random.Next(uniqueInts.Count) + 1;
ranInts.Add(uniqueInts[index]);
uniqueInts.RemoveAt(index);
}
然后从1到myInts.Count随机生成一个数字。存储myInt
值并将其从列表中删除。无需对列表进行洗牌,也不需要查看该值是否已存在。
答案 3 :(得分:5)
var random = new Random();
var intArray = Enumerable.Range(0, 4).OrderBy(t => random.Next()).ToArray();
此数组将包含0到4之间的5个随机数。
或
var intArray = Enumerable.Range(0, 10).OrderBy(t => random.Next()).Take(5).ToArray();
此数组将包含0到10之间的5个随机数。
int firstNumber = intArray[0];
int secondNumber = intArray[1];
int thirdNumber = intArray[2];
int fourthNumber = intArray[3];
int fifthNumber = intArray[4];
答案 4 :(得分:1)
而不是使用List
使用Dictionary
!!
答案 5 :(得分:0)
如果它对其他人有帮助,我更喜欢分配必要的最少数量的物品。下面,我使用HashSet,确保新项目是唯一的。这应该适用于非常大的集合,直到HashSet可以使用的极限。
public static IEnumerable<int> GetRandomNumbers(int numValues, int maxVal)
{
var rand = new Random();
var yieldedValues = new HashSet<int>();
int counter = 0;
while (counter < numValues)
{
var r = rand.Next(maxVal);
if (yieldedValues.Add(r))
{
counter++;
yield return r;
}
}
}
答案 6 :(得分:-1)
从1到40生成唯一的随机数:
输出确认:
class Program
{
static int[] a = new int[40];
static Random r = new Random();
static bool b;
static void Main(string[] args)
{
int t;
for (int i = 0; i < 20; i++)
{
lab: t = r.Next(1, 40);
for(int j=0;j<20;j++)
{
if (a[j] == t)
{
goto lab;
}
}
a[i] = t;
Console.WriteLine(a[i]);
}
Console.Read();
}
}
示例输出:
7 38 14 18 13 29 28 26 22 8 24 19 35 39 33 32 20 2 15 37