0

I'm trying to write a function in C++ that returns a random float/double in between 0 and 1 every time with a different value every time.

I've tried a few different orientations of srand, rand, and RAND_MAX but everytime, when I run my code, certain values that should change each time have constant values. I have one term thats x = 20 * randomnumber() but everytime it will return the same value for x. No matter how often I run the code. Here is my function.

double randomnumber()
{
    srand(time(NULL))
    double r1 = ((double)rand()) / RAND_MAX);

    return r1;
}

What I want it to do is to generate a float between 0 and 1 so that when I multiply by another integer, I get a value in between 0 and that integer. Note: I know there is a function where I can do that explicitly between 0 and the number but the code I'm writing is better to just multiply by a random number every time.

Thanks in advance.

  • 8
    [`std::uniform_real_distribution<>(0.0,1.0)`](https://en.cppreference.com/w/cpp/numeric/random/uniform_real_distribution)? – KamilCuk Jun 03 '19 at 23:03
  • 3
    **WARNING**: Using [`rand()` is highly problematic](https://channel9.msdn.com/Events/GoingNative/2013/rand-Considered-Harmful) and you’re strongly encouraged to use an appropriate [random number generator facility in the Standard Library](http://en.cppreference.com/w/cpp/numeric/random) that produces high-quality random values. Your use of `time(NULL)` as a random number seed means that this will produce identical results if run in the same second, and on many platforms `rand()` is [*barely* random at all](http://dilbert.com/strip/2001-10-25). – tadman Jun 03 '19 at 23:03
  • So theoretically, if I were to change that to the uniform real distribution stated here, it would fix the problem? --dig the comic btw –  Jun 03 '19 at 23:06
  • 5
    Check here [why you should call srand() only once](https://stackoverflow.com/q/7343833/6070341). – Costantino Grana Jun 03 '19 at 23:15
  • @CostantinoGrana Although technically correct, the number of times you *should* call `srand()` is zero, as `rand()` is trash and shouldn't be used in C++. – tadman Jun 03 '19 at 23:16
  • @tadman I tried using the RNG you recommended and my runtime was suddenly upwards of a minute where as with rand() I was running at 7 seconds. Do you have any recommendations for that? –  Jun 03 '19 at 23:18
  • The C++ way is to use a PRNG like Kamil has suggested. This avoids most of the crippling issues with `rand()`. – tadman Jun 03 '19 at 23:18
  • Do you want actually random numbers, or are pseudo-random good enough? If so you can change how you initialize that generator and use a [Mersenne Twister](https://en.wikipedia.org/wiki/Mersenne_Twister) instead of the raw random device. Many examples in that video I linked to and the [main documentation](https://en.cppreference.com/w/cpp/numeric/random). You may find that good random numbers take a little while longer to compute than the crud that `rand()` delivers. If you're really stuck from a performance perspective, make sure you're compiling with optimizations enabled, not debug code. – tadman Jun 03 '19 at 23:20
  • @tadman I'm using this for a montecarlo simulation so primarily my numbers are being used for weighting. I was able to rework the std lib and my runtime was about 11 seconds which is significantly better. I'll look into the Mersenne Twister though. –  Jun 03 '19 at 23:23
  • The documentation linked to by Kamil has a good example at the end. If you've still got problems it's worth editing your code here with an update of what you've got going on so we can try and reproduce, or open a new question about the performance issue. – tadman Jun 03 '19 at 23:25
  • Great! Thanks for the assistance. –  Jun 03 '19 at 23:28
  • 3
    Unfortunately there is much noise on this thread about good versus bad RNGs. But most likely the biggest problem is the one pointed to by @CostantinoGrana: you should only call `srand()` once at the start of your program, not every time in your method. Even if you use a better PRNG, seeding it the way you are doing will still sabotage it's randomness. – President James K. Polk Jun 04 '19 at 00:24

2 Answers2

2

The best way to do this is to use C++'s random number library, not rand().

We can do this really easily:

#include <random>

double randomnumber() {
    // Making rng static ensures that it stays the same
    // Between different invocations of the function
    static std::default_random_engine rng;

    std::uniform_real_distribution<double> dist(0.0, 1.0); 
    return dist(rng); 
}

This will generate a new random number every time, and it'll generate the same sequence of random numbers each time you run the program. If I run

int main() {
    for(int i = 0; i < 10; i++) {
        std::cout << randomnumber() << '\n'; 
    }
}

Then I'll see

0.131538
0.45865
0.218959
0.678865
0.934693
0.519416
0.0345721
0.5297
0.00769819
0.0668422

Initializing the generator randomly. If you want to generate different random numbers each time you run the program, you'll have to initialize the generator with a random seed. It's pretty easy to generate one:

auto getRandomSeed() 
    -> std::seed_seq
{
    // This gets a source of actual, honest-to-god randomness
    std::random_device source;

    // Here, we fill an array of random data from the source
    unsigned int random_data[10];
    for(auto& elem : random_data) {
        elem = source(); 
    }

    // this creates the random seed sequence out of the random data
    return std::seed_seq(random_data + 0, random_data + 10); 
}

Once we have this seed, we can modify randomnumber() to create the generator with the random seed:

#include <random>

double randomnumber() {
    // Making rng static ensures that it stays the same
    // Between different invocations of the function
    static auto seed = getRandomSeed(); 
    static std::default_random_engine rng(seed);

    std::uniform_real_distribution<double> dist(0.0, 1.0); 
    return dist(rng); 
}
Alecto Irene Perez
  • 10,321
  • 23
  • 46
1

Everything is Ok in your code - maybe you round result and do not notice changes. Running program sequentially changes double after 4-th decimal digit. time_t returned from time(...) is narrowing converted to unsigned int in my platform - check this. And it's sufficient to seed generator once - not every randomnumber() call. You wrote that you use result for monte carlo simulation - Monte Carlo simulation in production requires random numbers of high quality - not that rand() may produce.

#include <stdlib.h>     /* srand, rand */
#include <time.h>       /* time */
#include <iostream>

double randomnumber()
{
    double r1 = (double)rand() / RAND_MAX;

    return r1;
}

int main()
{
    srand(time(NULL));
    std::cout << randomnumber() << std::endl;
}
Vlad
  • 1,977
  • 19
  • 44