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(max)
|
||||||
- random(min, max)
|
- random(min, max)
|
||||||
- returns a random number between min and max, defaulting to values of 0 and 1
|
- 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, ...) @
|
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);
|
uint32_t prng_random(prng_t *rng);
|
||||||
double prng_random_double(prng_t *rng);
|
double prng_random_double(prng_t *rng);
|
||||||
double prng_random_double_range(prng_t *rng, double minimum, double maximum);
|
double prng_random_double_range(prng_t *rng, double minimum, double maximum);
|
||||||
|
void prng_jump(prng_t *rng, uint64_t step);
|
||||||
|
|
||||||
// vecmath.c
|
// vecmath.c
|
||||||
vec4f_t mtx_apply_vec(matrix_t *mtx, vec4f_t *vec);
|
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.
|
// 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
|
// It would also require storing more state, but it's only 128 bits at the
|
||||||
// moment, which is already pretty small for a PRNG.
|
// 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.
|
// * We could add a clone function that creates a new one with the same state.
|
||||||
// Useless for networking though.
|
// Useless for networking though.
|
||||||
|
|
||||||
@ -67,6 +73,15 @@ int icelua_fn_cl_prng_random(lua_State *L)
|
|||||||
return 1;
|
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 icelua_fn_common_prng_new(lua_State *L)
|
||||||
{
|
{
|
||||||
int top = icelua_assert_stack(L, 0, 2);
|
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_pushcclosure(L, &icelua_fn_cl_prng_random, 1); // Create closure, 1 upvalue (the RNG state)
|
||||||
lua_settable(L, -4); // Insert closure into table
|
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.
|
// Pop the RNG state off the stack.
|
||||||
lua_pop(L, 1);
|
lua_pop(L, 1);
|
||||||
|
|
||||||
|
23
src/random.c
23
src/random.c
@ -20,8 +20,9 @@
|
|||||||
// Basic implementation of the PCG psuedorandom number generator.
|
// Basic implementation of the PCG psuedorandom number generator.
|
||||||
// Specifically, PCG-XSH-RR, as that's what the paper recommends for general use.
|
// 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.
|
// 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
|
// TODO: We could drop the stream part and use a set value. It's useful for seeding
|
||||||
// for it, and it complicates the API. It does give us more possible sequences though.
|
// 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
|
// On the other hand, using a set value does allow us to ensure that a relatively
|
||||||
// good value is chosen (co-primes, etc.).
|
// 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;
|
double d = (double)random / UINT32_MAX;
|
||||||
return minimum + d * (maximum - minimum);
|
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