mirror of
https://git.savannah.gnu.org/git/guile.git
synced 2025-05-11 00:00:49 +02:00
139 lines
4.9 KiB
Scheme
139 lines
4.9 KiB
Scheme
;;;; "random.scm" Pseudo-Random number generator for scheme.
|
|
;;; Copyright (C) 1991, 1993, 1998, 1999 Aubrey Jaffer.
|
|
;
|
|
;Permission to copy this software, to redistribute it, and to use it
|
|
;for any purpose is granted, subject to the following restrictions and
|
|
;understandings.
|
|
;
|
|
;1. Any copy made of this software must include this copyright notice
|
|
;in full.
|
|
;
|
|
;2. I have made no warrantee or representation that the operation of
|
|
;this software will be error-free, and I am under no obligation to
|
|
;provide any services, by way of maintenance, update, or otherwise.
|
|
;
|
|
;3. In conjunction with products arising from the use of this
|
|
;material, there shall be no use of my name in any advertising,
|
|
;promotional, or sales literature without prior written consent in
|
|
;each case.
|
|
|
|
(require 'byte)
|
|
(require 'logical)
|
|
|
|
;;; random:chunk returns an integer in the range of 0 to 255.
|
|
(define (random:chunk sta)
|
|
(cond ((positive? (byte-ref sta 258))
|
|
(byte-set! sta 258 0)
|
|
(slib:error "random state called reentrantly")))
|
|
(byte-set! sta 258 1)
|
|
(let* ((idx (logand #xff (+ 1 (byte-ref sta 256))))
|
|
(xtm (byte-ref sta idx))
|
|
(idy (logand #xff (+ (byte-ref sta 257) xtm))))
|
|
(byte-set! sta 256 idx)
|
|
(byte-set! sta 257 idy)
|
|
(let ((ytm (byte-ref sta idy)))
|
|
(byte-set! sta idy xtm)
|
|
(byte-set! sta idx ytm)
|
|
(let ((ans (byte-ref sta (logand #xff (+ ytm xtm)))))
|
|
(byte-set! sta 258 0)
|
|
ans))))
|
|
|
|
|
|
;;@args n
|
|
;;@args n state
|
|
;;Accepts a positive integer or real @1 and returns a number of the
|
|
;;same type between zero (inclusive) and @1 (exclusive). The values
|
|
;;returned by @0 are uniformly distributed from 0 to @1.
|
|
;;
|
|
;;The optional argument @var{state} must be of the type returned by
|
|
;;@code{(seed->random-state)} or @code{(make-random-state)}. It defaults
|
|
;;to the value of the variable @code{*random-state*}. This object is used
|
|
;;to maintain the state of the pseudo-random-number generator and is
|
|
;;altered as a side effect of calls to @code{random}.
|
|
(define (random modu . args)
|
|
(let ((state (if (null? args) *random-state* (car args))))
|
|
(if (exact? modu)
|
|
(letrec ((bitlen (integer-length (+ -1 modu)))
|
|
(rnd (lambda ()
|
|
(do ((bln bitlen (+ -8 bln))
|
|
(rbs 0 (+ (ash rbs 8) (random:chunk state))))
|
|
((<= bln 7)
|
|
(set! rbs (+ (ash rbs bln)
|
|
(bit-field (random:chunk state) 0 bln)))
|
|
(and (< rbs modu) rbs))))))
|
|
(do ((ans (rnd) (rnd))) (ans ans)))
|
|
(* (random:uniform1 state) modu))))
|
|
|
|
(define random:random random)
|
|
;;;random:uniform is in randinex.scm. It is needed only if inexact is
|
|
;;;supported.
|
|
|
|
|
|
;;@defvar *random-state*
|
|
;;Holds a data structure that encodes the internal state of the
|
|
;;random-number generator that @code{random} uses by default. The nature
|
|
;;of this data structure is implementation-dependent. It may be printed
|
|
;;out and successfully read back in, but may or may not function correctly
|
|
;;as a random-number state object in another implementation.
|
|
;;@end defvar
|
|
|
|
|
|
;;@args state
|
|
;;Returns a new copy of argument @1.
|
|
;;
|
|
;;@args
|
|
;;Returns a new copy of @code{*random-state*}.
|
|
(define (copy-random-state . sta)
|
|
(copy-string (if (null? sta) *random-state* (car sta))))
|
|
|
|
|
|
;;@body
|
|
;;Returns a new object of type suitable for use as the value of the
|
|
;;variable @code{*random-state*} or as a second argument to @code{random}.
|
|
;;The number or string @1 is used to initialize the state. If
|
|
;;@0 is called twice with arguments which are
|
|
;;@code{equal?}, then the returned data structures will be @code{equal?}.
|
|
;;Calling @0 with unequal arguments will nearly
|
|
;;always return unequal states.
|
|
(define (seed->random-state seed)
|
|
(define sta (make-bytes (+ 3 256) 0))
|
|
(if (number? seed) (set! seed (number->string seed)))
|
|
; initialize state
|
|
(do ((idx #xff (+ -1 idx)))
|
|
((negative? idx))
|
|
(byte-set! sta idx idx))
|
|
; merge seed into state
|
|
(do ((i 0 (+ 1 i))
|
|
(j 0 (modulo (+ 1 j) seed-len))
|
|
(seed-len (bytes-length seed))
|
|
(k 0))
|
|
((>= i 256))
|
|
(let ((swp (byte-ref sta i)))
|
|
(set! k (logand #xff (+ k (byte-ref seed j) swp)))
|
|
(byte-set! sta i (byte-ref sta k))
|
|
(byte-set! sta k swp)))
|
|
sta)
|
|
|
|
|
|
;;@args
|
|
;;@args obj
|
|
;;Returns a new object of type suitable for use as the value of the
|
|
;;variable @code{*random-state*} or as a second argument to @code{random}.
|
|
;;If the optional argument @var{obj} is given, it should be a printable
|
|
;;Scheme object; the first 50 characters of its printed representation
|
|
;;will be used as the seed. Otherwise the value of @code{*random-state*}
|
|
;;is used as the seed.
|
|
(define (make-random-state . args)
|
|
(let ((seed (if (null? args) *random-state* (car args))))
|
|
(cond ((string? seed))
|
|
((number? seed) (set! seed (number->string seed)))
|
|
(else (let ()
|
|
(require 'object->string)
|
|
(set! seed (object->limited-string seed 50)))))
|
|
(seed->random-state seed)))
|
|
|
|
(define *random-state*
|
|
(make-random-state "http://swissnet.ai.mit.edu/~jaffer/SLIB.html"))
|
|
|
|
(provide 'random) ;to prevent loops
|
|
(if (provided? 'inexact) (require 'random-inexact))
|