(in-package :randist)
(declaim (optimize (speed 3) (safety 1) (debug 0)))
#| The multinomial distribution has the form
N! n_1 n_2 n_K
prob(n_1, n_2, ... n_K) = -------------------- p_1 p_2 ... p_K
(n_1! n_2! ... n_K!)
where n_1, n_2, ... n_K are nonnegative integers, sum_{k=1,K} n_k = N,
and p = (p_1, p_2, ..., p_K) is a probability distribution.
Random variates are generated using the conditional binomial method.
This scales well with N and does not require a setup step.
Ref:
C.S. David, The computer generation of multinomial random variates,
Comp. Stat. Data Anal. 16 (1993) 205-217
void
gsl_ran_multinomial (const gsl_rng * r, const size_t K,
const unsigned int N, const double p[], unsigned int n[])
{
size_t k;
double norm = 0.0;
double sum_p = 0.0;
unsigned int sum_n = 0;
/* p[k] may contain non-negative weights that do not sum to 1.0.
* Even a probability distribution will not exactly sum to 1.0
* due to rounding errors.
*/
for (k = 0; k < K; k++)
{
norm += p[k];
}
for (k = 0; k < K; k++)
{
if (p[k] > 0.0)
{
n[k] = gsl_ran_binomial (r, p[k] / (norm - sum_p), N - sum_n);
}
else
{
n[k] = 0;
}
sum_p += p[k];
sum_n += n[k];
}
}
|#
(defun random-multinomial1 (NN p n)
"Return the genrated values in the n vector"
(let ((norm 0d0)
(k (1- (array-dimension p 0))))
(declare (type integer NN))
;; (type (simple-array double-float (*)) p))
(setf norm (loop for i fixnum from 0 to k
sum (aref p i)))
(loop for i from 0 to k
do (progn
(setf (aref n i)
(if (> (aref p i) 0d0)
(random-binomial (/ (aref p i)
(- norm sum-p))
(- NN sum-n))
0)))
sum (aref p i) into sum-p
sum (aref n i) into sum-n)))
(defun convert-to-double-float-vector (x)
(map 'vector (lambda (x) (coerce x 'double-float)) x))
(defun random-multinomial% (NN p)
" The multinomial distribution has the form
N! n_1 n_2 n_K
prob(n_1, n_2, ... n_K) = -------------------- p_1 p_2 ... p_K
(n_1! n_2! ... n_K!)
where n_1, n_2, ... n_K are nonnegative integers, sum_{k=1,K} n_k = N,
and p = (p_1, p_2, ..., p_K) is a probability distribution.
Random variates are generated using the conditional binomial method.
This scales well with N and does not require a setup step.
Ref:
C.S. David, The computer generation of multinomial random variates,
Comp. Stat. Data Anal. 16 (1993) 205-217"
(let ((n (make-array (array-dimension p 0)
:element-type 'integer
:adjustable nil)))
(random-multinomial1 NN p n)
n))
(defun random-multinomial (NN p)
(random-multinomial% NN (convert-to-double-float-vector p)))
(defun test-multinomial1 (nn p &optional (k 10000))
(let* ((d (array-dimension p 0))
(r (make-array d :initial-element nil ))
(n (make-array d :element-type 'integer :adjustable nil)))
(loop for i from 0 to k
do (progn
(random-multinomial1 nn p n)
(loop for j from 0 to (1- d)
do (push (aref n j) (aref r j)))))
(loop for j from 0 to (1- d)
do (format t "~2d ~8f ~8f~t~8f ~8f~%"
j
(float (mean (aref r j)))
(* nn (aref p j))
(float (var (aref r j)))
(* nn (aref p j) (- 1d0 (aref p j))))))
(terpri))
(defun test-multinomial (&optional (k 10000))
(test-multinomial1 100 #(0.7d0 0.2d0 0.1d0) k)
(test-multinomial1 1000000 #(0.7d0 0.2d0 0.1d0) k)
(test-multinomial1 1000000 #(0.7d0 0.2d0 0.08d0 0.01d0 0.005d0 0.005d0) k))