Uma das formas de gerar números com uma distribuição uniforme num intervalo é
usando o método de congruências lineares1.
Vejamos então como implementar um gerador de números pseudo-aleatórios que gera
números inteiros entre 0 e m com uma distribuição uniforme
.
A ideia é gerar uma sucessão de
números com a forma
onde2
Ao valor de
dá-se o nome de semente porque é o primeiro termo da sucessão e é usado como
input para o termo seguinte. Note-se que para
um mesmo
obtém-se sempre o mesmo
.
A sucessão de números assim obtida tem período
e o intervalo de variação dos valores da
sucessão pode ser ajustado ao intervalo
através de
A maneira mais directa é escrever
(defun rand (a c m x) (mod (+ (* a x) c) m))e obtém-se
> (rand 24298 99991 199017 0) 99991
Esta não é a maneira preferível de obter um número aleatório. Queremos obter um
número simplesmente fazendo (rand), mas para isso, temos de ter uma maneira de
guardar os sucessivos termos da sucessão de modo a que sirvam de sementes para
os termos seguintes. Assim vamos definir a variável
(defvar *r-seed* 0)que inicializa a sucessão de sementes (ou dos próprios números pseudo-aleatórios). Esta é uma variável global e por isso está enquadrada por
*. Definimos um substituto para a função anterior como
(defun rand1 () (let ((a 24298) (c 99991) (m 199017)) (cond ((<= *r-seed* 199017) (setf *r-seed* (mod (+ (* a *r-seed*) c) m))) (t (setf *r-seed* 0)))))onde agora os parâmetros
a, c e m são locais e estão definidos dentro da
função. Assim
> (rand1) 99991
A função rand1 é simples de perceber. Depois de inicializar as quantidades a, c
e m verifica se *r-seed* é menor ou igual a 199017, se sim altera o valor de
*r-seed* para o novo termo da sucessão através de
(setf *r-seed* (mod (+ (* a *r-seed*) c) m))se não
(setf *r-seed* 0)
De modo a gerar números aleatórios entre
define-se a função
(defun rand (&optional alpha beta) (let ((m 199017)) (cond ((not (and alpha beta)) (float (/ (rand1) m))) (t (+ (* (float (/ (rand1) m)) (- beta alpha)) alpha)))))com dois argumentos opcionais
alpha e beta; se não forem dados (rand) gera
números com uma distribuição uniforme no intervalo
:
> (rand) 0.55237997 > (rand 0 2) 1.1378727
Ref:
1. D. Knuth, TAOCP
2. Master Library da TI Programable 58/59
Created: 18-06-2010 [00:00]
Last updated: 23-01-2025 [00:04]
For attribution, please cite this page as:
Charters, T., "Números pseudo-aleatórios em LISP": https://nexp.pt/rand.html (23-01-2025 [00:04])
(cc-by-sa) Tiago Charters - tiagocharters@nexp.pt