【问题标题】:generate random double numbers in c++在 C++ 中生成随机双精度数
【发布时间】:2011-02-11 20:51:05
【问题描述】:

如何在 c++ 中生成两个双精度数之间的随机数,这些数字应该看起来像 xxxxx,yyyyy。

【问题讨论】:

  • "这些数字应该看起来像 xxxxx,yyyyy"。如何生成随机双打,以及如何将双打格式化为字符串,是完全不同的问题。
  • 然后换一种方式考虑:生成均匀分布的 doubles 和生成均匀分布的 decimals 是有些不同的任务,尽管相关。
  • 生成均匀分布的整数与小数问题的关系更为密切。

标签: c++ random


【解决方案1】:

方法如下

double fRand(double fMin, double fMax)
{
    double f = (double)rand() / RAND_MAX;
    return fMin + f * (fMax - fMin);
}

记得在每次程序启动时使用适当的种子调用 srand()。

[编辑] 这个答案已经过时了,因为 C++ 得到了它的原生非 C 随机库(参见 Alessandro Jacopsons 的答案) 但是,这仍然适用于 C

【讨论】:

  • 如果将 RAND_MAX 加 1,请谨慎操作,因为它可能等于 INT_MAX。 double f = rand() / (RAND_MAX + 1.0);
  • 请注意,这可以限制随机性。范围 xxxxx,yyyyy 建议 10 个十进制数字。有很多 RAND_MAX 小于 10^10 的系统。这意味着该范围内的某些数字具有p(xxxxx,yyyyy)==0.0
  • 尽可能避免使用 rand()。请参阅 C++11 或 TR1 解决方案的其他答案。
  • 不是每个人都使用 C++0x、tr1 或 C++11 是否有任何参考表明 rand() 是要避免的?几十年来,它一直是获取随机数的唯一方法。
  • @ChamilaWijayarathna,您需要包含 cstdlib
【解决方案2】:

此解决方案需要 C++11(或 TR1)。

#include <random>

int main()
{
   double lower_bound = 0;
   double upper_bound = 10000;
   std::uniform_real_distribution<double> unif(lower_bound,upper_bound);
   std::default_random_engine re;
   double a_random_double = unif(re);

   return 0;
}

有关更多详细信息,请参阅 John D. Cook 的"Random number generation using C++ TR1"

另请参阅 Stroustrup 的 "Random number generation"

【讨论】:

  • 您可能希望使用更新的cppreference 文档来更新它,这非常好。
【解决方案3】:

这应该是高性能的、线程安全的和足够灵活的,可以用于多种用途:

#include <random>
#include <iostream>

template<typename Numeric, typename Generator = std::mt19937>
Numeric random(Numeric from, Numeric to)
{
    thread_local static Generator gen(std::random_device{}());

    using dist_type = typename std::conditional
    <
        std::is_integral<Numeric>::value
        , std::uniform_int_distribution<Numeric>
        , std::uniform_real_distribution<Numeric>
    >::type;

    thread_local static dist_type dist;

    return dist(gen, typename dist_type::param_type{from, to});
}

int main(int, char*[])
{
    for(auto i = 0U; i < 20; ++i)
        std::cout << random<double>(0.0, 0.3) << '\n';
}

