pioneer/src/Random.h

244 lines
5.2 KiB
C++

// Copyright © 2008-2021 Pioneer Developers. See AUTHORS.txt for details
// Licensed under the terms of the GPL v3. See licenses/GPL-3.txt
// A deterministic random number generator for use by the rest of the
// engine. It *should* give the same output for a given input regardless
// of the processor architecture. If it doesn't then it needs to be fixed!
// Based on: http://www.javamex.com/tutorials/random_numbers/xorshift.shtml
#ifndef RAND_H
#define RAND_H
#include <assert.h>
#include <cmath>
#include "RefCounted.h"
#include "fixed.h"
extern "C" {
#include "jenkins/lookup3.h"
}
#define PCG_LITTLE_ENDIAN 1
#include "pcg-cpp/pcg_random.hpp"
// A deterministic random number generator
class Random : public RefCounted {
pcg32 mPCG;
// For storing second rand from Normal
bool cached;
double z1;
public:
//
// Constructors
//
// Construct a new random generator using the given seed
Random(const Uint32 initialSeed = 0xabcd1234)
{
seed(initialSeed);
}
// Construct a new generator given an array of 32-bit seeds.
Random(const Uint32 *const seeds, size_t length)
{
seed(seeds, length);
}
// Construct a new random generator from an array of 64-bit
// seeds.
Random(const Uint64 *const seeds, size_t length)
{
seed(reinterpret_cast<const Uint32 *>(seeds), length * 2);
}
//
// Seed functions
//
// Seed the RNG using the hash of the given array of seeds.
void seed(const Uint32 *const seeds, size_t length)
{
const Uint32 hash = lookup3_hashword(seeds, length, 0);
mPCG.seed(hash);
cached = false;
}
// Seed using an array of 64-bit integers
void seed(const Uint64 *const seeds, size_t length)
{
seed(reinterpret_cast<const Uint32 *>(seeds), length * 2);
}
// Seed using a single 32-bit integer
void seed(const Uint32 value)
{
seed(&value, 1);
}
//
// Number generators.
//
// Starting from a given seed value the generator will return the
// same sequence of numbers. Unless otherwise stated the numbers
// are 32-bit and each call consumes one
//
// Get the next integer from the sequence
// interval [0, 2**32)
inline Uint32 Int32()
{
return mPCG();
}
// Pick an integer like you're rolling a "choices" sided die,
// a 6 sided die would return a number between 0 and 5.
// interval [0, choices)
inline Uint32 Int32(const int choices)
{
return Int32() % choices;
}
// Pick a number between min and max, inclusive.
// interval [min, max]
inline int Int32(const int min, const int max)
{
return (Int32() % (1 + max - min)) + min;
}
// Pick a number in the half open interval [0, 1)
inline double Double()
{
return double(Int32()) * (1. / 4294967296.); // divided by 2^32
}
// Pick a number in the closed interval [0, 1]
inline double Double_closed()
{
return double(Int32()) * (1. / 4294967295.); // divided by 2^32 - 1
}
// Pick a number in the open interval (0, 1)
inline double Double_open()
{
return (double(Int32()) + .5) * (1. / 4294967296.); // divided by 2^32
}
// Pick a 53-bit resolution double in the half open interval [0, 1)
// This method consumes two 32-bit numbers from the sequence.
inline double Double53()
{
return (double(Int32() >> 5) * 67108864. + double(Int32() >> 6)) * (1. / 9007199254740992.);
}
// Pick a number in the half-open interval [min, limit)
inline double Double(double min, double limit)
{
return Double(limit - min) + min;
}
// Pick a number in the half-open interval [0, limit)
inline double Double(double limit)
{
return limit * Double();
}
// Pick a number between 0 and max inclusive
// interval [0, max]
inline double Double_closed(double max)
{
return max * Double_closed();
}
// Pick a number between min and max inclusive
// interval [min, max]
inline double Double_closed(double min, double max)
{
return Double_closed(max - min) + min;
}
// interval [0,1)
inline double NDouble(int p)
{
double o = Double(1.0);
while (--p > 0)
o *= Double(1.0);
return o;
}
// Normal distribution with zero mean, and unit variance
inline double Normal()
{
return Normal(0.0, 1.0);
}
// Normal distribution with unit variance
inline double Normal(const double mean)
{
return Normal(mean, 1.0);
}
//Normal (Gauss) distribution
inline double Normal(const double mean, const double stddev)
{
//https://en.wikipedia.org/wiki/Box-Muller_transform#Polar_form
double u, v, s, z0;
if (cached) {
z0 = z1;
cached = false;
} else {
do {
u = Double_closed(-1, 1);
v = Double_closed(-1, 1);
s = u * u + v * v;
} while (s >= 1.0);
s = sqrt((-2.0 * log(s)) / s);
z0 = u * s;
z1 = v * s;
cached = true;
}
return mean + z0 * stddev;
}
inline int Poisson(const double lambda)
{
int k = 0;
double p = Double();
const double target = exp(-lambda);
while (p > target) {
k += 1;
p *= Double();
}
return k;
}
// Pick a fixed-point integer half open interval [0,1)
inline fixed Fixed()
{
return fixed(Int32());
}
// interval [0,1)
inline fixed NFixed(int p)
{
fixed o = Fixed();
while (--p > 0)
o *= Fixed();
return o;
}
const pcg32 &GetPCG() const { return mPCG; }
private:
Random(const Random &); // copy constructor not defined
void operator=(const Random &); // assignment operator not defined
};
#endif // RAND_H