Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

std::piecewise_linear_distribution not working under VS2012?

I decided to get to know c++11 <random> better, so I wrote such a code:

std::mt19937 gen(10);
std::piecewise_linear_distribution<> d(Range.begin(), Range.end(),
                                       RangeValues.begin());

std::map<int, unsigned int> hist;
for (int n = 0; ++n != iterations;)
    ++hist[std::round(d(gen))];

for (auto p : hist)
   std::cout << std::setw(2) << p.first << ": "
             << std::string(p.second/(iterations/200), '*') << '\n';

For some reason std::random_device seems to not work on Coliru, so I entered a const sample seed instead. I presume, that it is UB hence IIRC it is based heavily on hardware, and that's why it's not supported on Coliru (correct me if I am wrong). On Ideone it does work however.

Then I ported it to run on VS2012, the only difference being an own implementation of std::round:

return number < 0.0 ? std::ceil(number - 0.5) : std::floor(number + 0.5);

It works perfectly correct on Coliru, but when I compile and run it on VS2012, the output is just wrong.

Any idea how to correct this, and more importantly, why it happens? Am I doing something retarded, or is VS2012 not the smart one here?

like image 705
Wierzba Avatar asked Apr 14 '13 14:04

Wierzba


1 Answers

It seems that this is a Visual Studio issue. I've tried the program below (adapted from the OP) and the output generated by GCC 4.7.2, Clang 3.2 and Intel 13.1.0 are very reasonable whereas the one generated by Visual Studio Nov 2012 CTP is completely different.

The probability density is piecewise linear and defined by arrays x and p in the following way. A piecewise linear function connecting the points (x[i], p[i]) for i = 0, ..., N (where N = x.size() - 1) is built. Then this function is normalized (by dividing it by its integral) to get the probability density.

#include <iostream>
#include <iomanip>
#include <string>
#include <random>
#include <array>

int main() {

    std::mt19937 gen(10);

    std::array<double, 3> x = {{0, 20, 40}};
    std::array<double, 3> p = {{0,  1,  0}};
    std::piecewise_linear_distribution<> dist(x.begin(), x.end(), p.begin());

    std::array<int, 40> hist = {{0}};

    for (size_t i = 0; i < 200000; ++i)
        ++hist[static_cast<size_t>(dist(gen))];

    for (size_t n = 0; n < hist.size(); ++n)
        std::cout << std::setfill('0') << std::setw(2) << n << ' ' << 
          std::string(hist[n] / 200, '*') << std::endl;

    std::cout << "\nValues in interval [20, 21[ : " << hist[20] << std::endl;
}

In our example, the polygonal function connects (0, 0), (20, 1) and (40, 0). Hence, its shape is an isosceles triangle with base 40 and height 1 which yields an area of 20. Therefore, the probability density f connects (0, 0), (20, 1/20) and (40, 0). This implies that in the interval [20, 21[ we could expect around f(20) * (21 - 20) = 1/20 * 1 = 1/20 results of the draw. In total we draw 200,000 values and then, we can expect around 10,000 points in [20, 21[.

GCC, Clang and Intel report 9734 points in [20, 21[ and display a pattern that is quite similar to an isosceles triangle:

00 *
01 ***
02 *****
03 ********
04 ***********
05 **************
06 ***************
07 ******************
08 ********************
09 ************************
10 **************************
11 ****************************
12 *******************************
13 *********************************
14 ***********************************
15 ***************************************
16 *****************************************
17 ******************************************
18 **********************************************
19 ************************************************
20 ************************************************
21 *********************************************
22 *******************************************
23 *****************************************
24 **************************************
25 ************************************
26 **********************************
27 ******************************
28 ****************************
29 **************************
30 ***********************
31 ********************
32 ******************
33 ****************
34 *************
35 ***********
36 *********
37 ******
38 ***
39 *

Values in interval [20, 21[ : 9734

Unfortunately, Visual Studio Nov 2012 CTP gives this:

00 ********************************************** [truncated]
01 **********************************************
02 ***********************************
03 *****************************
04 **************************
05 ***********************
06 *********************
07 ********************
08 *******************
09 ******************
10 *****************
11 ****************
12 ***************
13 **************
14 **************
15 **************
16 *************
17 *************
18 *************
19 ************
20 ************
21 *************
22 *************
23 *************
24 *************
25 **************
26 ***************
27 ***************
28 ****************
29 *****************
30 ******************
31 *******************
32 *******************
33 *********************
34 ***********************
35 **************************
36 *****************************
37 ***********************************
38 **********************************************
39 ********************************************** [truncated]

Values in interval [20, 21[ : 2496

Notes:

  1. I've truncated Visual Studio output for better displaying.
  2. A better estimate for the number of points in [20, 21[ is 200,000 * (0.5 * (f(20) + f(21))) * (21 - 20) = 100,000 * (1/20 + 1/20 - 1/400) = 10,000 - 250 = 9750.
like image 146
Cassio Neri Avatar answered Nov 13 '22 06:11

Cassio Neri