Imagine that we're trying to create a function to generate a random string conforming to a user-supplied alphabet. That way, users can generate random strings with given characters. Something like:
string random_string(int length, string alphabet)
Now, the alphabet could be any combination of characters from length 2 to 256. So, to generate hex characters, you'd pass the string "0123456789abcdef"
...
So the question at hand is which is the better unbiased method to generate the desired random data, assuming a stream based full-byte random source (/dev/urandom, etc)...
Base Conversion
First, we compute how many "bytes" we need from the stream:
int bits_per_block = floor(log(strlen(alphabet), 2) + 1);
int bytes = ceil(length * bits_per_block / 8);
Then, fetch randomness from the stream up to the required number of bytes, and store it in a "buffer".
Now, walk the buffer and do a numeric base conversion between 256 and the destination base. Then substitute the destination base for the characters from the alphabet. That's it.
Note that you'd need to convert the entire random buffer, and then cut off the most significant part of the result to size it properly (because the ceil call can cause it to over-generate). This is good, because it means that every generated character of the result will be unbiased.
This has the advantage that you're always fetching a known amount of randomness, up to a maximum of 1 extra byte.
It has a disadvantage that it requires twice the memory (one for the buffer, and one for the return), and that it's fairly complex (base conversion is not trivial).
Here's a sample implementation in PHP of the base conversion algorithm, and here it is in action.
Value Picking
This is the standard integer generation algorithm. Basically, we'd have an algorithm like:
character generateByte(string alphabet) {
int len = strlen(alphabet);
int max = 256 - (len % 256);
do {
int byte = get_random_byte();
} while (byte > max);
return alphabet[byte % len];
}
Note that the max ensures that the maximum generated int is divisible by the len parameter. So if we were generating into base 10, max would be 250, and byte % 10
would therefore be unbiased.
It has the advantage that it's a lot easier to implement. But it has a disadvantage that it could not terminate, or use a LOT of randomness to generate a string, especially for some odd bases...
So, is the Base Conversion an appropriate route to take? Or should we stick with the "value picking" approach?
(len % 256)
should actually be(256 % len)
$\endgroup$