生成随机的唯一值 C#
Posted
技术标签:
【中文标题】生成随机的唯一值 C#【英文标题】:Generating random, unique values C# 【发布时间】:2013-01-06 13:19:05 【问题描述】:我已经搜索了一段时间并且一直在努力寻找这个,我正在尝试生成几个随机的、唯一的数字是 C#。我正在使用System.Random
,我正在使用DateTime.Now.Ticks
种子:
public Random a = new Random(DateTime.Now.Ticks.GetHashCode());
private void NewNumber()
MyNumber = a.Next(0, 10);
我经常打电话给NewNumber()
,但问题是我经常收到重复的号码。有人建议,因为我每次都声明随机数,它不会产生随机数,所以我把声明放在我的函数之外。有什么建议或比使用 System.Random
更好的方法吗?谢谢
【问题讨论】:
csharpindepth.com/Articles/Chapter12/Random.aspx 只要你只创建一次 Random 对象,你应该没有问题。如果您希望数字是唯一的(还没有那个数字),那么您需要添加额外的内容,而不仅仅是使用 Random 您是否在寻找“数字 1..10 的排列”而不是“1..10 范围内的随机数”? (绝对给你10个随机序列的唯一数字) 为什么不生成guid?你需要 int 还是任何唯一的字符串就足够了? 在生成所有十个唯一值之后,您希望您的代码生成什么?第 11 个值(共 10 个)应该是多少? 【参考方案1】:我经常调用 NewNumber(),但问题是我经常遇到 重复的数字。
Random.Next
不保证号码是唯一的。此外,您的范围是从 0 到 10,您可能会得到重复的值。可能您可以设置int
的列表,并在检查它是否不包含重复项后在列表中插入随机数。比如:
public Random a = new Random(); // replace from new Random(DateTime.Now.Ticks.GetHashCode());
// Since similar code is done in default constructor internally
public List<int> randomList = new List<int>();
int MyNumber = 0;
private void NewNumber()
MyNumber = a.Next(0, 10);
if (!randomList.Contains(MyNumber))
randomList.Add(MyNumber);
【讨论】:
+1。同样对于超过 10 个列表的任何内容都是不好的选择,HashSet 会更好。并且没有必要以这种方式初始化随机 - 无论如何在默认构造函数中完成了类似的代码...... 算法复杂度不好。这不应该被接受为答案。 这不是一个随机列表,您检查是否有重复,但现实世界中是否可能发生重复! 我认为解决方案对接受的范围和所选数字的数量很重要。如果选择的数字量很大,那么改组 Enumerable.Range.ToArray() 效果最好,而如果你从大范围中选择少量数字,那么 HashSet 是有意义的。 真的吗?这是一个非常糟糕的算法。不要使用它!【参考方案2】:如果您的范围仅为 0 到 9,您可以尝试改组可能的整数数组。这增加了避免数字生成中任何冲突的好处。
var nums = Enumerable.Range(0, 10).ToArray();
var rnd = new Random();
// Shuffle the array
for (int i = 0;i < nums.Length;++i)
int randomIndex = rnd.Next(nums.Length);
int temp = nums[randomIndex];
nums[randomIndex] = nums[i];
nums[i] = temp;
// Now your array is randomized and you can simply print them in order
for (int i = 0;i < nums.Length;++i)
Console.WriteLine(nums[i]);
【讨论】:
我刚刚测试了一个,它也很好用!非常感谢! 当心!那是不正确的随机播放实现!稍后我会发布一个正确的实现。 (现在编辑我上面的评论为时已晚)。请参阅下面我的帖子以了解正确的实现,以及有关它的一些讨论的链接。 为什么不只是Enumerable.Range(0, 10).OrderBy(x => rnd.NextDouble()).ToArray();
?
@thepirat000 唯一真正的原因是速度。对于 10 个元素,它显然没有什么区别,但对于大型集合来说,它会有所不同。OrderBy() 可能是一个 O(N log N) 算法,其中 shuffle 是 O(N)。【参考方案3】:
注意,我不推荐这个:)。 这里还有一个“oneliner”:
var result = Enumerable.Range(0,9).OrderBy(g => Guid.NewGuid()).ToArray();
【讨论】:
就像你去过的地方一样。但为什么不:Enumerable.Range(0, 9).OrderBy(g => rand.NextDouble()).ToList()
然后你会根据问题得到范围。
如果您想要两个介于 1 和 10,000,000 之间的唯一数字,这将非常缓慢。【参考方案4】:
我发布了一个正确的随机播放算法实现,因为此处发布的另一个不会产生统一的随机播放。
正如另一个答案所述,对于要随机化的少量值,您可以简单地用这些值填充一个数组,对数组进行洗牌,然后使用您想要的任意数量的值。
以下是Fisher-Yates Shuffle(又名 Knuth Shuffle)的实现。 (阅读该链接的“实现错误”部分(搜索“每次迭代时始终从有效数组索引的整个范围中选择 j”)以查看有关此处发布的其他实现有什么问题的一些讨论。)
using System;
using System.Collections.Generic;
namespace ConsoleApplication2
static class Program
static void Main(string[] args)
Shuffler shuffler = new Shuffler();
List<int> list = new List<int> 1, 2, 3, 4, 5, 6, 7, 8, 9 ;
shuffler.Shuffle(list);
foreach (int value in list)
Console.WriteLine(value);
/// <summary>Used to shuffle collections.</summary>
public class Shuffler
public Shuffler()
_rng = new Random();
/// <summary>Shuffles the specified array.</summary>
/// <typeparam name="T">The type of the array elements.</typeparam>
/// <param name="array">The array to shuffle.</param>
public void Shuffle<T>(IList<T> array)
for (int n = array.Count; n > 1; )
int k = _rng.Next(n);
--n;
T temp = array[n];
array[n] = array[k];
array[k] = temp;
private System.Random _rng;
【讨论】:
@downvoters:你调查过其他随机洗牌的问题吗?有问题的答案是低于已接受答案的答案。它使用了不正确的随机播放算法。另请参阅我对那个答案的 cmets。 为什么这个比另一个好? (除了你让它通用) @Mitulátbáti 您所说的“另一个”是指哪个答案?如果您的意思是“接受的答案”,那么这个更好,因为它具有复杂性O(N)
,而接受的答案具有复杂性O(N^2)
。
即使您多次将其称为“另一个”。我的意思是那个。
@Mitulátbáti 我特别提到了另一种洗牌算法,所以假设这确实是你的意思,那么这个答案比那个更好,因为这个产生了一个统一的洗牌 - 另一种洗牌算法(有只有另一种随机播放算法作为答案发布)有错误并产生非均匀随机播放。请注意,我在此处的回答已经提供了指向 Wikipedia 文章的链接,其中包含有关其他 shuffle 算法为何被破坏的一些详细信息。【参考方案5】:
这是一个唯一的答案:
检查这个现成的方法:给出你想获得的数字的范围和数量。
public static int[] getUniqueRandomArray(int min, int max, int count)
int[] result = new int[count];
List<int> numbersInOrder = new List<int>();
for (var x = min; x < max; x++)
numbersInOrder.Add(x);
for (var x = 0; x < count; x++)
var randomIndex = UnityEngine.Random.Range(0, numbersInOrder.Count);
result[x] = numbersInOrder[randomIndex];
numbersInOrder.RemoveAt(randomIndex);
return result;
【讨论】:
嗯 UnityEngine.Random 与 C# 等效项有点不同。当然,您可以毫不费力地转换为 C#。【参考方案6】:与@Habib's answer 相同,但作为一个函数:
List<int> randomList = new List<int>();
int UniqueRandomInt(int min, int max)
var rand = new Random();
int myNumber;
do
myNumber = rand.Next(min, max);
while (randomList.Contains(myNumber));
return myNumber;
如果 randomList 是类属性,UniqueRandomInt 将在该类的同一实例的上下文中返回唯一整数。如果您希望它在全球范围内唯一,则需要将 randomList 设为静态。
【讨论】:
【参考方案7】:根据你的真实情况,你可以做这样的事情:
using System;
using System.Collections.Generic;
using System.Linq;
namespace SO14473321
class Program
static void Main()
UniqueRandom u = new UniqueRandom(Enumerable.Range(1,10));
for (int i = 0; i < 10; i++)
Console.Write("0 ",u.Next());
class UniqueRandom
private readonly List<int> _currentList;
private readonly Random _random = new Random();
public UniqueRandom(IEnumerable<int> seed)
_currentList = new List<int>(seed);
public int Next()
if (_currentList.Count == 0)
throw new ApplicationException("No more numbers");
int i = _random.Next(_currentList.Count);
int result = _currentList[i];
_currentList.RemoveAt(i);
return result;
【讨论】:
【参考方案8】:这里是我使用 HashSet 查找 N 个随机唯一数字的版本。 看起来很简单,因为 HashSet 只能包含不同的项目。 很有趣 - 会比使用 List 或 Shuffler 更快吗?
using System;
using System.Collections.Generic;
namespace ConsoleApplication1
class RnDHash
static void Main()
HashSet<int> rndIndexes = new HashSet<int>();
Random rng = new Random();
int maxNumber;
Console.Write("Please input Max number: ");
maxNumber = int.Parse(Console.ReadLine());
int iter = 0;
while (rndIndexes.Count != maxNumber)
int index = rng.Next(maxNumber);
rndIndexes.Add(index);
iter++;
Console.WriteLine("Random numbers were found in 0 iterations: ", iter);
foreach (int num in rndIndexes)
Console.WriteLine(num);
Console.ReadKey();
【讨论】:
这是最好的答案。这是 HashSet 的完美用例【参考方案9】:我注意到接受的答案不断将 int 添加到列表中,并不断使用 if (!randomList.Contains(MyNumber))
检查它们,我认为这不能很好地扩展,特别是如果你一直要求新数字。
我会做相反的事情。
-
在启动时线性生成列表
从列表中获取随机索引
从列表中删除找到的 int
这在启动时需要更多的时间,但会更好地扩展。
public class RandomIntGenerator
public Random a = new Random();
private List<int> _validNumbers;
private RandomIntGenerator(int desiredAmount, int start = 0)
_validNumbers = new List<int>();
for (int i = 0; i < desiredAmount; i++)
_validNumbers.Add(i + start);
private int GetRandomInt()
if (_validNumbers.Count == 0)
//you could throw an exception here
return -1;
else
var nextIndex = a.Next(0, _validNumbers.Count - 1);
var number = _validNumbers[nextIndex];
_validNumbers.RemoveAt(nextIndex);
return number;
【讨论】:
但是从列表中删除一个元素不会花费 O(n) 时间吗?索引方法是一个不错的方法,但是从列表中删除一个元素(基于 .NET 中的数组构建)会消耗时间,尤其是当列表非常大时! 是的。你说得对。在那种情况下,我只是更早地而不是更晚地移动了消费任务。 RemoveAt 的成本要早得多,而后要少得多,而 Contains 则相反(而且它在内存方面也更好)。我需要对此进行优化。 我很高兴看到您的优化方法。感谢分享!【参考方案10】:可能有点晚了,但这里有更合适的代码,例如当你需要使用循环时:
List<int> genered = new List<int>();
Random rnd = new Random();
for(int x = 0; x < files.Length; x++)
int value = rnd.Next(0, files.Length - 1);
while (genered.Contains(value))
value = rnd.Next(0, files.Length - 1);
genered.Add(value);
returnFiles[x] = files[value];
【讨论】:
【参考方案11】: 用函数式方式* static Func<int> GetNextUniqueIntegerFunc(int min, int max)
var list = new List<int>();
var random = new Random();
int getNextValue()
while (true)
var random_number = random.Next(min, max);
if (!list.Contains(random_number))
list.Add(random_number);
return random_number;
return getNextValue;
【讨论】:
用函数方式【参考方案12】:从 0 到 9 的唯一随机数
int sum = 0;
int[] hue = new int[10];
for (int i = 0; i < 10; i++)
int m;
do
m = rand.Next(0, 10);
while (hue.Contains(m) && sum != 45);
if (!hue.Contains(m))
hue[i] = m;
sum = sum + m;
【讨论】:
【参考方案13】:randomNumber 函数返回 0 到 100000 之间的唯一整数值
bool check[] = new bool[100001]; Random r = new Random(); public int randomNumber() int num = r.Next(0,100000); while(check[num] == true) num = r.Next(0,100000); check[num] = true; return num;
【讨论】:
【参考方案14】:您还可以使用存储每个随机值的 dataTable,然后在 dataColumn 中使用 != 值时简单地执行 random 方法
【讨论】:
【参考方案15】:嗨,我在这里发布了一个视频,它解释了如何生成唯一的随机数
public List<int> random_generator()
Random random = new Random();
List<int> random_container = new List<int>;
do
int random_number = random.next(10);
if(!random_container.contains(random_number)
random_container.add(random_number)
while(random_container.count!=10);
return random_container;
在这里,,,在随机容器中,您将随机获得从 0 到 9(10 个数字)开始的非重复 10 个数字.. 谢谢........
【讨论】:
这不能回答System.Random
上的问题。【参考方案16】:
您可以使用 C# 的基本随机函数
Random ran = new Random();
int randomno = ran.Next(0,100);
您现在可以在任何您想要的地方使用 randomno 中的值,但请记住,这将在 0
和 100
之间生成一个随机数,并且您可以将其扩展到任何数字。
【讨论】:
【参考方案17】:试试这个:
private void NewNumber()
Random a = new Random(Guid.newGuid().GetHashCode());
MyNumber = a.Next(0, 10);
一些解释:
Guid
: base on here : 表示全局唯一标识符 (GUID)
Guid.newGuid()
产生一个唯一标识符,如 "936DA01F-9ABD-4d9d-80C7-02AF85C822A8"
它将在整个宇宙中独一无二base on here
哈希码 here 从我们的唯一标识符生成一个唯一整数
所以Guid.newGuid().GetHashCode()
给了我们一个唯一的数字,随机类将产生真正的随机数抛出这个
示例: https://rextester.com/ODOXS63244
用这种方法生成十个随机数,结果为:
-1541116401
7
-1936409663
3
-804754459
8
1403945863
3
1287118327
1
2112146189
1
1461188435
9
-752742620
4
-175247185
4
1666734552
7
我们有两个相邻的1
s,但哈希码不一样。
【讨论】:
请补充说明。 @Rob 是的,这会产生一个唯一的值,你测试了吗? 不,不会的。完全没有理由两次连续调用不会产生相同的值。为每个样本重新播种是一种经典的反模式。 Guid 是 128 位数字,而 int 是 32 位。所以 将 发生两个不同的 Guid 具有相同的哈希码。加上 Random 的两个不同种子值可能会导致相同的第一个值,尤其是在非常有限的范围内,例如 0-10。 @HansKesting 嗨,基于此 msdn.microsoft.com/en-us/library/…GetHashCode
返回基于参数对象的 int32 值以上是关于生成随机的唯一值 C#的主要内容,如果未能解决你的问题,请参考以下文章