【问题标题】:Random number generator, C++随机数生成器,C++
【发布时间】:2011-02-16 16:49:04
【问题描述】:

我知道在 C++ 中生成随机数有一些限制(可能是不统一的)。如何生成从 1 到 14620 的数字?

谢谢。

【问题讨论】:

  • 你现在有没有研究过如何在 C++ 中使用随机数?您也没有说明您是否需要更好的解决方案来在 C++ 中内置随机数,或者只是被告知如何使用 rand。
  • 您能否澄清一下特定于 c++ 的“随机数生成的限制”是什么意思?或多或少统一的序列来自更好或更差的生成器。
  • 如果 rand() 是(可以是不均匀的)那么它就没有多大用处了。
  • @thecoshman:是的,我确实查过了,但我不知道如何让它们在均匀分布的意义上真正随机。
  • @notroackstar 然后重新提出你的问题应该说明你想要真正的随机数,或者至少是非均匀分布

标签: c++ random


【解决方案1】:

如果你有一个 c++0x 环境,boost lib 的紧密派生现在是标准的:

#include <random>
#include <iostream>

int main()
{
    std::uniform_int_distribution<> d(1, 14620);
    std::mt19937 gen;
    std::cout << d(gen) << '\n';
}

这将是快速、简单和高质量的。

你没有指定,但如果你想要浮点而不是 sub in:

std::uniform_real_distribution<> d(1, 14620);

如果您需要非均匀分布,您可以非常轻松地构建自己的分段常数或分段线性分布。

【讨论】:

  • 请注意,统一的 int 分布和统一的实数分布之间存在重要区别。统一的 int 分布在 closed range [1,14620] 中返回数字,而统一的实数分布在 half-open range [1,14620) 中返回数字。
  • 也可以使用 C++0x-esque 种子,std::random_device r; std::mt19937 gen(r());
  • 如果你没有 C++0x,boost::random 可能应该是你的标准,除非你真的只需要快速和肮脏的东西......
【解决方案2】:

一种常见的方法是将std::rand() 与模数结合使用:

#include<cstdlib>
#include<ctime>

// ...
std::srand(std::time(0));  // needed once per program run
int r = std::rand() % 14620 + 1;

但是,正如@tenfour 在他的回答中提到的那样,模运算符可能会破坏 std::rand() 返回值的一致性。这是因为模将它丢弃的值转换为有效值,并且这种转换可能不统一。例如,对于[0, 10) 中的n,值n % 99 转换为0,因此您可以通过真零或转换为零的9 来获得零。其他值都只有一次让步的机会。

另一种方法是将随机数从std::rand() 转换为[0, 1) 范围内的浮点值,然后将该值转换并移动到您想要的范围内。

int r = static_cast<double>(std::rand()) / RAND_MAX * 14620) + 1;

