STYLE: floor/truncate instead of rounding for Random::position (issue #865)

- affects random integer ranges.
  Simpler to extend the range by one and floor (truncate) instead of
  rounding using odd intervals.
This commit is contained in:
Mark Olesen
2018-06-13 12:38:13 +02:00
parent 67310ed4ef
commit a784afc87d
3 changed files with 59 additions and 24 deletions

View File

@ -68,6 +68,22 @@ double randomFraction(const uint64_t bits)
using namespace Foam; using namespace Foam;
// Test uniformity of random
void testPosition(const label n)
{
List<label> samples(n, Zero);
Random rnd(123456);
for (label i=0; i < 100000*n; ++i)
{
++samples[rnd.position<label>(0,n-1)];
}
Info<< nl << "uniform [0," << n << ")\n "
<< flatOutput(samples) << nl;
}
// Output with cout instead of Info to keep values unsigned on output // Output with cout instead of Info to keep values unsigned on output
using std::cout; using std::cout;
using std::setw; using std::setw;
@ -175,20 +191,24 @@ int main(int argc, char *argv[])
} }
// Test uniformity of random // Test uniformity of random
testPosition(20);
testPosition(3);
// This should fail (in FULLDEBUG)
const bool throwingError = FatalError.throwExceptions();
try
{ {
List<label> samples(20, Zero); Info<<"Random position(10,5): "
<< Random().position<label>(10, 5) << endl;
Random rnd(123456); }
for (label i=0; i < 1000*samples.size(); ++i) catch (Foam::error& err)
{ {
++samples[rnd.position<label>(0,19)]; Info<< "Caught FatalError " << err << nl << endl;
}
Info<< nl << "uniform [0,20)" << nl << " "
<< flatOutput(samples) << nl;
} }
Info<< nl << "Done." << endl; FatalError.throwExceptions(throwingError);
Info<< "\nDone" << nl << endl;
return 0; return 0;
} }

View File

@ -121,9 +121,24 @@ Foam::scalar Foam::Random::position
template<> template<>
Foam::label Foam::Random::position(const label& start, const label& end) Foam::label Foam::Random::position(const label& start, const label& end)
{ {
// Extend range from [0, N-1] to (-0.5, N-0.5) to ensure that round() #ifdef FULLDEBUG
// results in the same number density at the ends. if (start > end)
return start + round(scalar01()*((end - start) + 0.998) - 0.499); {
FatalErrorInFunction
<< "start index " << start << " > end index " << end << nl
<< abort(FatalError);
}
#endif
// Extend the upper sampling range by 1 and floor the result.
// Since the range is non-negative, can use integer truncation
// instead using floor().
const label val = start + label(scalar01()*(end - start + 1));
// Rare case when scalar01() returns exactly 1.000 and the truncated
// value would be out of range.
return min(val, end);
} }

View File

@ -61,10 +61,10 @@ class Random
//- Initial random number seed //- Initial random number seed
label seed_; label seed_;
//- Random number generator on the int32 interval [0, 2^31) //- Random number generator on the int32 interval [0,2^31)
Rand48 generator_; Rand48 generator_;
//- Uniform distribution on the scalar interval [0, 1] //- Uniform distribution on the scalar interval [0,1]
std::uniform_real_distribution<scalar> uniform01_; std::uniform_real_distribution<scalar> uniform01_;
//- Is there a gaussian sample cached? //- Is there a gaussian sample cached?
@ -91,7 +91,7 @@ public:
Random(const Random& r, const bool reset = false); Random(const Random& r, const bool reset = false);
// Destructor //- Destructor
~Random() = default; ~Random() = default;
@ -111,20 +111,20 @@ public:
//- Return a random bit //- Return a random bit
inline int bit(); inline int bit();
//- Return a sample whose components lie in the range 0-1 //- Return a sample whose components lie in the range [0,1]
template<class Type> template<class Type>
Type sample01(); Type sample01();
//- Return a sample whose components are normally distributed //- Return a sample whose components are normally distributed
// with zero mean and unity variance N(0, 1) //- with zero mean and unity variance N(0,1)
template<class Type> template<class Type>
Type GaussNormal(); Type GaussNormal();
//- Return a sample between start and end //- Return a sample on the interval [start,end]
template<class Type> template<class Type>
Type position(const Type& start, const Type& end); Type position(const Type& start, const Type& end);
//- Randomise value in the range 0-1 //- Randomise value in the range [0,1]
template<class Type> template<class Type>
void randomise01(Type& value); void randomise01(Type& value);
@ -135,16 +135,16 @@ public:
// Global random numbers - consistent across all processors // Global random numbers - consistent across all processors
//- Return a sample whose components lie in the range 0-1 //- Return a sample whose components lie in the range [0,1]
template<class Type> template<class Type>
Type globalSample01(); Type globalSample01();
//- Return a sample whose components are normally distributed //- Return a sample whose components are normally distributed
// with zero mean and unity variance N(0, 1) //- with zero mean and unity variance N(0,1)
template<class Type> template<class Type>
Type globalGaussNormal(); Type globalGaussNormal();
//- Return a sample between start and end //- Return a sample on the interval [start,end]
template<class Type> template<class Type>
Type globalPosition(const Type& start, const Type& end); Type globalPosition(const Type& start, const Type& end);