You're close, but the problem with your answer is that there is more than one way to write a number as a sum of two other numbers.
If m<n
, then this works because the numbers 0,1,...,m-1
appear each with equal probability, and the algorithm terminates almost surely.
This answer does not work in general because there is more than one way to write a number as a sum of two other numbers. For instance, there is only one way to get 0
but there are many many ways to get m/2
, so the probabilities will not be equal.
Example: n = 2
and m=3
0 = 0+0
1 = 1+0 or 0+1
2 = 1+1
so the probability distribution from your method is
P(0)=1/4
P(1)=1/2
P(2)=1/4
which is not uniform.
To fix this, you can use unique factorization. Write m
in base n
, keeping track of the largest needed exponent, say e
. Then, find the biggest multiple of m
that is smaller than n^e
, call it k
. Finally, generate e
numbers with randn()
, take them as the base n
expansion of some number x
, if x < k*m
, return x
, otherwise try again.
Assuming that m < n^2
, then
int randm() {
// find largest power of n needed to write m in base n
int e=0;
while (m > n^e) {
++e;
}
// find largest multiple of m less than n^e
int k=1;
while (k*m < n^2) {
++k
}
--k; // we went one too far
while (1) {
// generate a random number in base n
int x = 0;
for (int i=0; i<e; ++i) {
x = x*n + randn();
}
// if x isn't too large, return it x modulo m
if (x < m*k)
return (x % m);
}
}