【问题标题】:How can I generate a random number from 1 - 39 without making a number overlap? [duplicate]如何在不重叠的情况下生成 1 - 39 的随机数? [复制]
【发布时间】:2014-08-29 02:00:32
【问题描述】:
#include <iostream>
#include <stdlib.h>
#include <time.h>
using namespace std;

void randnum()
{
    int random;
    srand((unsigned int)time(0));
    for(int i=1;i<=5;i++)
    {
        random=(rand()%39)+1;
        cout<<random<<endl;
    }
}

int main()
{
    cout<<"Five random number is here"<<endl;
    randnum();
    system ("PAUSE");
    return 0;
}

我随机这样做是为了练习 C++。我总是在设置随机发生器的范围时感到困惑(我的方法正确吗?从 1 到 39)。另外,如何防止数字相互重叠?也就是说,如果我从 1-39 输出 5 个不同的数字,它总是可以 5 个不同的数字,例如 4,5,2,7,12 而不是 4,5,2,4,12(4 在这里使用了两次)

【问题讨论】:

  • 如果您使用的是C++,请使用标准库。见en.cppreference.com/w/cpp/numeric/random/…
  • 如果您想从 1 到 39 中选择 5 个数字,最好将所有这些数字添加到一个向量中,将其打乱,然后取前 5 个。
  • 当你这样做时,请记住 std::iotastd::shuffle
  • @Ninja - 我认为这是一个与计算[a,b] 范围内的元素及其排列有关的初学者 C/C++ 问题。我不认为在这种情况下引用的 dup 是等效的,因为它谈论的是渐近分析和执行相同操作的算法的运行时间之类的事情。 (如果海报更高级,那就另当别论了)。我投票决定让他保持开放,这样他就可以得到一些关于在[a,b] 范围内计数、排列和没有重复的简单答案。 (并且在某个地方可能有更合适的副本)。
  • This answer 展示了如何对一组数字进行洗牌 - 然后你可以取前 5 个或其他数字。或者,如果您选择一小部分潜在值,则将每个选择与已选择的值进行比较会更快。

标签: c++ shuffle visual-c++-2010


【解决方案1】:

是的,获取1到39之间随机数的方法是正确的。

为确保数字不重叠,我想到了两种算法:

  1. 保留一组已经发过的号码,并在第二次被选中时跳过,或者
  2. 创建所有候选编号的列表并随机重新排序,然后按顺序提供它们

