Pick a random number between 0 and n using a constant source of randomness
x86 machines with rdrand
instruction, 10 bytes
BITS 64
_try_again:
rdrand eax
jnc _try_again
cmp eax, edi
ja _try_again
ret
0FC7F0 73FB 39F8 77F7 C3
The input is in the register rdi
and the output in rax
.
This respects the SYS V AMD64 ABI so the code effectively implement a C function
unsigned int foo(unsigned int max);
with 32-bit ints.
The instruction rdrand
is described by Intel
RDRAND
returns random numbers that are supplied by a cryptographically secure, deterministic random bit generator DRBG. The DRBG is designed to meet the NIST SP 800-90A standard.
Dealing with CSRNG it is implicit that the distribution is uniform, anyway, quoting the NIST SP 800-90A:
A random number is an instance of an unbiased random variable, that is, the output produced by a uniformly distributed random process.
The procedure generates a random number and if it is non-strictly greater than the input it is returned.
Otherwise, the process is reiterated.
Since eax
is 32-bit, rdrand
returns a number between 0 and 232-1, so for every n in [0, 232-1] the number of expected iterations is 232/(n+1) which is defined for all n in [0, 230).
Jelly, 7 6 bytes
⁴!!X%‘
Thanks to @JonathanAllan for golfing off 1 byte!
Cannot be run on TIO because (16!)! is a huge number.
How it works
⁴!!X%‘ Main link. Argument: n
⁴ Set the return value to 16.
!! Compute (16!)!.
X Pseudo-randomly choose an integer between 1 and (16!)!.
Since (16!)! is evenly divisible by any k ≤ 2**30, it is evenly divisible
by n+1.
%‘ Take the result modulo n+1.
Mathematica, 29 bytes
Based on Dennis's Jelly answer.
RandomInteger[2*^9!-1]~Mod~#&
I wouldn't recommend actually running this. 2e9!
is a pretty big number...
It turns out to be shortest to generate a huge number that is divisible by all possible inputs and the map the result to the required range with a simple modulo.
Rejection Sampling, 34 bytes
My old approach that led to somewhat more interesting code:
13!//.x_/;x>#:>RandomInteger[13!]&
Basic rejection sampling. We initialise the output to 13! (which is larger than the maximum input 230) and then repeatedly replace it with a random integer between 0 and 13! as long as the value is bigger than the input.