【讨论】:

    【解决方案4】:

    如果此处的准确性是一个问题,您可以通过随机化有效位来创建具有更精细分级的随机数。 假设我们想要一个介于 0.0 和 1000.0 之间的双精度数。

    例如,在 MSVC (12 / Win32) 上,RAND_MAX 为 32767。

    如果您使用常见的rand()/RAND_MAX 方案,您的差距将与

    1.0 / 32767.0 * ( 1000.0 - 0.0) = 0.0305 ...
    

    在 IEE 754 双变量(53 个有效位)和 53 位随机化的情况下,0 到 1000 问题的最小可能随机化间隙将是

    2^-53 * (1000.0 - 0.0) = 1.110e-13
    

    因此显着降低。

    缺点是需要 4 次 rand() 调用才能获得随机整数(假设为 15 位 RNG)。

    double random_range (double const range_min, double const range_max)
    {
      static unsigned long long const mant_mask53(9007199254740991);
      static double const i_to_d53(1.0/9007199254740992.0);
      unsigned long long const r( (unsigned long long(rand()) | (unsigned long long(rand()) << 15) | (unsigned long long(rand()) << 30) | (unsigned long long(rand()) << 45)) & mant_mask53 );
      return range_min + i_to_d53*double(r)*(range_max-range_min);
    }
    

    如果尾数或RNG的位数未知,则需要在函数中获取相应的值。

    #include <limits>
    using namespace std;
    double random_range_p (double const range_min, double const range_max)
    {
      static unsigned long long const num_mant_bits(numeric_limits<double>::digits), ll_one(1), 
        mant_limit(ll_one << num_mant_bits);
      static double const i_to_d(1.0/double(mant_limit));
      static size_t num_rand_calls, rng_bits;
      if (num_rand_calls == 0 || rng_bits == 0)
      {
        size_t const rand_max(RAND_MAX), one(1);
        while (rand_max > (one << rng_bits))
        {
          ++rng_bits;
        }
        num_rand_calls = size_t(ceil(double(num_mant_bits)/double(rng_bits)));
      }
      unsigned long long r(0);
      for (size_t i=0; i<num_rand_calls; ++i)
      {
        r |= (unsigned long long(rand()) << (i*rng_bits));
      }
      r = r & (mant_limit-ll_one);
      return range_min + i_to_d*double(r)*(range_max-range_min);
    }
    

    注意:我不知道所有平台上 unsigned long long(64 位)的位数是否大于双尾数位数(IEE 754 为 53 位)。 如果不是这种情况,包含if (sizeof(unsigned long long)*8 &gt; num_mant_bits) ... 之类的检查可能是“聪明的”。

    【讨论】:

      【解决方案5】:

      这个 sn-p 直接来自 Stroustrup 的The C++ Programming Language (4th Edition), §40.7;它需要 C++11:

      #include <functional>
      #include <random>
      
      class Rand_double
      {
      public:
          Rand_double(double low, double high)
          :r(std::bind(std::uniform_real_distribution<>(low,high),std::default_random_engine())){}
      
          double operator()(){ return r(); }
      
      private:
          std::function<double()> r;
      };
      
      #include <iostream>    
      int main() {
          // create the random number generator:
          Rand_double rd{0,0.5};
      
          // print 10 random number between 0 and 0.5
          for (int i=0;i<10;++i){
              std::cout << rd() << ' ';
          }
          return 0;
      }
      

      【讨论】:

        【解决方案6】:

        为了生成随机数,我们可以使用其他朋友告诉的方法。我想在这里补充一个非常重要的一点。

        别人告诉的代码是:

        //I have made this as a function that returns the random double value, just copy this
        // if you want
        double random(){
            return (double)rand() / RAND_MAX; // for generating random points between 0 to 1
        }
        //now suppose I want any random value between two numbers min and max then I can use this as :
        int mynum = min + (max-min)*random();
        

        但是这段代码的问题在于它是有偏见的,我的意思是它在 0 和 1 之间没有平等地给出值。 Click here to see the image 此图像显示返回的值如何更偏向中心(即接近值 1)。为了避免这种情况,我们应该首选以下代码:

        double random(){
                return sqrt((double)rand() / RAND_MAX); // for generating random points between 0 to 1
            }
        

        选择平方根函数的原因

        选择 sqrt() 而不是 cbrt() 之类的任何其他函数将其偏向外端的原因是,在上面提到的第一种方法中,生成的点与 R^2 成正比,因为我们的随机数与R,从而使圆的整体面积与 R^2 成正比,这使它们更集中于中心。使我们的随机数与 sqrt(R) 成正比将使在圆的所有区域上生成的点与 R 成正比,这将使所有点在整个圆内均匀地生成。

        请注意,在应用 sqrt([0, 1] 之间的点)之后,结果将是一个大于原始 random() 的值,从而使其更偏向外端。这使得点在整个圆上均匀生成。

        感谢 @archit91 在 this article 中分享有关 LeetCode 的这些有用信息

        【讨论】:

          【解决方案7】:

          已经有很多很棒的解决方案,而且很多都非常优雅。我只是想我会在列表中添加另一个。我直接从“Modern C++ Programming CookBook, 2nd edition”中引用。在关于随机数生成器的章节中,强调了正确初始化伪随机数生成器的重要性。它补充说,Mersenne twister 引擎倾向于重复生成一些值而不包括其他值,因此不会生成均匀分布的数字,而是更像二项式或泊松分布。我包含的 sn-p 经历了初始化生成器的步骤,以便生成具有真正均匀分布的伪随机数。

          auto generate_random_double(double lb, double ub)//lb= lowerbound, ub = upperbound
          {
              //produce random #'s to be used as seeding values
              std::random_device rd{};
          
              //Generate random data for all the internal bits of the engine
              std::array<double, std::mt19937::state_size> seed_data{};
              ranges::generate(seed_data,std::ref(rd));
              
              //Create an std::seed_seq object from the pseudo random data 
              std::seed_seq seq(std::begin(seed_data), std::end(seed_data));
              
              //Create an engine object and initialize the bits representing the internal      
              //state of the engine; form example an mt19937 has 19937 bits
              auto eng = std::mt19937{ seq };
          
              //Create object based on the approprieat distribution based on application   
              //requirments 
              const auto randDouble = std::uniform_real_distribution<>{ lb,ub };
          
              //return object seeded with the previously initialized object
              return randDouble(eng);
          
          }//end method generate_random_double
          

          【讨论】:

            【解决方案8】:

            这是一个使用 C++11 的自包含 C++ 类。它在半开区间[low, high) (low &lt;= x &lt; high) 内生成随机双精度。

            #include <random>
            
            // Returns random double in half-open range [low, high).
            class UniformRandomDouble
            {
                std::random_device _rd{};
                std::mt19937 _gen{_rd()};
                std::uniform_real_distribution<double> _dist;
            
                public:
            
                    UniformRandomDouble() {
                        set(1.0, 10.0);
                    }
                    
                    UniformRandomDouble(double low, double high) {
                        set(low, high);
                    }
            
                    // Update the distribution parameters for half-open range [low, high).
                    void set(double low, double high) {
                        std::uniform_real_distribution<double>::param_type param(low, high);
                        _dist.param(param);
                    }
            
                    double get() {
                        return _dist(_gen);
                    }
            };
            

            【讨论】:

              【解决方案9】:

              类似这样的:

              #include <iostream>
              #include <time.h>
              
              using namespace std;
              
              int main()
              {
                  const long max_rand = 1000000L;
                  double x1 = 12.33, x2 = 34.123, x;
              
                  srandom(time(NULL));
              
                  x = x1 + ( x2 - x1) * (random() % max_rand) / max_rand;
              
                  cout << x1 << " <= " << x << " <= " << x2 << endl;
              
                  return 0;
              }
              

              【讨论】:

              • "(random() % max_rand)" = "random()"(即 3 % 7 = 3)。这将是一个浪费的处理步骤。
              • @Zak:如果它使用标准常量 RAND_MAX.... 会是这样,但事实并非如此。
              猜你喜欢
              • 2013-08-10
              • 1970-01-01
              • 2012-05-16
              • 1970-01-01
              • 1970-01-01
              • 2018-11-27
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              相关资源
              最近更新 更多