简体   繁体   English

在 C++ 中生成随机双精度数

[英]generate random double numbers in c++

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

Here's how就是这样

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

Remember to call srand() with a proper seed each time your program starts.请记住每次程序启动时都使用适当的种子调用 srand()。

[Edit] This answer is obsolete since C++ got it's native non-C based random library (see Alessandro Jacopsons answer) But, this still applies to C [编辑] 这个答案已经过时,因为 C++ 得到了它的原生非 C 随机库(参见 Alessandro Jacopsons 的答案)但是,这仍然适用于 C

This solution requires C++11 (or TR1).此解决方案需要 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;
}

For more details see John D. Cook's "Random number generation using C++ TR1" .有关更多详细信息,请参阅 John D. Cook 的“使用 C++ TR1 生成随机数”

See also Stroustrup's "Random number generation" .另见 Stroustrup 的“随机数生成”

This should be performant, thread-safe and flexible enough for many uses:这应该是高性能的、线程安全的和足够灵活的,可以用于许多用途:

#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';
}

If accuracy is an issue here you can create random numbers with a finer graduation by randomizing the significant bits.如果此处的准确性是一个问题,您可以通过随机化有效位来创建具有更精细分级的随机数。 Let's assume we want to have a double between 0.0 and 1000.0.假设我们想要一个介于 0.0 和 1000.0 之间的双精度数。

On MSVC (12 / Win32) RAND_MAX is 32767 for example.例如,在 MSVC (12 / Win32) 上,RAND_MAX 为 32767。

If you use the common rand()/RAND_MAX scheme your gaps will be as large as如果您使用常见的rand()/RAND_MAX方案,您的差距将与

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

In case of IEE 754 double variables (53 significant bits) and 53 bit randomization the smallest possible randomization gap for the 0 to 1000 problem will be在 IEE 754 双变量(53 个有效位)和 53 位随机化的情况下,0 到 1000 问题的最小可能随机化间隙将是

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

and therefore significantly lower.因此显着降低。

The downside is that 4 rand() calls will be needed to obtain the randomized integral number (assuming a 15 bit RNG).缺点是需要 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);
}

If the number of bits for the mantissa or the RNG is unknown the respective values need to be obtained within the function.如果尾数或 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);
}

Note: I don't know whether the number of bits for unsigned long long (64 bit) is greater than the number of double mantissa bits (53 bit for IEE 754) on all platforms or not.注意:我不知道所有平台上 unsigned long long(64 位)的位数是否大于双尾数位数(IEE 754 为 53 位)。 It would probably be "smart" to include a check like if (sizeof(unsigned long long)*8 > num_mant_bits) ... if this is not the case.如果不是这种情况,包括if (sizeof(unsigned long long)*8 > num_mant_bits) ...这样的检查可能是“聪明的”。

This snippet is straight from Stroustrup's The C++ Programming Language (4th Edition) , §40.7;该片段直接来自 Stroustrup 的The C++ Programming Language (4th Edition) §40.7; it requires C++11:它需要 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;
}

For generating random numbers we can use the methods that our other friends told.为了生成随机数,我们可以使用其他朋友告诉的方法。 I want to add a very important point here.我想在这里补充一个非常重要的观点。

The code told by others is :别人告诉的代码是:

//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();

But the problem with this code is that it is biased, I mean that it is not giving value equally between 0 and 1. Click here to see the image This image shows how the value returned is more biased towards the center (ie is near value one).但是这个代码的问题是它是有偏差的,我的意思是它没有在 0 和 1 之间给出相等的值。点击这里查看图像此图像显示返回的值如何更偏向中心(即接近值一)。 In order to avoid such condition we should prefer the following code:为了避免这种情况,我们应该更喜欢以下代码:

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

Reason for choosing Square root function选择平方根函数的原因

The reason for choosing sqrt() rather than any other functions like cbrt() to bias it towards the outer end is that in the first approach mentioned above, the points generated were proportional to R^2 because our random was proportional to R, thus making points overall area of the circle proportional to R^2 which made them concentrated more towards the center.选择 sqrt() 而不是 cbrt() 之类的任何其他函数将其偏向外端的原因是,在上面提到的第一种方法中,生成的点与 R^2 成正比,因为我们的随机数与 R 成正比,因此使圆的整体面积与 R^2 成正比,这使它们更集中于中心。 Making our random proportional to sqrt(R) would make the points generated over all the area of the circle proportional to R which would make all the points generate uniformly throughout the circle.使我们的随机数与 sqrt(R) 成正比将使在圆的所有区域上生成的点与 R 成正比,这将使所有点在整个圆内均匀地生成。

Note that after applying sqrt (a point between [0, 1]), the result would be a value greater than the original random() thus making it biased more towards the outer end.请注意,在应用 sqrt([0, 1] 之间的点)之后,结果将是一个大于原始 random() 的值,从而使其更偏向外端。 This makes the point uniformly generated over all of the circle.这使得点在整个圆上均匀生成。

I would like to thank @archit91 for sharing this usefull information on LeetCode in this article我要感谢 @archit91 在本文中分享有关 LeetCode 的这些有用信息

So many great solutions already and many are very elegant.已经有很多很棒的解决方案,而且很多都非常优雅。 I just figured I would add another to the list.我只是想我会在列表中添加另一个。 I am drawing references directly from the 'Modern C++ Programming CookBook, 2nd edition'.我直接从“Modern C++ Programming CookBook, 2nd edition”中引用。 In the chapter on random number generators, there is some emphasis on how very important it is to properly initialize the pseudo-random number generators.在关于随机数生成器的章节中,强调了正确初始化伪随机数生成器的重要性。 It adds that the Mersenne twister engine has a bias towards producing some values repeatedly and not including other values therefore not generating numbers in a uniform distribution but more like a binomial or Poisson distribution.它补充说,Mersenne twister 引擎倾向于重复生成一些值而不包括其他值,因此不会生成均匀分布的数字,而是更像二项式或泊松分布。 The snippet I am including goes through the steps of initializing a generator in order to produce pseudo-random numbers with a true uniform distribution.我包含的代码片段通过初始化生成器的步骤来生成具有真正均匀分布的伪随机数。

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

Here's a self-contained C++ class using C++11.这是一个使用 C++11 的自包含 C++ 类。 It generates a random double within a half-open interval [low, high) ( low <= x < high ).它在半开区间[low, high) ( low <= x < 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);
        }
};

This is similar to my answer above but should work from C++11 and up.这类似于我上面的答案,但应该适用于 C++11 及更高版本。

#include <iostream>
#include <random>
#include <array>
#include <algorithm>
#include <functional>

/**
 * a function that will generate pseudo random numbers in a normal distribution
 * @param lb  is the lower bound of the distribution (inclusive)
 * @param ub  is the upper bound of the distribution (inclusive)
 * @return  a pseudo random number in the range [lb, ub]
 */
auto generate_random_double(double lb, double ub)
{
    std::random_device rd{};
    std::array<double, std::mt19937::state_size> seed_data{};
    std::generate(seed_data.begin(), seed_data.end(), std::ref(rd));
    std::seed_seq seq(std::begin(seed_data), std::end(seed_data));
    auto eng = std::mt19937{ seq };
    const auto randDouble = std::uniform_real_distribution<>{ lb,ub };
    return std::bind(randDouble, eng);
}

something like this:像这样的东西:

#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;
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM