I'm trying to calculate the covariance of a multichannel image patch (using cv::calcCovarMatrix), so I can in turn calculate the Mahalonobis distance of a pixel from that patch and I'm really struggling to find the right options to reshape the matrix into the right format.
For example if my matrix has 3 rows, 4 columns, and 2 channels:
// Channel 1:
1 2 3 4
5 6 7 8
9 0 1 2
// Channel 2:
99 98 97 96
95 94 93 92
91 90 89 88
What I believe I need is to reshape the image into a shape with 3x4=12 rows and 2 columns (or its transpose):
// Desired result:
1 2 3 4 5 6 7 8 9 0 1 2
99 98 97 96 95 94 93 92 91 90 89 88
Is this the correct format for cv::calcCovarMatrix?
What parameters do I need for .reshape() to achieve this?
An example in code:
#include <opencv2/opencv.hpp>
int main(int argc, char* argv[])
{
// Construct channel 1
cv::Mat_<float> channel1 = (cv::Mat_<float>(3, 4) << 1.0, 2.0, 3.0, 4.0,
5.0, 6.0, 7.0, 8.0,
9.0, 0.0, 1.0, 2.0);
std::cout << "Channel 1: " << std::endl;
std::cout << channel1 << std::endl;
// Construct channel 2
cv::Mat_<float> channel2 = (cv::Mat_<float>(3, 4) << 99.0, 98.0, 97.0, 96.0,
95.0, 94.0, 93.0, 92.0,
91.0, 90.0, 89.0, 88.0);
std::cout << "Channel 2: " << std::endl;
std::cout << channel2 << std::endl;
// Merge together
std::vector<cv::Mat> stack;
cv::Mat merged;
stack.push_back(channel1);
stack.push_back(channel2);
cv::merge(stack, merged);
std::cout << "Merged:" <<std::endl;
std::cout << merged << std::endl;
// Reshape
cv::Mat reshaped = merged.reshape(0,1).reshape(1); // <----Need help with this line
std::cout << "Reshaped:" <<std::endl;
std::cout << reshaped << std::endl;
return 0;
}
Not tested, but looking at the docs and the calcCovarMatrix() implementation, you should do something like
cv::Mat reshaped = merged.reshape(1,1);
or
cv::Mat reshaped = merged.reshape(1,3*4);
It seems that calcCovarMatrix() can process both column matrices and row-matrices.
You can take a look at the code in opencv/modules/core/src/matmul.cpp, line 2097
Related
Im having an issue using opencv trying to convert an image to an array. The conversion works however i seem to have incorrect dimensions in the resulting array:
#include <opencv2/opencv.hpp>
int main()
{
auto img = cv::imread("test.jpg", CV_LOAD_IMAGE_COLOR);
std::cout << "img cols: " << img.cols << " img rows: "
<< img.rows << " channels: " << img.channels() << std::endl;
std::vector<float> array2;
if (img.isContinuous()) {
array2.assign((float*)img.ptr(0), (float*)(img.ptr(img.rows - 1)) + img.cols);
std::cout << array2.size() << "\n";
}
return 0;
}
The output from the first print line results in :
img cols: 416 img rows: 416 channels: 3
Which is correct, however after assigning the data to the array the dimensions are : 518336 , when they should be 519168 (416*416*3).
Could anyone possibly suggest what exactly is causing the resulting array to be smaller than expected?
There are several problems with your code:
First of all, cv::imread("test.jpg", CV_LOAD_IMAGE_COLOR); will (on success) return a cv::Mat with datatype CV_8UC3, however you're accessing the elements as floats. This means that the values you will read will be garbage, and you will also end up reading past the end of the pixel buffer.
If you want floats, then you need to do some conversion/casting, either before or during the act of copying.
The second problem lies in your calculation of the "end" pointer, where you seem to forget that you're dealing with a multi-channel cv::Mat. In case of a CV_8UC3 matrix, each pixel is represented by 3 bytes, hence there are cols*channels bytes per row. (That's why you're short by 2*416 elements)
Not really a problem, but a limitation -- your code only works for continuous Mats.
I would take a somewhat different approach, and take advantage of functionality provided by OpenCV.
Option 1
Use cv::Mat::copyTo, since OutputArray can wrap a std::vector<T>. However, for this to work, the source Mat needs to have 1 channel and 1 row. We can achieve this efficiently using cv::Mat::reshape, but the Mat needs to be continuous, so that limitation stays.
std::vector<uchar> to_array_v1(cv::Mat3b const& img)
{
std::vector<uchar> a;
if (img.isContinuous()) {
img.reshape(1, 1).copyTo(a);
}
return a;
}
Option 2
Use MatIterators which we can get using cv::Mat::begin and cv::Mat::end. The iterators will work correctly even on a non-continuous Mat, however we need them to iterate over bytes, so we need to reshape the matrix to a single channel one. Since we're not changing the number of rows, the reshape will also work on a non-continuous Mat.
std::vector<uchar> to_array_v2(cv::Mat3b const& img)
{
cv::Mat1b tmp(img.reshape(1));
return std::vector<uchar>(tmp.begin(), tmp.end());
}
Option 3
The approach suggested by Silencer, using the rather poorly documented cv::Mat::datastart and cv::Mat::dataend members. The documentation of cv::Mat::locateROI sheds some more light on the meaning of those member variables:
However, each submatrix contains information (represented by datastart and dataend fields) that helps reconstruct the original matrix size and the position of the extracted submatrix within the original matrix.
This means that this approach has 2 limitations: it needs a continous matrix, and it won't work correctly for a submatrix, even if it's continuous. (Specifically, for a continuous submatrix, it would return the entire buffer of the "parent" matrix)
std::vector<uchar> to_array_v3(cv::Mat3b const& img)
{
std::vector<uchar> a;
if (img.isContinuous() && !img.isSubmatrix()) {
a.assign(img.datastart, img.dataend);
}
return a;
}
Test Code
#include <opencv2/opencv.hpp>
#include <iostream>
#include <numeric>
#include <vector>
// Paste implementations from the answer here
cv::Mat3b test_image()
{
cv::Mat1b m(4, 4);
std::iota(m.begin(), m.end(), 0);
cv::Mat3b img;
cv::merge(std::vector<cv::Mat1b>{ m * 3, m * 3 + 1, m * 3 + 2 }, img);
return img;
}
void print(cv::Mat3b const& img)
{
std::cout << "Continuous: " << (img.isContinuous() ? "yes" : "no") << '\n';
std::cout << "Submatrix: " << (img.isSubmatrix() ? "yes" : "no") << '\n';
std::cout << img << "\n";
}
void print(std::vector<uchar> const& a)
{
if (a.empty()) {
std::cout << "empty";
} else {
for (auto n : a) {
std::cout << int(n) << ' ';
}
}
std::cout << "\n";
}
void test(cv::Mat3b const& img)
{
print(img);
print(to_array_v1(img));
print(to_array_v2(img));
print(to_array_v3(img));
}
int main()
{
cv::Mat3b img(test_image());
test(img);
cv::Mat3b img2(img(cv::Rect(0, 0, 3, 3)));
test(img2);
cv::Mat3b img3(img(cv::Rect(1, 1, 3, 1)));
test(img3);
return 0;
}
Running this program will produce the following output:
Continuous: yes
Submatrix: no
[ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 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, 40, 41, 42, 43, 44, 45, 46, 47]
0 1 2 3 4 5 6 7 8 9 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 40 41 42 43 44 45 46 47
0 1 2 3 4 5 6 7 8 9 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 40 41 42 43 44 45 46 47
0 1 2 3 4 5 6 7 8 9 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 40 41 42 43 44 45 46 47
Continuous: no
Submatrix: yes
[ 0, 1, 2, 3, 4, 5, 6, 7, 8;
12, 13, 14, 15, 16, 17, 18, 19, 20;
24, 25, 26, 27, 28, 29, 30, 31, 32]
empty
0 1 2 3 4 5 6 7 8 12 13 14 15 16 17 18 19 20 24 25 26 27 28 29 30 31 32
empty
Continuous: yes
Submatrix: yes
[ 15, 16, 17, 18, 19, 20, 21, 22, 23]
15 16 17 18 19 20 21 22 23
15 16 17 18 19 20 21 22 23
empty
Mat img = imread("test.png");
std::vector<uchar> arr;
// convert Mat of CV_8UC3 to std::vector<uchar> if continuous
if(img.isContinuous()){
arr.assign(img.datastart, img.dataend);
}
Im trying to build a simple input/output matrix (where you can calculate the multiplier effect in a simple economy if demand increases). But for some reason the final result is not adding up.
#include <iostream>
#include <Eigen/Dense>
using namespace std;
using namespace Eigen;
void InputOutput(){
MatrixXf ProdA(5, 5);;
VectorXf Intd(5);
VectorXf Finald(5);
ProdA <<
10, 20, 0, 0, 5,
20, 30, 20, 10, 10,
10, 10, 0, 10, 10,
10, 40, 20, 5, 5,
20, 20, 30, 5, 5;
Intd << 55, 40, 20, 30, 10;
Finald << 0, 0, 0, 0, 0;
VectorXf ones(5);
ones << 1, 1, 1, 1, 1;
Finald = ProdA * ones + Intd;
MatrixXf AMatrix = MatrixXf::Zero(ProdA.rows(), ProdA.cols());
AMatrix = ProdA.array() / (Finald.replicate(1, ProdA.cols())).array();
cout << "Here is the Coefficient vector production needed:\n" << AMatrix << endl;
MatrixXf IminA(5, 5);;
IminA = MatrixXf::Identity(AMatrix.rows(), AMatrix.cols()) - AMatrix;
cout << "Here is the matrix of production:\n" << ProdA << endl;
cout << "Here is the vector Internal demand:\n" << Intd << endl;
cout << "Here is the vector Final demand:\n" << Finald << endl;
cout << "Here is the Coefficient vector production needed:\n" << AMatrix << endl;
MatrixXf IminAinv(5, 5);;
IminAinv = IminA.inverse();
cout << "The inverse of CoMatrix - Imatrix is:\n" << IminAinv << endl;
cout << "To check, final demand is:\n" << (IminAinv * Intd) << endl;
When I verify if the (I-A)inverse matrix (or IminAinv) is properly calculated it doesn't add up. By multiplying the IminAinv by Internal demand (int), I should get the same Intd. That is if Intd isn't changed. Instead I get a bigger number. Also if I calculate the inverse of the IminA matrix myself, I get something different then eigen.
So something goes wrong in getting the inverse of Identity matrix - Coefficient matrix. But what?
Thanks!
EDIT :
After some more digging into why there are some differences in the final results, I discovered that those "underlying mechanisms" mentioned in Case 2 were, in fact, my own mistake out of inadvertence while entering the matrices' values.
Here follows the original answer with these mistakes taken care of.
The actual problem does not lie in the inversion of the AMatrix but in a much more subtle detail.
You are using this command to perform the division in the definition of AMatrix:
AMatrix = ProdA.array() / (Finald.replicate(1, ProdA.cols())).array();
But if you check the results of this replicate operation on Finald you get:
...
cout << "Here is the replicated final demand vector:\n" << (Finald.replicate(1, ProdA.cols())).array() << endl;
...
>>
Here is the replicated final demand vector:
90 90 90 90 90
130 130 130 130 130
60 60 60 60 60
110 110 110 110 110
90 90 90 90 90
whereas the correct one should be:
90 130 60 110 90
90 130 60 110 90
90 130 60 110 90
90 130 60 110 90
90 130 60 110 90
You can transpose the replicated final demand vector like this:
MatrixXf Finaldrep(5,5);
Finaldrep = (Finald.replicate(1, ProdA.cols())).array().transpose();
and then of course:
AMatrix = ProdA.array() / Finaldrep.array();
which yields:
cout << "Here is the transposed replicated final demand vector:\n" << Finaldrep << endl;
...
>>
Here is the transposed replicated final demand vector:
90 130 60 110 90
90 130 60 110 90
90 130 60 110 90
90 130 60 110 90
90 130 60 110 90
So, let's see what are the differences in your intermediate and final results in both these cases:
Case 1
ie Your current approach
Here is the Coefficient vector production needed:
0.111111 0.222222 0 0 0.0555556
0.153846 0.230769 0.153846 0.0769231 0.0769231
0.166667 0.166667 0 0.166667 0.166667
0.0909091 0.363636 0.181818 0.0454545 0.0454545
0.222222 0.222222 0.333333 0.0555556 0.0555556
The determinant of IminA is: 0.420962
The inverse of CoMatrix - Imatrix is:
1.27266 0.468904 0.131153 0.0688064 0.13951
0.443909 1.68132 0.377871 0.215443 0.240105
0.451292 0.628205 1.25318 0.287633 0.312705
0.404225 0.841827 0.423093 1.20242 0.224877
0.586957 0.777174 0.586957 0.23913 1.27174
To check, final demand is:
94.8349
108.09
86.7689
102.689
95
I have also added the determinant of IminA
Case 2
ie using the reversed final demand vector
Here is the Coefficient vector production needed:
0.111111 0.153846 0 0 0.0555556
0.222222 0.230769 0.333333 0.0909091 0.111111
0.111111 0.0769231 0 0.0909091 0.111111
0.111111 0.307692 0.333333 0.0454545 0.0555556
0.222222 0.153846 0.5 0.0454545 0.0555556
The determinant of IminA is: 0.420962
The inverse of CoMatrix - Imatrix is:
1.27266 0.324626 0.196729 0.0562962 0.13951
0.641202 1.68132 0.818721 0.254615 0.346818
0.300861 0.289941 1.25318 0.156891 0.20847
0.494053 0.712316 0.77567 1.20242 0.27485
0.586957 0.538044 0.880435 0.195652 1.27174
To check, final demand is:
90
130
60
110
90
Now, I understand that the Finald check still does not produce the exact values of the originally defined Finald, but I believe that this has something to do with the precision or some other underlying mechanism. (see NOTE)
As a proof of concept here are some results obtained with MATLAB, using the second case (reversed) for the replicated Final Demand Vector (denom):
>> AMatrixcm = ProdA ./ Finaldfullcm
AMatrixcm =
0.1111 0.1538 0 0 0.0556
0.2222 0.2308 0.3333 0.0909 0.1111
0.1111 0.0769 0 0.0909 0.1111
0.1111 0.3077 0.3333 0.0455 0.0556
0.2222 0.1538 0.5000 0.0455 0.0556
>> IminAcm = eye(5) - AMatrixcm
IminAcm =
0.8889 -0.1538 0 0 -0.0556
-0.2222 0.7692 -0.3333 -0.0909 -0.1111
-0.1111 -0.0769 1.0000 -0.0909 -0.1111
-0.1111 -0.3077 -0.3333 0.9545 -0.0556
-0.2222 -0.1538 -0.5000 -0.0455 0.9444
>> det(IminAcm)
ans =
0.4210
>> IminAinvcm = inv(IminAcm)
IminAinvcm =
1.2727 0.3246 0.1967 0.0563 0.1395
0.6412 1.6813 0.8187 0.2546 0.3468
0.3009 0.2899 1.2532 0.1569 0.2085
0.4941 0.7123 0.7757 1.2024 0.2748
0.5870 0.5380 0.8804 0.1957 1.2717
>> Finaldcheckcm = IminAinvcm * Intdc
Finaldcheckcm =
90.0000
130.0000
60.0000
110.0000
90.0000
It is quite clear that the second case results are (almost) identical to the MATLAB ones.
NOTE: Here you can see that the MATLAB output is identical to the original Finald, however, if you perform the last matrix multiplication (the one in the validation of the Final Demand vector) by hand, you will see that in fact both MATLAB and Case 2 versions of IminAinv yield the same result as the final output of Case 2, ie [88.9219, 125.728, 59.5037, 105.543, 84.5808].
This is why I think that there is some other mechanism involved in these differences. (See EDIT on top of post)
I' trying to get cloud of points from two stereo images using OpenCV, but I can't get coordinates.
I found points coordinates using Optical Flow
I found projection matrixes for cameras.
Mat RT1;<br>
hconcat(R, T1, RT1);<br>
Mat P1 = C*RT1;<br>
R is 3x3 rotation matrix, T is 3x1 transform matrix (column), P1 - projection matrix.
I pass them to triangulatePoints function
triangulatePoints(P1, P2, leftPoints, rightPoints, out);
P1 and P2 is 3x4 projection matrix (Mat_<double>).
leftPoints and rightPoints is std::vector of Point2f.
What is out? It should be 1xN matrix of 4D coords. Is this Vec4f?
I am trying get coordinates
for (int i = 0; i < out.cols; i++)
{
Vec4f vec = out.at<Vec4f>(0, i);
float w = vec[3];
stream << "v " << vec[0] / w << " " << vec[1]/w << " " << vec[2]/w << "\n";
}
But I have two problems:
This cycle throw exception (works for small i, about 20% of out.cols)
OpenCV Error: Assertion failed (dims <= 2 && data && (unsigned)i0 <
(unsigned)si ze.p[0] && (unsigned)(i1 * DataType<_Tp>::channels) <
(unsigned)(size.p1 * cha nnels()) &&
((((sizeof(size_t)<<28)|0x8442211) >> ((DataType<_Tp>::depth) & ((1 <<
3) - 1))*4) & 15) == elemSize1()) in cv::Mat::at, file
c:\opencv\build\includ e\opencv2\core\mat.inl.hpp, line 89
I think this is some kind of Index Out Range Exception
Result is very strange:
Image
So, I am doing something wrong. How correctly work with this function and get 3D coordinates of points? I'll hope you can help me.
I don't understand exactly your approach in getting the coordinates.
As far as I see, you should not be accessing elements like
out.at<Vec4f>(0, i);
Instead, do it like this:
float x = out.at<float>(0, i);
float y = out.at<float>(1, i);
float z = out.at<float>(2, i);
float w = out.at<float>(3, i);
stream << "v " << x << " " << y << " " << z << "\n";
Or use double... depending whether you out is of type CV_32F or CV_64F.
Here is how I do it:
Mat points3DHomogeneous;
triangulatePoints(projectionMatrixL, projectionMatrixR, pointsL, pointsR, points3DHomogeneous);
projectionMatrixL:
projectionMatrixR:
pointsL:
700 250
200 300
600 350
400 400
500 450
600 500
700 550
800 600
150 650
1000 700
pointsR:
690 250
180 300
590 350
385 400
495 450
575 500
691 550
782 600
120 650
960 700
points3DHomogeneous is the result:
So I'm trying to make a graphing application, and I'm using Desmos as a base for that.
The thing I'm struggling with is the way Desmos handles the subdivisions of the axes. When you zoom in or out the scales are always on "easy" simple numbers like 5, 100, 1000 etc. So my question is: how does one go about simplifying their scale with any level of zoom?
BTW: Using C++
I was going to write a description of how to do this in general, but then I realize that the code may be easier than explaining.
Most important step: define precisely what you mean by "easy simple" numbers.
Example #1: 1, 2, 4, 8, 16, 32, 64, 128, ... , 1073741824, ...
These are powers of two. So, a straightforward ceil(log(x)/log(2.0)) will solve it.
Example #2: 1, 2, 5, 10, 20, 50, 100, 200, 500, 1000, 2000, 5000, 10000, ...
There is a mixture of powers of two, and some multiples of it. Let's take a closer look.
A subset of these can be described as powers of ten.
Changing the formula to ceil(log(x)/log(10.0)) will solve it.
For each power-of-ten, its multiples by 2.0 and 5.0 are also "easy simple numbers".
Inside each iteration, after checking the power-of-ten value, also check the two multiples. If it fits inside one of the multiple, that value can be returned as result.
Code
The following code is only meant to explain the concept. It is not efficient - an efficient version should have used logarithm to get the result in O(1) time.
#include <iostream>
#include <vector>
#include <limits>
#include <stdexcept>
#include <algorithm>
using namespace std;
double getNiceAxisLength(double value, double baseLength, double step, const std::vector<double>& subSteps)
{
typedef std::vector<double>::const_iterator VecDoubleIter;
if (value < 0.0)
{
throw std::invalid_argument("Error: value must be non-negative. Take absolute value if necessary.");
}
if (baseLength <= 0.0)
{
throw std::invalid_argument("Error: baseLength must be positive.");
}
if (step <= 1.0)
{
throw std::invalid_argument("Error: step must be strictly greater than 1.");
}
for (VecDoubleIter iter = subSteps.begin(); iter != subSteps.end(); ++iter)
{
double subStep = *iter;
if (subStep <= 1.0 || subStep >= step)
{
throw std::invalid_argument("Error: each subStep must be strictly greater than 1, and strictly smaller than step.");
}
}
// make ascending.
std::vector<double> sortedSubSteps(subSteps.begin(), subSteps.end());
std::sort(sortedSubSteps.begin(), sortedSubSteps.end());
if (value <= baseLength)
{
return baseLength;
}
double length = baseLength;
double terminateLength = numeric_limits<double>::max() / step;
while (length < terminateLength)
{
for (VecDoubleIter iter = sortedSubSteps.begin(); iter != sortedSubSteps.end(); ++iter)
{
double subStep = *iter;
if (value <= length * subStep)
{
return (length * subStep);
}
}
double nextLength = length * step;
if (value <= nextLength)
{
return nextLength;
}
length = nextLength;
}
return baseLength;
}
int main()
{
double baseLength = 1.0;
double step = 10.0;
std::vector<double> subSteps;
subSteps.push_back(2.5);
subSteps.push_back(5);
for (int k = 0; k < 1000; k += ((k >> 2) + 1))
{
double value = k;
double result = getNiceAxisLength(value, baseLength, step, subSteps);
cout << "k: " << value << " result: " << result << endl;
}
cout << "Hello world!" << endl;
return 0;
}
Output
k: 0 result: 1
k: 1 result: 1
k: 2 result: 2.5
k: 3 result: 5
k: 4 result: 5
k: 6 result: 10
k: 8 result: 10
k: 11 result: 25
k: 14 result: 25
k: 18 result: 25
k: 23 result: 25
k: 29 result: 50
k: 37 result: 50
k: 47 result: 50
k: 59 result: 100
k: 74 result: 100
k: 93 result: 100
k: 117 result: 250
k: 147 result: 250
k: 184 result: 250
k: 231 result: 250
k: 289 result: 500
k: 362 result: 500
k: 453 result: 500
k: 567 result: 1000
k: 709 result: 1000
k: 887 result: 1000
Hello world!
Hello world!
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 wrong, or is VS2012 not the smart one here?
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:
I've truncated Visual Studio output for better displaying.
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.