Search code examples
c++c++11randomnormal-distribution

Why c++11 std::normal_distribution return same pattern when called from a function?


I want to introduce Gaussian noise to a signal. I looked into several examples of how to use C++11 std::normal_distribution and I was able to achieve the expected random result using this example: https://stackoverflow.com/a/32890945/3424478

#include <functional>
#include <iostream>
#include <iterator>
#include <random>

int main() {
    // Example data
    std::vector<double> data = {1., 2., 3., 4., 5., 6.};

    // Define random generator with Gaussian distribution
    const double mean = 0.0;
    const double stddev = 0.1;
    auto dist = std::bind(std::normal_distribution<double>{mean, stddev},
                          std::mt19937(std::random_device{}()));

    // Add Gaussian noise
    for (auto& x : data) {
        x = x + dist();
    }

    // Output the result, for demonstration purposes
    std::copy(begin(data), end(data), std::ostream_iterator<double>(std::cout, " "));
    std::cout << "\n";

    return 0;
}

This work perfectly while I call dist() several times from main() and also works fine with different vectors but as soon as I move the code to a function it always return the same constant noise pattern, I want to call this function to modify the reference signal and assign it to different arrays or vectors. Here is my code:

void AddGaussianNoiseToPixel(std::array<short int, N_SLICES>& pixel_array, const std::array<short int, N_SLICES>& reference_pixel)
{
    const float mean   = 0.0;
    const float stddev = 2.0;
    auto dist = std::bind(std::normal_distribution<float>{mean, stddev},
                          std::mt19937(std::random_device{}()));

    for (const auto& slice : reference_pixel) 
    {
        pixel_array[&slice-&reference_pixel[0]] = rint(slice+dist());
    }
}

I read a similar post: https://stackoverflow.com/a/22921927/3424478 where this suppose to be happening due to the seed passed to the random generator but that's not the case since I'm passing std::random_device{}() to the random enginestd::mt19937()

EDIT:

I'm using MinGW-W64-builds-4.3.5 in Windows 7


Solution

  • This is most likely related to a feature/bug in mingw that makes std::random_device deterministic. You can circumvent this by adding another source of entropy, e.g. current time:

      uint64_t seed = std::random_device{}() |
        std::chrono::system_clock::now().time_since_epoch().count();
    

    However, a better solution is to use only one engine and distribution object. A simple way to do that is to use a static variable within a new function.