;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; This file is part of AIslash.
;
; AIslash is free software: you can redistribute it and/or modify
; it under the terms of the GNU General Public License as published by
; the Free Software Foundation, either version 3 of the License, or
; (at your option) any later version.
;
; AIslash is distributed in the hope that it will be useful,
; but WITHOUT ANY WARRANTY; without even the implied warranty of
; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
; GNU General Public License for more details.
;
; You should have received a copy of the GNU General Public License
; along with AIslash. If not, see
The bald guy says when debugging code containing random ;selections, it is useful to ;recreate a ;prior run that lead to an error. Hence, most languages ;offer a pseudo-random number generator which generates a sequence of ;random numbers from some ;seed. Exactly the same sequence of "random" numbers can be recreated by ;resetting the seed and re-running the random sequence. ;
Sadly, I can't make LISP's built in random number generator work that ;way. Yes, I 've tried reseting *random-state* and that did not work. ;Also, I began to worry ;that what ever I did would not be portable to other LISPs. ;
So the following implements a ;simple pseudo-random number generator. ;
(defparameter *seed0* 10013) (defparameter *seed* *seed0*) (defun reset-seed () (setf *seed* *seed0*)) ;;
This is the Park-Miller multiplicative congruential randomizer ; (CACM, October 88, Page 1195). Creates pseudo random floating ; point numbers in the range 0.0 < x ≤ 1.0. ;
(defun park-miller-randomizer () (let ((multiplier 16807.0d0);16807 is (expt 7 5) (modulus 2147483647.0d0)) ;2147483647 is (- (expt 2 31) 1) (let ((temp (* multiplier *seed*))) (setf *seed* (mod temp modulus)) (/ *seed* modulus)))) ;;
Returns a pseudo random floating-point number ; in range 0.0 ≤ number < n. ;Nogte that we subtract the randomly generated number from 1.0 ; before scaling so that we end up in the range ; 0.0 ≤ x < 1.0, not 0.0 < x ≤ 1.0.
(defun my-random (n) (let ((random-number (park-miller-randomizer))) (* n (- 1.0d0 random-number)))) ;;
Returns a pseudo random integer ; in range 0 ≤ number < n-1.
(defun my-random-int (n) (let ((random-number (/ (my-random 1000.0) 1000))) (floor (* n random-number)))) ;
(deftest test-random () (check (equalp (random-demo) (random-demo)))) (defun random-demo (&optional (resetp t)) (let (counts out) (labels ((sorter (x y) (< (car x) (car y))) (zap () (setf out nil) (if resetp (reset-seed)) (setf counts (make-hash-table))) (inc (n) (setf (gethash n counts) (1+ (gethash n counts 0)))) (cache (k v) (push (list k v) out))) (zap) (dotimes (i 10000) ; 10000 times do (inc (my-random-int 5))) ; generate a num 0..4 (maphash #'cache counts) ; hash key/buckets ==> lists (sort out #'sorter)))) ; sort and print list ;