【讨论】:

    【解决方案2】:

    方法是正确的。为了防止数字相互重叠,对我来说最好的解决方案是创建一个已生成数字的向量,每次生成一个新的随机数时,看看它是否已经在向量中。如果是,请重新生成。如果不是,则将其添加到向量中并继续。

    【讨论】:

    • 随着您生成更多数字,这将逐渐变慢,因为随着“坏”结果的数量增加,它必须重试更多。对于特定情况可能没问题,但不能很好地推广到更大的数字。
    • @CraigMcQueen:如果这是个问题,可以在bitsetsetunordered_set 中跟踪已使用的值,但一般方法仍然有效。 +1
    • @TonyD 这将加快检查生成的号码是否已被使用。但是随着“已使用”值的数量增加,您仍然会遇到越来越多地重试随机生成的问题。
    • @CraigMcQueen:使用哈希集方法的位集,重要的是已使用数字与未使用数字的比率,问题中有什么暗示a)big-O效率是有任何相关性,并且 b) 通缉人数与总人数的比例将永远超过 5:39?有时,针对有限情况的最佳解决方案并不是针对一般情况的最佳解决方案。
    【解决方案3】:

    尝试创建一个包含数字 1 -39 的向量,将它们打乱,然后选择前 5 个。然后你就有 5 个不重复的随机数。

    random_shuffle() 函数在 C++ 库中实现。在这里检查: http://www.cplusplus.com/reference/algorithm/random_shuffle/

    【讨论】:

      【解决方案4】:

      随机=(rand()%39)+1;

      这可能会导致重复。


      我总是在设置随机发生器的范围时感到困惑(我的方法在 1-39 之间是否正确?)

      [begin,end](括号表示“包含”)范围内的元素数为:

       count = end - begin + 1
      

      如果您需要基于 0 的 count 元素之一,则执行:

      rand() % count
      

      因为起始元素可能为0,所以你实际上执行以下操作来获取范围内的值:

      rand() % count + begin
      

      还有如何防止数字相互重叠?

      在这种情况下,更简单的解决方案之一是使用向量。它不如其他答案有效(就像@Retired Ninja 建议的那样),但它更容易理解。如下所示。

      下面的代码只是转储了随机播放的结果(这不是随机的,因为它根据使用的seed 在运行中重复)。你应该不难适应前 5 个元素(我们不能给你所有的答案)。

      ShuffledRange range (1, 39);
      ...
      
      $ ./tt.exe 
      29 33 8 37 9 32 38 24 16 14 36 7 10 31 34 39 27 11 6 4 35 1 19 20 18 15 5 12 22
      21 3 30 17 25 2 28 23 26 13 
      

      如果你指定一个seed(默认是0),那么你会得到一个不同的序列:

      ShuffledRange range (1, 39, 2);
      ...
      
      $ ./tt.exe 
      12 20 28 6 7 15 32 17 35 11 18 31 27 4 23 36 25 24 22 1 33 2 37 39 21 9 38 13 5 3
      14 10 8 34 16 19 29 26 30
      

      因为random_shuffle,下面的代码需要C++ 11。 Visual Studio 2012 应该适用于 C++ 11。我不确定 Visual Studio 2010。

      GCC 需要:

      $ g++ -Wall -Wextra -std=c++11 tt.cpp -o tt.exe
      

      Mac OS X:

      $ g++ -Wall -Wextra -std=c++11 -stdlib=libc++ tt.cpp -o tt.exe
      

      class ShuffledRange
      {
      public:
      
          explicit ShuffledRange(unsigned int low, unsigned int high, int seed=0)
            : m_numbers(move(create_numbers(low,high,seed))), m_it(m_numbers.begin()) { }
      
          unsigned int GetCount() const {
              return static_cast<unsigned int>(m_numbers.size());
          }
      
          bool HasNext() const {
              return m_it != m_numbers.end();
          }
      
          unsigned int GetNext()
          {
              if(!HasNext())
                  throw std::runtime_error("No numbers left");
      
              unsigned int temp = *m_it++;
              return temp;
          }
      
      protected:                
      
          vector<unsigned int> create_numbers(unsigned int low, unsigned int high, int seed)
          {
              if(high < low)
                  throw std::runtime_error("Bad range of elements");
      
              vector<unsigned int> temp;
              temp.reserve(high - low + 1);
      
              for(unsigned int i = low; i <= high; i++)
                  temp.push_back(i);
      
              srand(seed);
              random_shuffle(temp.begin(), temp.end());
      
              return temp;
          }
      
      private:
      
          vector<unsigned int> m_numbers;
          vector<unsigned int>::iterator m_it;
      };
      
      int main(int argc, char* argv[])
      {
          ShuffledRange range(1, 39);
      
          while(range.HasNext())
              cout << range.GetNext() << " ";
      
          cout << endl;
      
          return 0;
      }
      

      提示....

      int main()
      {
          cout<<"Five random number is here"<<endl;
          randnum();
          system ("PAUSE");
          return 0;
      }
      

      如果您在main 的右大括号(即})上放置一个断点(F9),那么您将不需要system ("PAUSE");。 Visual Studio 将中断并等待您。检查完这些值后,按 F5 完成程序。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2019-10-12
        • 2012-11-06
        • 1970-01-01
        • 2014-05-01
        • 2012-10-23
        • 1970-01-01
        • 1970-01-01
        • 2011-11-28
        相关资源
        最近更新 更多