Created
September 14, 2024 04:42
-
-
Save qqwqqw689/8aec98a8f187126c0bf0e559a842e1d0 to your computer and use it in GitHub Desktop.
Portable pseudorandom number generator in c.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* Portable pseudorandom number generator | |
* Based on a 24,55 Fibonacci generator, using 55/503 rejection | |
* c.f.- TAoCP, 3.2.2(7), for j=24,k=55,m=2^64 | |
* | |
* THIS FILE IS PUBLIC DOMAIN CODE. | |
* | |
* Written by Bob Adolf. | |
* Attribution is appreciated but by no means legally required. | |
* | |
* This function is sufficient for most non-crypto applications. | |
* It passes all but one of George Marsaglia's "diehard" randomness tests. | |
* (overlapping 5-tuple permutations, which is allegedly buggy) | |
*/ | |
#ifndef __PRNG_H__ | |
#define __PRNG_H__ | |
#include <inttypes.h> | |
#include <stdint.h> | |
#include <stdio.h> | |
#include <stdlib.h> | |
#define LAG1 (UINT16_C(24)) | |
#define LAG2 (UINT16_C(55)) | |
#define RAND_SSIZE ((UINT16_C(1)) << 6) | |
#define RAND_SMASK (RAND_SSIZE - 1) | |
#define RAND_EXHAUST_LIMIT LAG2 | |
// 10x is a heuristic, it just needs to be large enough to remove correlation | |
#define RAND_REFILL_COUNT ((LAG2 * 10) - RAND_EXHAUST_LIMIT) | |
struct prng_rand_t | |
{ | |
uint64_t s[RAND_SSIZE]; // Lags | |
uint_fast16_t i; // Location of the current lag | |
uint_fast16_t c; // Exhaustion count | |
}; | |
#define PRNG_RAND_MAX UINT64_MAX | |
static inline uint64_t prng_rand(struct prng_rand_t* state) | |
{ | |
uint_fast16_t i; | |
uint_fast16_t r, new_rands = 0; | |
if (!state->c) | |
{ // Randomness exhausted, run forward to refill | |
new_rands += RAND_REFILL_COUNT + 1; | |
state->c = RAND_EXHAUST_LIMIT - 1; | |
} | |
else | |
{ | |
new_rands = 1; | |
state->c--; | |
} | |
for (r = 0; r < new_rands; r++) | |
{ | |
i = state->i; | |
state->s[i & RAND_SMASK] = state->s[(i + RAND_SSIZE - LAG1) & RAND_SMASK] | |
+ state->s[(i + RAND_SSIZE - LAG2) & RAND_SMASK]; | |
state->i++; | |
} | |
return state->s[i & RAND_SMASK]; | |
} | |
static inline void prng_srand(uint64_t seed, struct prng_rand_t* state) | |
{ | |
uint_fast16_t i; | |
// Naive seed | |
state->c = RAND_EXHAUST_LIMIT; | |
state->i = 0; | |
state->s[0] = seed; | |
for (i = 1; i < RAND_SSIZE; i++) | |
{ | |
// Arbitrary magic, mostly to eliminate the effect of low-value seeds. | |
// Probably could be better, but the run-up obviates any real need to. | |
state->s[i] = i * (UINT64_C(2147483647)) + seed; | |
} | |
// Run forward 10,000 numbers | |
for (i = 0; i < 10000; i++) | |
{ | |
prng_rand(state); | |
} | |
} | |
// Clean up our macros | |
#undef LAG1 | |
#undef LAG2 | |
#undef RAND_SSIZE | |
#undef RAND_SMASK | |
#undef RAND_EXHAUST_LIMIT | |
#undef RAND_REFILL_COUNT | |
// PRNG_RAND_MAX is exported | |
#endif |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment