From 563ec5eeddc69e7bb8f66624868d3dd6cc0da586 Mon Sep 17 00:00:00 2001 From: Richard Thier Date: Tue, 1 Apr 2025 19:21:39 +0200 Subject: [PATCH] Initial commit --- fastrand.h | 51 +++++++++++++++++++++++++++++++++++++++++++++++++++ main.c | 44 ++++++++++++++++++++++++++++++++++++++++++++ makefile | 4 ++++ 3 files changed, 99 insertions(+) create mode 100644 fastrand.h create mode 100644 main.c create mode 100644 makefile diff --git a/fastrand.h b/fastrand.h new file mode 100644 index 0000000..c5b18b9 --- /dev/null +++ b/fastrand.h @@ -0,0 +1,51 @@ +/* Single-header LCG random generator and faster-than-modulo rand_until, rand_between */ +/* LICENCE: Feel free to use it but please mention MagosIT Kft or its youtube channel */ +#ifndef FAST_RAND_H +#define FAST_RAND_H + +#include +#ifndef NO_CSTDLIB +#include +#endif /* NO_CSTDLIB */ + +/* Currently a single integer is enough */ +typedef uint32_t rand_state; + +/** Creates a random number generator state with given seed */ +static inline rand_state init_rand_with(uint32_t seed) { + return seed; +} + +#ifndef NO_CSTDLIB +/** Creates a random number generator state with arc4random() which does not need seeding as it uses system etropy */ +static inline rand_state init_rand() { + return arc4random(); +} +#endif /* NO_CSTDLIB */ + +// 32-bit LCG +static inline uint32_t lcg(uint32_t *state) { + *state = *state * 1664525u + 1013904223u; + return *state; +} + +/** Pick a "reasonably random" number in [0, until-1] without modulus */ +static inline uint32_t rand_until(uint32_t *state, uint32_t until) { + uint32_t rand = lcg(state); + // Multiply by "until", take the upper 32 bits of the 64-bit result + return (uint32_t)(((uint64_t)rand * until) >> 32); +} + +/** + * Pick a "reasonably random" number in [from, to) without modulus. + * + * @param state The random state - See: init_rand() and init_rand_with(seed) + * @param from The smallest possible value + * @param to The biggest possible value + 1 + * @returns A value in [from, to) interval + */ +static inline uint32_t rand_between(uint32_t *state, uint32_t from, uint32_t to) { + return from + rand_until(state, to - from); +} + +#endif /* FAST_RAND_H */ diff --git a/main.c b/main.c new file mode 100644 index 0000000..826b7a9 --- /dev/null +++ b/main.c @@ -0,0 +1,44 @@ +// gcc nomod.c -o nomod; ./nomod + +/* +Explanation: + +* rand is treated as a 32-bit fixed-point number in [0, 1) (implicitly scaled by 2³²). +* Multiplying by len scales it to [0, len). +* Shifting right by 32 bits extracts the integer part (0..len-1). + +I guess I will go with decimal system for understanding this... +So lets say I have two-digit decimal numbers from 00..99 this +means that multiplying two of these should always fit in a +four-digit decimal number isn't it? from 0000..9999 because +99*99 is 9801 so it fits in and its the biggest possible pairs +to multiply. + +Now lets have a "len" number with also at most two digits, let +it be 20 - this will be our "modulus". Lets multiply a random +two-digit number with this. We get lets say 71*20 which is 1400. +Shifting two digits to the right it becomes 14. If I do 99*20 +(which is biggest) I get 1980 so when shiften decimally it +becomes 19 (which is again smaller than 20). + +It looks like the random numbers from 00..99 indeed start to map +onto 00..19 in this case. Very fascinating! Just avoid cryptin' +*/ + +#include +#include +#include "fastrand.h" + +int main() { + uint32_t from = 12; + uint32_t to = 32; + int n = 30; + + rand_state rs = init_rand(); + for(int i = 0; i < n; ++i) { + uint32_t choice = rand_between(&rs, from, to); + printf("rand [%u, %u): %u\n", from, to, choice); + } + + return 0; +} diff --git a/makefile b/makefile new file mode 100644 index 0000000..5278ea7 --- /dev/null +++ b/makefile @@ -0,0 +1,4 @@ +debug: + gcc main.c -g -o main +release: + gcc main.c -O2 -o main