【问题标题】:Random array generation with no duplicates无重复的随机数组生成
【发布时间】:2014-01-11 03:42:35
【问题描述】:

我正在尝试创建一个生成没有重复值的随机数组的东西。我已经看过其他答案,但似乎没有一个可以帮助我理解。我想不出一种方法来实际生成不包含重复项的随机数。这是我迄今为止尝试过的:

srand(time(NULL));
int numbers [4];

for (int x=0; x!=4;x++)
{
    numbers[x] = 1 + (rand() % 4) ;
    printf("%d ", numbers[x]);
}

任何帮助将不胜感激。

【问题讨论】:

  • 使用std::set 并使用来自<random> 的适当分布。
  • “随机”是什么意思,就像任何可能的整数一样?一个洗牌的数组?
  • 现在是什么,随机的,还是没有重复的序列?
  • 仅供参考,duplicates 的存在并不意味着您的数组没有 random 数字。

标签: c++ c random


【解决方案1】:

你开始用从0开始的连续元素填充一个容器

std::iota(begin(vec), end(vec), 0);

然后你得到一个像样的随机数生成器并正确地播种

std::mt19937 rng(std::random_device{}());

最后你使用 rng 打乱元素

std::shuffle(begin(vec), end(vec), rng);

live on coliru


在某些实现中random_device 无法正常工作(最明显的是 Windows 上的 gcc),您必须使用替代种子,即当前时间 → chrono

