mirror of https://github.com/mosra/magnum.git
4 changed files with 208 additions and 0 deletions
@ -0,0 +1,100 @@ |
|||||||
|
#ifndef Magnum_Math_Random_h |
||||||
|
#define Magnum_Math_Random_h |
||||||
|
|
||||||
|
// TO DO Licence things.
|
||||||
|
|
||||||
|
#include <random> |
||||||
|
#include <chrono> |
||||||
|
#include "Magnum/Types.h" |
||||||
|
#include "Magnum/Math/Constants.h" |
||||||
|
#include "Magnum/Math/Vector2.h" |
||||||
|
#include "Magnum/Math/Vector3.h" |
||||||
|
#include "Magnum/Math/Quaternion.h" |
||||||
|
#include "Magnum/Math/Functions.h" |
||||||
|
|
||||||
|
namespace Magnum |
||||||
|
{ |
||||||
|
namespace Math |
||||||
|
{ |
||||||
|
|
||||||
|
namespace Random |
||||||
|
{ |
||||||
|
class RandomGenerator |
||||||
|
{ |
||||||
|
public: |
||||||
|
RandomGenerator() |
||||||
|
{ |
||||||
|
std::seed_seq seeds{{ |
||||||
|
static_cast<std::uintmax_t>(std::random_device{}()), |
||||||
|
static_cast<std::uintmax_t>(std::chrono::steady_clock::now() |
||||||
|
.time_since_epoch() |
||||||
|
.count()), |
||||||
|
}}; |
||||||
|
g = std::mt19937{seeds}; |
||||||
|
}; |
||||||
|
template <typename T> |
||||||
|
typename std::enable_if<std::is_same<Int, T>::value, T>::type |
||||||
|
generate(T start = -Magnum::Math::Constants<T>::inf(), |
||||||
|
T end = Magnum::Math::Constants<T>::inf()) |
||||||
|
{ |
||||||
|
return std::uniform_int_distribution<T>{start, end}(g); |
||||||
|
} |
||||||
|
|
||||||
|
template <typename T> |
||||||
|
typename std::enable_if<std::is_same<Float, T>::value, T>::type |
||||||
|
generate(T start = -Magnum::Math::Constants<T>::inf(), |
||||||
|
T end = Magnum::Math::Constants<T>::inf()) |
||||||
|
{ |
||||||
|
return std::uniform_real_distribution<T>{start, end}(g); |
||||||
|
} |
||||||
|
|
||||||
|
private: |
||||||
|
// namespace Implementation
|
||||||
|
std::mt19937 g; |
||||||
|
}; |
||||||
|
|
||||||
|
template <class T = Float> |
||||||
|
T randomScalar(RandomGenerator &g, T begin = 0.0f, T end = 1.0f) |
||||||
|
{ |
||||||
|
|
||||||
|
return g.generate(static_cast<T>(begin), |
||||||
|
static_cast<T>(end)); |
||||||
|
} |
||||||
|
|
||||||
|
template <class T = Float> |
||||||
|
Vector2<T> randomUnitVector2(RandomGenerator &g) |
||||||
|
{ |
||||||
|
auto a = g.generate(0.0f, 2 * Math::Constants<T>::pi()); |
||||||
|
return {std::cos(a), std::sin(a)}; |
||||||
|
} |
||||||
|
|
||||||
|
template <class T = Float> |
||||||
|
Vector3<T> randomUnitVector3(RandomGenerator &g) |
||||||
|
{ |
||||||
|
// Better to have it "theta" and "z" than three random numbers.
|
||||||
|
// https://mathworld.wolfram.com/SpherePointPicking.html
|
||||||
|
auto a = g.generate(0.0f, 2 * Math::Constants<T>::pi()); |
||||||
|
auto z = randomScalar(g, -1.0f, -1.0f); |
||||||
|
auto r = sqrt<T>(1 - z * z); |
||||||
|
return {r * std::cos(a), r * std::sin(a), z}; |
||||||
|
} |
||||||
|
|
||||||
|
template <class T = Float> |
||||||
|
Quaternion<T> randomRotation(RandomGenerator &g) |
||||||
|
{ |
||||||
|
//http://planning.cs.uiuc.edu/node198.html
|
||||||
|
auto u = randomScalar(g); |
||||||
|
auto v = 2 * Math::Constants<T>::pi() * randomScalar(g); |
||||||
|
auto w = 2 * Math::Constants<T>::pi() * randomScalar(g); |
||||||
|
return Quaternion<T>({sqrt<T>(1 - u) * std::sin(v), |
||||||
|
sqrt<T>(1 - u) * std::cos(v), |
||||||
|
sqrt<T>(u) * std::sin(w)}, |
||||||
|
sqrt<T>(u) * std::cos(w)); |
||||||
|
} |
||||||
|
|
||||||
|
} // namespace Random
|
||||||
|
|
||||||
|
} // namespace Math
|
||||||
|
} // namespace Magnum
|
||||||
|
|
||||||
|
#endif |
||||||
@ -0,0 +1,101 @@ |
|||||||
|
#include <Corrade/TestSuite/Tester.h> |
||||||
|
#include <Corrade/TestSuite/Compare/Numeric.h> |
||||||
|
#include <Corrade/Utility/DebugStl.h> |
||||||
|
|
||||||
|
#include "Magnum/Math/Random.h" |
||||||
|
|
||||||
|
namespace Magnum |
||||||
|
{ |
||||||
|
namespace Math |
||||||
|
{ |
||||||
|
|
||||||
|
namespace Test |
||||||
|
{ |
||||||
|
namespace |
||||||
|
{ |
||||||
|
|
||||||
|
struct RandomTest : Corrade::TestSuite::Tester |
||||||
|
{ |
||||||
|
explicit RandomTest(); |
||||||
|
|
||||||
|
void randScalar(); |
||||||
|
void unitVector2(); |
||||||
|
void unitVector3(); |
||||||
|
void randomRotation(); |
||||||
|
void randomDiceChiSquare(); |
||||||
|
}; |
||||||
|
|
||||||
|
typedef Vector<2, Float> Vector2; |
||||||
|
typedef Vector<3, Float> Vector3; |
||||||
|
typedef Math::Constants<Float> Constants; |
||||||
|
|
||||||
|
RandomTest::RandomTest() |
||||||
|
{ |
||||||
|
Corrade::TestSuite::Tester::addRepeatedTests( |
||||||
|
{&RandomTest::randScalar, |
||||||
|
&RandomTest::unitVector2, |
||||||
|
&RandomTest::unitVector3, |
||||||
|
&RandomTest::randomRotation}, |
||||||
|
/*repeat number*/ 200); |
||||||
|
Corrade::TestSuite::Tester::addTests( |
||||||
|
{&RandomTest::randomDiceChiSquare}); |
||||||
|
} |
||||||
|
|
||||||
|
void RandomTest::randScalar() |
||||||
|
{ |
||||||
|
Math::Random::RandomGenerator g; |
||||||
|
CORRADE_COMPARE_AS(Math::Random::randomScalar<Float>(g, -1.0, 1.0), 1.0f, Corrade::TestSuite::Compare::LessOrEqual); |
||||||
|
CORRADE_COMPARE_AS(Math::Random::randomScalar<Float>(g, -1.0, 1.0), -1.0f, Corrade::TestSuite::Compare::GreaterOrEqual); |
||||||
|
} |
||||||
|
|
||||||
|
void RandomTest::unitVector2() |
||||||
|
{ |
||||||
|
Math::Random::RandomGenerator g; |
||||||
|
CORRADE_COMPARE((Math::Random::randomUnitVector2(g)).length(), 1.0f); |
||||||
|
} |
||||||
|
void RandomTest::unitVector3() |
||||||
|
{ |
||||||
|
Math::Random::RandomGenerator g; |
||||||
|
|
||||||
|
CORRADE_COMPARE((Math::Random::randomUnitVector3(g)).length(), 1.0f); |
||||||
|
} |
||||||
|
|
||||||
|
void RandomTest::randomRotation() |
||||||
|
{ |
||||||
|
Math::Random::RandomGenerator g; |
||||||
|
|
||||||
|
CORRADE_COMPARE(Math::Random::randomRotation(g).length(), 1.0f); |
||||||
|
} |
||||||
|
|
||||||
|
void RandomTest::randomDiceChiSquare() |
||||||
|
{ |
||||||
|
// A step by step explanation
|
||||||
|
// https://rpg.stackexchange.com/questions/70802/how-can-i-test-whether-a-die-is-fair
|
||||||
|
Math::Random::RandomGenerator g; |
||||||
|
|
||||||
|
int error_count = 0; // We have 1 chance to over shoot. Thats why no repeated test.
|
||||||
|
|
||||||
|
const Int dice_side = 20; |
||||||
|
const Int expected = 10000; |
||||||
|
const Float thresholdfor100 = 36.191; |
||||||
|
|
||||||
|
for (auto i = 0; i < 100; i++) |
||||||
|
{ |
||||||
|
std::vector<Int> faces(dice_side, 0); |
||||||
|
for (std::size_t i = 0; i < expected * dice_side; i++) |
||||||
|
faces[Math::Random::randomScalar<Int>(g, 0, dice_side - 1)]++; |
||||||
|
Float chi_square = 0.0f; |
||||||
|
for (std::size_t i = 0; i < dice_side; i++) |
||||||
|
chi_square += Float(pow((faces[i] - expected), 2)) / expected; |
||||||
|
if (chi_square > thresholdfor100) |
||||||
|
error_count++; |
||||||
|
} |
||||||
|
CORRADE_COMPARE_AS(error_count, 2, Corrade::TestSuite::Compare::Less); |
||||||
|
} |
||||||
|
|
||||||
|
} // namespace
|
||||||
|
} // namespace Test
|
||||||
|
} // namespace Math
|
||||||
|
} // namespace Magnum
|
||||||
|
|
||||||
|
CORRADE_TEST_MAIN(Magnum::Math::Test::RandomTest) |
||||||
Loading…
Reference in new issue