Add prng_jump to allow taking arbitrary steps through the PRNG
This commit is contained in:
parent
f87fe1cb99
commit
4c20dea060
@ -738,6 +738,8 @@ common.prng_new(seed, stream) @
|
||||
- random(max)
|
||||
- random(min, max)
|
||||
- returns a random number between min and max, defaulting to values of 0 and 1
|
||||
- jump(step)
|
||||
- jump ahead by step amount, can be negative
|
||||
|
||||
|
||||
str = common.net_pack(fmt, ...) @
|
||||
|
@ -698,6 +698,7 @@ void prng_seed(prng_t *rng, uint64_t seed, uint64_t stream);
|
||||
uint32_t prng_random(prng_t *rng);
|
||||
double prng_random_double(prng_t *rng);
|
||||
double prng_random_double_range(prng_t *rng, double minimum, double maximum);
|
||||
void prng_jump(prng_t *rng, uint64_t step);
|
||||
|
||||
// vecmath.c
|
||||
vec4f_t mtx_apply_vec(matrix_t *mtx, vec4f_t *vec);
|
||||
|
@ -21,6 +21,12 @@
|
||||
// taken, but without a way to take arbitrary steps, this is pretty useless.
|
||||
// It would also require storing more state, but it's only 128 bits at the
|
||||
// moment, which is already pretty small for a PRNG.
|
||||
// * We have jump now, but if we allow seeding with strings, then we could get
|
||||
// initial seeds that can't fit in Lua anyway.
|
||||
// * We could return a hex string or something, but that would require us to
|
||||
// accept hex strings only. How would we deal with non-hex strings? Separate
|
||||
// function for hex strings? get_state/set_state, but allow arbitrary strings
|
||||
// (and hash them somehow) in seed?
|
||||
// * We could add a clone function that creates a new one with the same state.
|
||||
// Useless for networking though.
|
||||
|
||||
@ -67,6 +73,15 @@ int icelua_fn_cl_prng_random(lua_State *L)
|
||||
return 1;
|
||||
}
|
||||
|
||||
int icelua_fn_cl_prng_jump(lua_State *L)
|
||||
{
|
||||
int top = icelua_assert_stack(L, 1, 1);
|
||||
prng_t *rng = lua_touserdata(L, lua_upvalueindex(1));
|
||||
uint64_t step = (uint64_t)lua_tonumber(L, 1);
|
||||
prng_jump(rng, step);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int icelua_fn_common_prng_new(lua_State *L)
|
||||
{
|
||||
int top = icelua_assert_stack(L, 0, 2);
|
||||
@ -96,6 +111,11 @@ int icelua_fn_common_prng_new(lua_State *L)
|
||||
lua_pushcclosure(L, &icelua_fn_cl_prng_random, 1); // Create closure, 1 upvalue (the RNG state)
|
||||
lua_settable(L, -4); // Insert closure into table
|
||||
|
||||
lua_pushstring(L, "jump"); // Function name
|
||||
lua_pushvalue(L, -2); // Duplicate RNG reference to top of stack
|
||||
lua_pushcclosure(L, &icelua_fn_cl_prng_jump, 1); // Create closure, 1 upvalue (the RNG state)
|
||||
lua_settable(L, -4); // Insert closure into table
|
||||
|
||||
// Pop the RNG state off the stack.
|
||||
lua_pop(L, 1);
|
||||
|
||||
|
23
src/random.c
23
src/random.c
@ -20,8 +20,9 @@
|
||||
// Basic implementation of the PCG psuedorandom number generator.
|
||||
// Specifically, PCG-XSH-RR, as that's what the paper recommends for general use.
|
||||
// This may diverge from the official implementations, but I wanted to give credit.
|
||||
// TODO: We could drop the stream part and use a set value. We have no real use
|
||||
// for it, and it complicates the API. It does give us more possible sequences though.
|
||||
// TODO: We could drop the stream part and use a set value. It's useful for seeding
|
||||
// a new PRNG from an existing one's output, but if people just want random seeds,
|
||||
// we can seed from time.
|
||||
// On the other hand, using a set value does allow us to ensure that a relatively
|
||||
// good value is chosen (co-primes, etc.).
|
||||
|
||||
@ -64,3 +65,21 @@ double prng_random_double_range(prng_t *rng, double minimum, double maximum) {
|
||||
double d = (double)random / UINT32_MAX;
|
||||
return minimum + d * (maximum - minimum);
|
||||
}
|
||||
|
||||
// Magical maths thanks to Forrest B. Brown, "Random number Generation with Arbitrary Strides"
|
||||
void prng_jump(prng_t *rng, uint64_t step) {
|
||||
uint64_t m_result = 1;
|
||||
uint64_t c_result = 0;
|
||||
uint64_t stream = rng->stream;
|
||||
uint64_t multiplier = PRNG_MULTIPLIER;
|
||||
while (step > 0) {
|
||||
if (step & 1) {
|
||||
m_result *= multiplier;
|
||||
c_result = c_result * multiplier + stream;
|
||||
}
|
||||
stream = (multiplier + 1) * stream;
|
||||
multiplier *= multiplier;
|
||||
step >>= 1;
|
||||
}
|
||||
rng->state = m_result * rng->state + c_result;
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user