<p>This problem is akin to rolling a <em>k</em>-sided die given only a <em>p</em>-sided die, without wasting randomness.</p>
<p>In this sense, by Lemma 3 in &quot;<a href="https://perso.math.u-pem.fr/kloeckner.benoit/papiers/DiceSimulation.pdf" rel="nofollow noreferrer">Simulating a dice with a dice</a>&quot; by B. Kloeckner, this waste is inevitable unless &quot;every prime number dividing <em>k</em> also divides <em>p</em>&quot;. Thus, for example, if <em>p</em> is a power of 2 (and any block of random bits is the same as rolling a die with a power of 2 number of faces) and <em>k</em> has prime factors other than 2, the best you can do is get arbitrarily close to no waste of randomness, such as by batching multiple rolls of the <em>p</em>-sided die until <em>p</em>^<em>n</em> is &quot;close enough&quot; to a power of <em>k</em>.</p>
<p>Let me also go over some of your concerns about regenerating random numbers:</p>
<ul>
<li>&quot;Reducing the period&quot;: Besides batching of bits, this concern can be dealt with in several ways:
<ul>
<li>Use a <a href="https://peteroupc.github.io/hqprng.html" rel="nofollow noreferrer">PRNG with a bigger &quot;period&quot;</a> (maximum cycle length).</li>
<li>Add a <a href="https://peteroupc.github.io/bdshuffle.html" rel="nofollow noreferrer">Bays–Durham shuffle</a> to the PRNG's implementation.</li>
<li>Use a &quot;true&quot; random number generator; this is not trivial.</li>
<li>Employ <em>randomness extraction</em>, which is discussed in <a href="https://arxiv.org/abs/1502.02539" rel="nofollow noreferrer">Devroye and Gravel 2015-2020</a> and in my <a href="https://peteroupc.github.io/randextract.html" rel="nofollow noreferrer">Note on Randomness Extraction</a>.  However, randomness extraction is pretty involved.</li>
<li>Ignore the problem, especially if it isn't a security application or serious simulation.</li>
</ul>
</li>
<li>&quot;Time to generate new numbers until one is in the right range&quot;: If you want unbiased random numbers, then any algorithm that does so will generally have to run forever in the worst case. Again, by Lemma 3, the algorithm will run forever in the worst case unless &quot;every prime number dividing <em>k</em> also divides <em>p</em>&quot;, which is not the case if, say, <em>k</em> is 10 and <em>p</em> is 32.</li>
</ul>
<p>See also the question: <a href="https://stackoverflow.com/questions/6046918/how-to-generate-a-random-integer-in-the-range-0-n-from-a-stream-of-random-bits">How to generate a random integer in the range [0,n] from a stream of random bits without wasting bits?</a>, especially my answer there.</p>
What is the proper method of constraining a pseudo-random number to a smaller range?
<p>What is the best way to constrain the values of a PRNG to a smaller range? If you use modulus and the old max number is not evenly divisible by the new max number you bias toward the <code>0</code> through <code>(old_max - new_max - 1)</code>. I assume the best way would be something like this (this is floating point, not integer math)</p>
<pre><code>random_num = PRNG() / max_orginal_range * max_smaller_range
</code></pre>
<p>But something in my gut makes me question that method (maybe floating point implementation and representation differences?).</p>
<p>The random number generator will produce consistent results across hardware and software platforms, and the constraint needs to as well.</p>
<p>I was right to doubt the pseudocode above (but not for the reasons I was thinking). MichaelGG's <a href="https://stackoverflow.com/questions/734482/what-is-the-proper-method-of-constraining-a-pseduo-random-number-to-a-smaller-ran/739945#739945">answer</a> got me thinking about the problem in a different way. I can model it using smaller numbers and test every outcome. So, let's assume we have a PRNG that produces a random number between 0 and 31 and you want the smaller range to be 0 to 9. If you use modulus you bias toward 0, 1, 2, and 3. If you use the pseudocode above you bias toward 0, 2, 5, and 7. I don't think there can be a good way to map one set into the other. The best that I have come up with so far is to regenerate the random numbers that are greater than <code>old_max/new_max</code>, but that has deep problems as well (reducing the period, time to generate new numbers until one is in the right range, etc.).</p>
<p>I think I may have naively approached this problem. It may be time to start some serious research into the literature (someone has to have tackled this before).</p>