With R, how to generate n
random numbers x_1
, ..., x_n
that lie between -1
and 1
and that sum to 0
?
What about the generalization to another sum and another range?
First of all, I would say that, the approaches below are just possible implementations for the constrained randomness, but not secure for the uniform distribution property or something like that.
Following a recursion idea, we can generate r
first with more boundary constraints, which might speed up a lot and be much more efficient
f <- function(s, n, a, b) {
if (s < n * a || s > n * b) {
stop("Invalid parameters.")
}
if (n == 1) {
return(s)
}
r <- runif(1, max(a, s - (n - 1) * b), min(b, s - (n - 1) * a))
c(r, Recall(s - r, n - 1, a, b))
}
and we can see
> (v <- f(s = 60, n = 30, a = 1, b = 3))
[1] 1.544962 1.229845 2.013064 1.510149 2.933672 1.782947 1.650229 2.700521
[9] 1.151468 1.758759 2.035019 1.355591 2.731922 2.918394 2.288166 2.198345
[17] 1.313646 2.312720 1.232810 1.591426 1.020105 2.788073 1.208734 2.929171
[25] 1.397976 2.044319 1.593190 2.961647 2.849886 2.953244
> summary(v)
Min. 1st Qu. Median Mean 3rd Qu. Max.
1.002 1.387 2.126 2.000 2.585 2.892
> length(v)
[1] 30
> sum(v)
[1] 60
A brute-force (inefficient) but lazy approach is rejection sampling
f <- function(s, n, a, b) {
repeat {
v <- runif(n, a, b)
x <- s * v / sum(v)
if (all(x >= a & x <= b)) break
}
x
}
such that
> (v <- f(s = 60, n = 30, a = 1, b = 3))
[1] 1.800257 1.706306 2.855300 2.177379 2.844279 2.293842 1.011653 2.820371
[9] 2.803390 2.427355 1.892209 1.829180 2.240873 1.641185 2.267275 1.899986
[17] 1.042455 1.400519 2.612722 1.018635 2.024762 1.413173 1.376111 2.685723
[25] 1.886224 2.151509 1.598368 1.114850 2.303480 2.860629
> summary(v)
Min. 1st Qu. Median Mean 3rd Qu. Max.
1.012 1.609 1.962 2.000 2.396 2.861
> length(v)
[1] 30
> sum(v)
[1] 60