【讨论】:

    【解决方案3】:

    srand() / rand() 是您需要的功能,正如其他人已经回答的那样。

    % 的问题在于结果明显不一致。为了说明,假设rand() 返回的范围是 0-3。以下是调用 4000 次的假设结果:

    0 - 1000 times
    1 - 1000 times
    2 - 1000 times
    3 - 1000 times
    

    现在,如果您对 (rand() % 3) 进行相同的抽样,您会注意到结果如下:

    0 - 2000 times
    1 - 1000 times
    2 - 1000 times
    

    哎哟!更统一的解决方案是这样的:

    int n = (int)(((((double)std::rand()) / RAND_MAX) * 14620) + 1);

    对不起,草率的代码,但我们的想法是使用浮点数学将其适当地缩小到您想要的范围,然后转换为整数。

    【讨论】:

    • '(((double)std::rand()) / RAND_MAX)' - 你能在这里解释一下这部分吗?你是在生成随机数,然后按 RAND_MAX 缩放它吗?
    • 除以RAND_MAX,您将生成一个介于 0.0 和 1.0 之间的统一随机数。这使得缩放到您想要的任何范围变得简单。在你的情况下,1-14620。
    • 我不明白您为什么要消除中间 double;最终结果是您想要的int。除了按照其他人的建议使用 % 之外,我不知道只有整数的答案。
    • 很好地使用转换来保持尽可能高的分辨率,但希望看到使用 C++ 转换。
    【解决方案4】:

    使用rand

    ( rand() % 100 ) is in the range 0 to 99
    ( rand() % 100 + 1 ) is in the range 1 to 100
    ( rand() % 30 + 1985 ) is in the range 1985 to 2014
    
    ( rand() % 14620 + 1 ) is in the range 1 to 14620
    

    编辑:

    如链接中所述,随机发生器应在使用前使用srand 播种。一个常见的独特值是调用time 的结果。

    【讨论】:

    • 您遗漏了重要的附录:请注意,尽管此模运算不会在 span 中生成真正均匀分布的随机数
    • 对,不是没有播种。该链接在页面底部提到了播种。我会更新以包含此内容。
    • @James,@meagar,如何生成真正均匀分布的随机数?我必须在算法的一个循环内生成 30,000 个。
    • @meager 我不得不承认我不知道这一点,而且我还不得不承认我不明白为什么——至少现在不明白。
    • @sstn: en.wikipedia.org/wiki/Pseudorandom_number_generator 有一些关于为什么需要种子的信息。
    【解决方案5】:

    如前所述,您可以使用rand()。例如

    int n = rand() % 14620 + 1;

    完成了这项工作,但它是不统一的。 这意味着某些值(低值)会稍微更频繁地出现。这是因为 rand() 产生的值在 0 到 RAND_MAX 的范围内,而 RAND_MAX 通常不能被 14620 整除。例如如果RAND_MAX == 15000,那么数字1 的可能性是数字1000 的两倍,因为rand() == 0rand() == 14620 都产生n==1,但只有rand()==999 使n==1000 为真。

    但是,如果 14620 远小于 RAND_MAX,则这种影响可以忽略不计。在我的计算机上,RAND_MAX 等于 2147483647。如果rand() 产生介于 0 和 RAND_MAX 之间的均匀样本,那么,因为 2147483647 % 14620 = 10327 和 2147483647 / 14620 = 146886,n 平均介于 1 到 1468 次之间而如果您抽取 2147483647 个样本,则 10329 和 14620 之间的数字平均会出现 146886 次。 如果你问我,差别不大。

    但是,如果RAND_MAX == 15000 会产生如上所述的不同。 在这种情况下,建议使用一些较早的帖子

    int n = (int)(((((double)std::rand()) / RAND_MAX) * 14620) + 1);

    使其“更统一”。 请注意,这只会更改更频繁出现的数字,因为rand() 仍然返回“仅”RAND_MAX 不同的值。 为了使其真正统一,如果它在 14620*int(RAND_MAX/14620) 和 RAND_MAX 之间的范围内,则必须拒绝任何整数形式 rand(),然后再次调用 rand()。 在RAND_MAX == 15000 的示例中,您将拒绝介于 14620 和 15000 之间的任何 rand() 值并再次绘制。 对于大多数应用程序,这不是必需的。我会更担心rand() 的随机性。

    【讨论】:

      【解决方案6】:
      【解决方案7】:

      rand() 函数并不是最好的随机生成器,更好的方法是使用 CryptGenRandom()。

      这个例子应该可以解决问题:

      #include <Windows.h>
      
      // Random-Generator
      HCRYPTPROV hProv;
      INT Random() {
          if (hProv == NULL) {
              if (!CryptAcquireContext(&hProv, NULL, NULL, PROV_RSA_FULL, CRYPT_SILENT | CRYPT_VERIFYCONTEXT))
                  ExitProcess(EXIT_FAILURE);
          }
      
          int out;
          CryptGenRandom(hProv, sizeof(out), (BYTE *)(&out));
          return out & 0x7fffffff;
      }
      
      int main() {
          int ri = Random() % 14620 + 1;
      }
      

      【讨论】:

        【解决方案8】:

        模数运算符是最重要的,你可以用这个模数限制,检查一下:

        // random numbers generation in C++ using builtin functions
        #include <iostream>
        
        using namespace std;
        
        #include <iomanip>
        
        using std::setw;
        
        #include <cstdlib>   // contains function prototype for rand
        
        int main()
        {
        // loop 20 times
        for ( int counter = 1; counter <= 20; counter++ ) {
        
            // pick random number from 1 to 6 and output it
            cout << setw( 10 ) << ( 1 + rand() % 6 );
        
            // if counter divisible by 5, begin new line of output
            if ( counter % 5 == 0 )
                cout << endl;
        
        }
        
        return 0;  // indicates successful termination
        
        } // end main
        

        【讨论】:

          猜你喜欢
          • 2011-01-24
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2012-09-24
          • 1970-01-01
          • 2016-08-15
          • 2017-04-10
          • 1970-01-01
          相关资源
          最近更新 更多