glprng01.c 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228
  1. /* glprng01.c */
  2. /***********************************************************************
  3. * This code is part of GLPK (GNU Linear Programming Kit).
  4. *
  5. * This code is a modified version of the module GB_FLIP, a portable
  6. * pseudo-random number generator. The original version of GB_FLIP is
  7. * a part of The Stanford GraphBase developed by Donald E. Knuth (see
  8. * http://www-cs-staff.stanford.edu/~knuth/sgb.html).
  9. *
  10. * Note that all changes concern only external names, so this modified
  11. * version produces exactly the same results as the original version.
  12. *
  13. * Changes were made by Andrew Makhorin <mao@gnu.org>.
  14. *
  15. * GLPK is free software: you can redistribute it and/or modify it
  16. * under the terms of the GNU General Public License as published by
  17. * the Free Software Foundation, either version 3 of the License, or
  18. * (at your option) any later version.
  19. *
  20. * GLPK is distributed in the hope that it will be useful, but WITHOUT
  21. * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
  22. * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
  23. * License for more details.
  24. *
  25. * You should have received a copy of the GNU General Public License
  26. * along with GLPK. If not, see <http://www.gnu.org/licenses/>.
  27. ***********************************************************************/
  28. #include "glpenv.h"
  29. #include "glprng.h"
  30. #if 0
  31. int A[56] = { -1 };
  32. #else
  33. #define A (rand->A)
  34. #endif
  35. /* pseudo-random values */
  36. #if 0
  37. int *fptr = A;
  38. #else
  39. #define fptr (rand->fptr)
  40. #endif
  41. /* the next A value to be exported */
  42. #define mod_diff(x, y) (((x) - (y)) & 0x7FFFFFFF)
  43. /* difference modulo 2^31 */
  44. static int flip_cycle(RNG *rand)
  45. { /* this is an auxiliary routine to do 55 more steps of the basic
  46. recurrence, at high speed, and to reset fptr */
  47. int *ii, *jj;
  48. for (ii = &A[1], jj = &A[32]; jj <= &A[55]; ii++, jj++)
  49. *ii = mod_diff(*ii, *jj);
  50. for (jj = &A[1]; ii <= &A[55]; ii++, jj++)
  51. *ii = mod_diff(*ii, *jj);
  52. fptr = &A[54];
  53. return A[55];
  54. }
  55. /***********************************************************************
  56. * NAME
  57. *
  58. * rng_create_rand - create pseudo-random number generator
  59. *
  60. * SYNOPSIS
  61. *
  62. * #include "glprng.h"
  63. * RNG *rng_create_rand(void);
  64. *
  65. * DESCRIPTION
  66. *
  67. * The routine rng_create_rand creates and initializes a pseudo-random
  68. * number generator.
  69. *
  70. * RETURNS
  71. *
  72. * The routine returns a pointer to the generator created. */
  73. RNG *rng_create_rand(void)
  74. { RNG *rand;
  75. int i;
  76. rand = xmalloc(sizeof(RNG));
  77. A[0] = -1;
  78. for (i = 1; i <= 55; i++) A[i] = 0;
  79. fptr = A;
  80. rng_init_rand(rand, 1);
  81. return rand;
  82. }
  83. /***********************************************************************
  84. * NAME
  85. *
  86. * rng_init_rand - initialize pseudo-random number generator
  87. *
  88. * SYNOPSIS
  89. *
  90. * #include "glprng.h"
  91. * void rng_init_rand(RNG *rand, int seed);
  92. *
  93. * DESCRIPTION
  94. *
  95. * The routine rng_init_rand initializes the pseudo-random number
  96. * generator. The parameter seed may be any integer number. Note that
  97. * on creating the generator this routine is called with the parameter
  98. * seed equal to 1. */
  99. void rng_init_rand(RNG *rand, int seed)
  100. { int i;
  101. int prev = seed, next = 1;
  102. seed = prev = mod_diff(prev, 0);
  103. A[55] = prev;
  104. for (i = 21; i; i = (i + 21) % 55)
  105. { A[i] = next;
  106. next = mod_diff(prev, next);
  107. if (seed & 1)
  108. seed = 0x40000000 + (seed >> 1);
  109. else
  110. seed >>= 1;
  111. next = mod_diff(next, seed);
  112. prev = A[i];
  113. }
  114. flip_cycle(rand);
  115. flip_cycle(rand);
  116. flip_cycle(rand);
  117. flip_cycle(rand);
  118. flip_cycle(rand);
  119. return;
  120. }
  121. /***********************************************************************
  122. * NAME
  123. *
  124. * rng_next_rand - obtain pseudo-random integer in the range [0, 2^31-1]
  125. *
  126. * SYNOPSIS
  127. *
  128. * #include "glprng.h"
  129. * int rng_next_rand(RNG *rand);
  130. *
  131. * RETURNS
  132. *
  133. * The routine rng_next_rand returns a next pseudo-random integer which
  134. * is uniformly distributed between 0 and 2^31-1, inclusive. The period
  135. * length of the generated numbers is 2^85 - 2^30. The low order bits of
  136. * the generated numbers are just as random as the high-order bits. */
  137. int rng_next_rand(RNG *rand)
  138. { return
  139. *fptr >= 0 ? *fptr-- : flip_cycle(rand);
  140. }
  141. /***********************************************************************
  142. * NAME
  143. *
  144. * rng_unif_rand - obtain pseudo-random integer in the range [0, m-1]
  145. *
  146. * SYNOPSIS
  147. *
  148. * #include "glprng.h"
  149. * int rng_unif_rand(RNG *rand, int m);
  150. *
  151. * RETURNS
  152. *
  153. * The routine rng_unif_rand returns a next pseudo-random integer which
  154. * is uniformly distributed between 0 and m-1, inclusive, where m is any
  155. * positive integer less than 2^31. */
  156. #define two_to_the_31 ((unsigned int)0x80000000)
  157. int rng_unif_rand(RNG *rand, int m)
  158. { unsigned int t = two_to_the_31 - (two_to_the_31 % m);
  159. int r;
  160. xassert(m > 0);
  161. do { r = rng_next_rand(rand); } while (t <= (unsigned int)r);
  162. return r % m;
  163. }
  164. /***********************************************************************
  165. * NAME
  166. *
  167. * rng_delete_rand - delete pseudo-random number generator
  168. *
  169. * SYNOPSIS
  170. *
  171. * #include "glprng.h"
  172. * void rng_delete_rand(RNG *rand);
  173. *
  174. * DESCRIPTION
  175. *
  176. * The routine rng_delete_rand frees all the memory allocated to the
  177. * specified pseudo-random number generator. */
  178. void rng_delete_rand(RNG *rand)
  179. { xfree(rand);
  180. return;
  181. }
  182. /**********************************************************************/
  183. #if 0
  184. /* To be sure that this modified version produces the same results as
  185. the original version, run this validation program. */
  186. int main(void)
  187. { RNG *rand;
  188. int j;
  189. rand = rng_create_rand();
  190. rng_init_rand(rand, -314159);
  191. if (rng_next_rand(rand) != 119318998)
  192. { fprintf(stderr, "Failure on the first try!\n");
  193. return -1;
  194. }
  195. for (j = 1; j <= 133; j++) rng_next_rand(rand);
  196. if (rng_unif_rand(rand, 0x55555555) != 748103812)
  197. { fprintf(stderr, "Failure on the second try!\n");
  198. return -2;
  199. }
  200. fprintf(stderr, "OK, the random-number generator routines seem to"
  201. " work!\n");
  202. rng_delete_rand(rand);
  203. return 0;
  204. }
  205. #endif
  206. /* eof */