Initial commit

This commit is contained in:
Richard Thier 2025-04-01 19:21:39 +02:00
commit 563ec5eedd
3 changed files with 99 additions and 0 deletions

51
fastrand.h Normal file
View File

@ -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 <stdint.h>
#ifndef NO_CSTDLIB
#include <stdlib.h>
#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 */

44
main.c Normal file
View File

@ -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 <stdio.h>
#include <stdint.h>
#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;
}

4
makefile Normal file
View File

@ -0,0 +1,4 @@
debug:
gcc main.c -g -o main
release:
gcc main.c -O2 -o main