random.red 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117
  1. module random; % Random Number Generator.
  2. % Author: C.J. Neerdaels, with adjustments by A.C. Norman.
  3. % Entrypoints:
  4. % random_new_seed n Re-seed the random number generator
  5. % random n return next value (range 0 <= r < n)
  6. % next!-random!-number()
  7. % return next value in range 0<=r<randommodulus!*
  8. % Note that random_new_seed is automatically called with argument 1 if
  9. % the user does not explicitly call it with some other argument, and
  10. % that resetting the seed in the generator is a fairly expensive
  11. % business. % The argument to random() may be integer or floating, large
  12. % or small, but should be strictly positive.
  13. global '(unidev_vec!* randommodulus!*);
  14. global '(unidev_fac!* unidev_next!* unidev_nextp!* unidev_mj!*);
  15. global '(randomseed!*);
  16. unidev_vec!* := mkvect(54)$
  17. randommodulus!* := 100000000; % This is a fixnum in PSL and CSL (10^8).
  18. unidev_fac!* := 1.0/randommodulus!*;
  19. % The following two lines are for speed fanatics - they should be OK
  20. % with both PSL and CSL (as of June 1993). They can be removed with no
  21. % serious effect to code that is not random-number intensive.
  22. % compiletime on fastfor;
  23. % compiletime flag('(randommodulus!* unidev_fac!*), 'constant!?);
  24. flag('(random random_new_seed),'opfn); % Make symbolic operators.
  25. symbolic procedure random_new_seed offset;
  26. % Sets the unidev seed to offset
  27. begin scalar mj, mk, ml, ii;
  28. if not fixp offset or offset <= 0
  29. then typerr(offset,"positive integer");
  30. mj := remainder(offset, randommodulus!*);
  31. putv(unidev_vec!*, 54, mj);
  32. mk := mj + 1; % This arranges that one entry in the vector
  33. % will end up with '1' in it, and that is
  34. % enough to ensure we get a long cycle.
  35. for i:= 1:54 do <<
  36. ml := mk #- mj;
  37. if iminusp ml then ml := ml #+ randommodulus!*;
  38. ii := remainder(21*i,55);
  39. putv(unidev_vec!*, ii #- 1, ml);
  40. mk := mj;
  41. mj := ml >>;
  42. for k:=1:4 do << % Cycle generator a few times to pre-scramble.
  43. for i:=0:54 do <<
  44. ml := getv(unidev_vec!*, i) #-
  45. getv(unidev_vec!*, remainder(i #+ 31,55));
  46. if iminusp ml then ml := ml #+ randommodulus!*;
  47. putv(unidev_vec!*, i, ml) >> >>;
  48. unidev_next!* := 0;
  49. unidev_nextp!* := 31;
  50. return nil
  51. end;
  52. %*************************UNIDEV****************************************
  53. symbolic procedure next!-random!-number;
  54. % Returns a uniform random deviate between 0 and randommodulus!*-1.
  55. begin scalar mj;
  56. if unidev_next!* = 54 then unidev!_next!* := 0
  57. else unidev!_next!* := unidev!_next!* #+ 1;
  58. if unidev!_nextp!* = 54 then unidev!_nextp!* := 0
  59. else unidev!_nextp!* := unidev!_nextp!* #+ 1;
  60. mj := getv(unidev_vec!*, unidev_next!*) #-
  61. getv(unidev_vec!*, unidev_nextp!*);
  62. if iminusp mj then mj := mj #+ randommodulus!*;
  63. putv(unidev_vec!*, unidev_next!*, mj);
  64. return mj
  65. end;
  66. symbolic procedure random size;
  67. % Returns a random value in the range 0 <= r < size.
  68. begin scalar m, r;
  69. if not numberp size or size <= 0
  70. then typerr(size,"positive number");
  71. if floatp size then <<
  72. % next!-random!-number() returns just under 27 bits of randomness, and
  73. % for a properly random double precision (IEEE) value I need 52 or 53
  74. % bits. So I just call next!-random!-number() twice and glue the bits
  75. % together.
  76. r := float next!-random!-number() * unidev_fac!*;
  77. return (float next!-random!-number() + r) * unidev_fac!* * size >>
  78. else <<
  79. % I first generate a random variate over a range that is some power of
  80. % randommodulus!*. Then I select from this to restrict my range to be
  81. % an exact multiple of size. The worst case for this selection is when
  82. % the power of randommodulus!* is just less than twice size, in which
  83. % case on average two trials are needed. In the vast majority of cases
  84. % the cost of making the selection will be much less. With a value
  85. % uniform over some multiple of my range I can use simple remaindering
  86. % to get the result.
  87. repeat <<
  88. r := next!-random!-number();
  89. m := randommodulus!*;
  90. while m < size do <<
  91. m := m * randommodulus!*;
  92. r := randommodulus!* * r + next!-random!-number() >>;
  93. >> until r < m - remainder(m, size);
  94. return remainder(r, size) >>
  95. end;
  96. random_new_seed 1; % Ensure that code is set up ready for use.
  97. endmodule;
  98. end;