Improve generation of pseudo-random integers. (#99720, Morten Welinder

2002-12-10  Sebastian Wilhelmi  <wilhelmi@ira.uka.de>

        * glib/grand.c (g_rand_int_range): Improve generation of
        pseudo-random integers. (#99720, Morten Welinder <terra@diku.dk>)

        * README.in, docs/reference/glib/running.sgml,
        docs/reference/glib/tmpl/random_numbers.sgml,
        docs/reference/glib/changes.sgml: Added notes about the new
        algorithm.
This commit is contained in:
Sebastian Wilhelmi 2002-12-10 13:51:06 +00:00 committed by Sebastian Wilhelmi
parent 1c462ac694
commit df9d9332f8
2 changed files with 67 additions and 33 deletions

View File

@ -36,12 +36,18 @@ yield equally distributed numbers.
GLib changed the seeding algorithm for the pseudo-random number GLib changed the seeding algorithm for the pseudo-random number
generator Mersenne Twister, as used by <structname>GRand</structname> generator Mersenne Twister, as used by <structname>GRand</structname>
and <structname>GRandom</structname>. This was necessary, because some and <structname>GRandom</structname>. This was necessary, because some
seeds would yield very bad pseudo-random streams. The original seeding seeds would yield very bad pseudo-random streams. Also the
algorithm, as found in GLib 2.0.x, can be used instead of the new one pseudo-random integers generated by
by setting the environment variable <envar>G_RANDOM_VERSION</envar> to <function>g_rand*_int_range()</function> will have a
the value of '2.0'. Use the GLib-2.0 algorithm only if you have slightly better equal distribution with the new version of GLib.
sequences of numbers generated with Glib-2.0 that you need to </para>
reproduce exactly.
<para>
The original seeding and generation algorithms, as found in GLib 2.0.x,
can be used instead of the new ones by setting the environment variable
<envar>G_RANDOM_VERSION</envar> to the value of '2.0'. Use the
GLib-2.0 algorithms only if you have sequences of numbers generated
with Glib-2.0 that you need to reproduce exactly.
</para> </para>
<!-- ##### SECTION See_Also ##### --> <!-- ##### SECTION See_Also ##### -->

View File

@ -285,34 +285,62 @@ g_rand_int_range (GRand* rand, gint32 begin, gint32 end)
g_return_val_if_fail (rand != NULL, begin); g_return_val_if_fail (rand != NULL, begin);
g_return_val_if_fail (end > begin, begin); g_return_val_if_fail (end > begin, begin);
/* All tricks doing modulo calculations do not have a perfect switch (get_random_version ())
* distribution -> We must use the slower way through gdouble for
* maximal quality. */
if (dist <= 0x10000L) /* 2^16 */
{ {
/* This method, which only calls g_rand_int once is only good case 20:
* for (end - begin) <= 2^16, because we only have 32 bits set if (dist <= 0x10000L) /* 2^16 */
* from the one call to g_rand_int (). */ {
/* This method, which only calls g_rand_int once is only good
/* we are using (trans + trans * trans), because g_rand_int only * for (end - begin) <= 2^16, because we only have 32 bits set
* covers [0..2^32-1] and thus g_rand_int * trans only covers * from the one call to g_rand_int (). */
* [0..1-2^-32], but the biggest double < 1 is 1-2^-52.
*/ /* we are using (trans + trans * trans), because g_rand_int only
* covers [0..2^32-1] and thus g_rand_int * trans only covers
gdouble double_rand = g_rand_int (rand) * * [0..1-2^-32], but the biggest double < 1 is 1-2^-52.
(G_RAND_DOUBLE_TRANSFORM + */
G_RAND_DOUBLE_TRANSFORM * G_RAND_DOUBLE_TRANSFORM);
gdouble double_rand = g_rand_int (rand) *
random = (gint32) (double_rand * dist); (G_RAND_DOUBLE_TRANSFORM +
} G_RAND_DOUBLE_TRANSFORM * G_RAND_DOUBLE_TRANSFORM);
else
{ random = (gint32) (double_rand * dist);
/* Now we use g_rand_double_range (), which will set 52 bits for }
us, so that it is safe to round and still get a decent else
distribution */ {
random = (gint32) g_rand_double_range (rand, 0, dist); /* Now we use g_rand_double_range (), which will set 52 bits for
} us, so that it is safe to round and still get a decent
distribution */
random = (gint32) g_rand_double_range (rand, 0, dist);
}
break;
case 22:
if (dist == 0)
random = 0;
else
{
/* maxvalue is set to the predecessor of the greatest
* multiple of dist less or equal 2^32. */
guint32 maxvalue;
if (dist <= 0x80000000u) /* 2^31 */
{
/* maxvalue = 2^32 - 1 - (2^32 % dist) */
guint32 leftover = (0x80000000u % dist) * 2;
if (leftover >= dist) leftover -= dist;
maxvalue = 0xffffffffu - leftover;
}
else
maxvalue = dist - 1;
do
random = g_rand_int (rand);
while (random > maxvalue);
random %= dist;
}
break;
default:
g_assert_not_reached ();
}
return begin + random; return begin + random;
} }