【讨论】:

    【解决方案2】:

    如果您想在不维护已访问索引的情况下伪随机地遍历一个大空间,您应该查看我多年前为基本技术贡献的这个项目。 http://packetfactory.openwall.net/projects/ipspace/index.html

    您应该能够根据您的目的调整它,来源在页面底部。

    【讨论】:

      【解决方案3】:

      首先rand() 是生成随机数,但不是没有重复的。

      如果您想生成一个随机数组没有重复rand() 方法根本不起作用。

      假设您要生成一组 1000 个数字。在最好的情况下,假设您生成了前 999 个没有重复的数字,最后想到的是生成最后一个数字得到这个数字的概率是 1/1000,所以这几乎要花很长时间才能生成。在实践中,只有 10 个数字会造成很大的麻烦。

      最好的方法是通过增量(或严格单调序列)生成所有数字,即随机播放它们。在这种情况下,将有没有重复

      Here 是一个关于如何使用 10 个数字进行操作的示例。即使有 1000 个号码,它也是 working

      注意:来自Jhon Leeheyanswer 的随机播放功能。

      #include <stdio.h>
      #include <stdlib.h>
      #include <time.h>
      
      void shuffle(int *arr, size_t n)
      {
          if (n > 1) 
          {
              size_t i;
              srand(time(NULL));
              for (i = 0; i < n - 1; i++) 
              {
                size_t j = i + rand() / (RAND_MAX / (n - i) + 1);
                int t = arr[j];
                arr[j] = arr[i];
                arr[i] = t;
              }
          }
      }
      
      int main()
      {
          int i;
          int arr[10];
          for (i=0; i<10; i++){
              arr[i] = i;
          }
          shuffle(arr, 10);
          for (i=0; i<10; i++){
              printf("%d ", arr[i]);
          }
      }
      

      【讨论】:

      • 如果以下约束为真,那么您是对的:“[0, N) 中的随机值,数组大小为 N”。但如果它不是真的并且数组可以拟合大于 N 的值并且 N 远小于 rand() codomain,那么你就错了。
      • 当我说增量时,我的意思并不完全是 [0-N],它可能是一个严格的单调序列。
      • 你不能指望概率来获得如此精确的值。不同运行时的不同行为(可能需要 10 秒、...、1 小时,甚至永远)
      • 嗨@rullof 我试过你的代码,它似乎工作正常,但我想真正了解它是如何工作的,因为我不完全确定它是如何生成所有随机数的。你有没有机会给我一个更标准的解释,因为我最近开始不得不用 C 编程,而且我很挣扎。另外,你知道是否有什么方法可以让它做长度为 4 的随机数数组,但包括不超过 9 的所有整数?因为如果我将长度更改为 4,它只会打印整数 {0,1,2,3,4} 但如果它实际打印所有 9 会很有帮助。
      • @rullof 感谢您的帮助,希望能解决我的第二个疑问,Luis
      【解决方案4】:

      这个怎么样:

      #define NUMS (10)
      
      int randomSequence[NUMS] = {0}, i = 0, randomNum; 
      bool numExists[NUMS] = {false};
      
      while(i != NUMS)
      {
          randomNum = rand() % NUMS;
      
          if(numExists[randomNum] == false)
          {
              randomSequence[i++] = randomNum;
              numExists[randomNum] = true;
          }
      }
      

      当然,NUMS 越大,执行while 循环的时间就越长。

      【讨论】:

        【解决方案5】:

        生成每个随机数后,循环遍历之前的值并进行比较。如果匹配,请重新生成一个新值并重试。

        【讨论】:

          【解决方案6】:

          有两种解决方案可供选择:

          1. 使用类似 rand() 的方法生成随机数并检查重复项。

          2. 找到一个严格单调(最好严格递增)的数学序列,并将其项作为数组的成员。然后,你可以洗牌你的阵列。结果不会是真正随机的,但使用 rand() 也不会。 rand() 使用了类似的技术,这就是为什么我们需要用一些变化来设置种子,比如时间。例如,您可以使用时间来生成序列的第一个元素,并且通过良好的序列,您的结果将至少是不错的。请注意,序列必须严格单调,以避免产生重复。顺序不必太复杂。例如,如果你得到unix time modulo 10000 作为第一项,然后你使用像 x[i] = x[i-1] + 3*x[i-2] 这样的递归生成其他项应该没问题。当然,你也可以使用更复杂的序列,但要小心溢出(因为你不能对结果应用模运算符,因为它不会再增加了)和你想要的位数。

          【讨论】:

          • 我真的很喜欢这个答案。我使用了您在回答中提出的第一个解决方案。
          • 是的,这就是为什么我没有坚持那个:)。
          • @Edge7 rand() 方法根本不起作用(查看我的答案以了解原因)!
          • @rullof 我认为这个问题还不清楚。如果问题是用随机不重复值(0
          • @Edge7 是他写的代码nambers[x] = 1 + rand() 所以他绝对试图让数字 1 2 3 随机化
          【解决方案7】:
          srand(time(NULL));
          const int N = 4;
          int numbers [N];
          
          bool isAlreadyAdded(int value, int index)
          {
               for( int i = 0; i < index; i ++)
                    if( numbers[i] == value)
                        return true;
               return false;
          }
          for (int x=0; x!=N;x++)
          {
              int tmp = 1 + (rand() % N) ;
              while( x !=0 && isAlreadyAdded(tmp, x))
                     tmp = 1 + (rand() % N) ;
          
              numbers[x] = tmp;
              printf("%d ", numbers[x]);
          }
          

          这只是一种方式。它应该可以工作,当然还有更好的方法

          【讨论】:

          • 作为 C++ 程序员,我们应该avoid rand,尤其是当我们现在拥有&lt;random&gt; header
          • 你说得对,我使用 rand() 是因为我不想更改他的大部分代码,而只是说“检查您已经添加的先前值”
          【解决方案8】:

          您可以使用自己的随机数生成器,其序列大于或等于数组的长度。有关说明,请参阅http://en.wikipedia.org/wiki/Linear_congruential_generator#Period_length

          所以你需要具有表达式 Xn+1 = (aXn + c) mod m 的 LCG。值 m 必须至少与数组的长度一样大。检查最大序列长度的“当且仅当”条件,并确保您的数字满足它们。

          因此,您将能够为大多数用途生成具有令人满意的随机性的随机数,这可以保证在前 m 个调用中不会重复任何数字。

          【讨论】:

            【解决方案9】:

            在 c++ 中,您只需要:

            std::random_shuffle()
            

            http://www.cplusplus.com/reference/algorithm/random_shuffle/

            int numbers [4];
            
            for (int x=0; x!=4;x++)
            {
                numbers[x] = x;
            }
            
            std::random_shuffle(numbers, numbers +4);
            

            更新:好的,我一直在想一个合适的映射函数可以从每个索引到一个随机数,但再想一想我意识到这可能很难。以下应该有效:

                int size = 10;
                int range = 100;
            
                std::set<int> sample;
            
                while(sample.size() != size)
                    sample.insert(rand() % range); // Or whatever random source.
            
                std::vector<int> result(sample.begin(), sample.end());
            
                std::random_shuffle ( result.begin(), result.end() );
            

            【讨论】:

            • 这根本不能回答问题。
            • 这将为您提供从 0 到 3 的数字,随机打乱。如果您需要更广泛的分布,则需要使用其他方法。
            • @Rapptz 取决于对示例的阅读与问题的措辞。此外,随机洗牌可以很容易地用于生成一般情况。 OP,请说明所需的目标范围。
            • @Mgetz 因此评论是随机来源;我不认为这是有趣的地方。
            • @ĐēēpakShãrmã This article explains what is wrong with rand srand 只是种子 rand 并且在任何情况下都不是线程安全的。
            猜你喜欢
            • 2012-06-29
            • 1970-01-01
            • 1970-01-01
            • 2015-08-13
            • 2016-05-05
            • 2015-06-22
            • 2018-06-25
            相关资源
            最近更新 更多