Search code examples
ralgorithmrandom

Random numbers between -1 and 1 summing to 0


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?


Solution

  • 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.


    • Recursion

    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
    

    • Rejection Sampling

    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