生成随机的唯一值 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 =&gt; 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 =&gt; 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 中的值,但请记住,这将在 0100 之间生成一个随机数,并且您可以将其扩展到任何数字。

【讨论】:

【参考方案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

我们有两个相邻的1s,但哈希码不一样。

【讨论】:

请补充说明。 @Rob 是的,这会产生一个唯一的值,你测试了吗? 不,不会的。完全没有理由两次连续调用不会产生相同的值。为每个样本重新播种是一种经典的反模式。 Guid 是 128 位数字,而 int 是 32 位。所以 发生两个不同的 Guid 具有相同的哈希码。加上 Random 的两个不同种子值可能会导致相同的第一个值,尤其是在非常有限的范围内,例如 0-10。 @HansKesting 嗨,基于此 msdn.microsoft.com/en-us/library/… GetHashCode 返回基于参数对象的 int32 值

以上是关于生成随机的唯一值 C#的主要内容,如果未能解决你的问题,请参考以下文章

如何使用 C# 生成随机并唯一的数字?

如何为字段的每个唯一值生成随机记录?

如何让C#产生不重复的随机数

如何在laravel中为每个用户生成唯一的随机值并将其添加到数据库中

C# Random 生成不重复随机数

生成唯一且随机的整数