[R] Generate random vectors (continuous number) with a fixed sum

Rui Barradas ru|pb@rr@d@@ @end|ng |rom @@po@pt
Wed Apr 23 00:31:45 CEST 2025


Hello,

Inline.

Às 17:55 de 22/04/2025, Brian Smith escreveu:
> i.e. we should have
> 
> all elements of Reduce("+", res) should be equal to  s = 0.05528650577311
> 
> My assertion is that it is not happing here.

You are right, that's not what is happening. The output is n vectors of 
2 elements each. It's each of these vectors that add up to s. 
Appparently I misunderstood the problem.

Maybe this is what you want?
(There is no n argument, the matrix is always 2*m)


one_vec <- function(a, b, s) {
   repeat{
     repeat{
       u <- runif(1, a[1], b[1])
       if(s - u > 0) break
     }
     v <- s - u
     if(a[2] < v && v < b[2]) break
   }
   c(u, v)
}
gen_mat <- function(m, a, b, s) {
   replicate(m, one_vec(a, b, s))
}

set.seed(2025)
res <- gen_mat(10000, a, b, s)
colSums(res)


Hope this helps,

Rui Barradas


> 
> 
> On Tue, 22 Apr 2025 at 22:20, Brian Smith <briansmith199312 using gmail.com> wrote:
>>
>> Hi Rui,
>>
>> Thanks for the explanation.
>>
>> But in this case, are we looking at the correct solution at all?
>>
>> My goal is to generate random vector where:
>> 1) the first element is bounded by (a[1], b[1]) and second element is
>> bounded by (a[2], b[2])
>> 2) sum of the element is s
>>
>> According to the outcome,
>> The first matrix values are bounded by c(a[1], b[1]) & second matrix
>> values are bounded by c(a[2], b[2])
>>
>> But,
>> regarding the sum, I think we should have sum (element-wise) sum
>> should be equal to s = 0.05528650577311.
>>
>> How could we achieve that then?
>>
>> On Tue, 22 Apr 2025 at 22:03, Rui Barradas <ruipbarradas using sapo.pt> wrote:
>>>
>>> Às 12:39 de 22/04/2025, Brian Smith escreveu:
>>>> Hi Rui,
>>>>
>>>> Many thanks for your time and insight.
>>>>
>>>> However, I am not sure if I could understand the code. Below is what I
>>>> tried based on your code
>>>>
>>>> library(Surrogate)
>>>> a <- c(0.015, 0.005)
>>>> b <- c(0.070, 0.045)
>>>> set.seed(2025)
>>>> res <- mapply(\(a, b, s, n, m) RandVec(a, b, s, n, m),
>>>>                  MoreArgs = list(s = 0.05528650577311, n = 2, m = 10000), a, b)
>>>>
>>>> res1 = res[[1]]
>>>> res2 = res[[2]]
>>>>
>>>> apply(res1, 1, min) > a   ## [1] TRUE TRUE
>>>> apply(res2, 1, min) > a   ## [1] FALSE  TRUE
>>>>
>>>> I could not understand what basically 2 blocks of res signify? Which
>>>> one I should take as final simulation of the vector? If I take the
>>>> first block then the lower bound condition is fulfilled, but not with
>>>> the second block. However with the both blocks, the total equals s is
>>>> satisfying.
>>>>
>>>> I appreciate your further insight.
>>>>
>>>> Thanks and regards,
>>>>
>>>> On Mon, 21 Apr 2025 at 20:43, Rui Barradas <ruipbarradas using sapo.pt> wrote:
>>>>>
>>>>> Hello,
>>>>>
>>>>> Inline.
>>>>>
>>>>> Às 16:08 de 21/04/2025, Rui Barradas escreveu:
>>>>>> Às 15:27 de 21/04/2025, Brian Smith escreveu:
>>>>>>> Hi,
>>>>>>>
>>>>>>> There is a function called RandVec in the package Surrogate which can
>>>>>>> generate andom vectors (continuous number) with a fixed sum
>>>>>>>
>>>>>>> The help page of this function states that:
>>>>>>>
>>>>>>> a
>>>>>>>
>>>>>>> The function RandVec generates an n by m matrix x. Each of the m
>>>>>>> columns contain n random values lying in the interval [a,b]. The
>>>>>>> argument a specifies the lower limit of the interval. Default 0.
>>>>>>>
>>>>>>> b
>>>>>>>
>>>>>>> The argument b specifies the upper limit of the interval. Default 1.
>>>>>>>
>>>>>>> However in my case, the lower and upper limits are not same. For
>>>>>>> example, if I need to draw a pair of number x, y, such that x + y = 1,
>>>>>>> then the lower and upper limits are different.
>>>>>>>
>>>>>>> I tried with below code
>>>>>>>
>>>>>>> library(Surrogate)
>>>>>>>
>>>>>>> RandVec(a=c(0.1, 0.2), b=c(0.2, 0.8), s=1, n=2, m=5)$RandVecOutput
>>>>>>>
>>>>>>> This generates error with message
>>>>>>>
>>>>>>> Error in if (b - a == 0) { : the condition has length > 1
>>>>>>>
>>>>>>> Is there any way to generate the numbers with different lower and
>>>>>>> upper limits?
>>>>>>>
>>>>>>> ______________________________________________
>>>>>>> R-help using r-project.org mailing list -- To UNSUBSCRIBE and more, see
>>>>>>> https://stat.ethz.ch/mailman/listinfo/r-help
>>>>>>> PLEASE do read the posting guide https://www.R-project.org/posting-
>>>>>>> guide.html
>>>>>>> and provide commented, minimal, self-contained, reproducible code.
>>>>>> Hello,
>>>>>>
>>>>>> Use ?mapply to cycle through all values of a and b.
>>>>>> Note that the output matrices are transposed, the random vectors are the
>>>>>> rows.
>>>>> Sorry, this is not true. The columns are the random vectors, as
>>>>> documented. An example setting the RNG seed, for reproducibility.
>>>>>
>>>>>
>>>>> library(Surrogate)
>>>>>
>>>>> a <- c(0.1, 0.2)
>>>>> b <- c(0.2, 0.8)
>>>>> set.seed(2025)
>>>>> res <- mapply(\(a, b, s, n, m) RandVec(a, b, s, n, m),
>>>>>                  MoreArgs = list(s = 1, n = 2, m = 5), a, b)
>>>>>
>>>>> res
>>>>> #> $RandVecOutput
>>>>> #>          [,1]      [,2]      [,3]     [,4]      [,5]
>>>>> #> [1,] 0.146079 0.1649319 0.1413759 0.257086 0.1715478
>>>>> #> [2,] 0.253921 0.2350681 0.2586241 0.142914 0.2284522
>>>>> #>
>>>>> #> $RandVecOutput
>>>>> #>           [,1]      [,2]      [,3]      [,4]      [,5]
>>>>> #> [1,] 0.5930918 0.2154583 0.6915523 0.7167089 0.3617918
>>>>> #> [2,] 0.4069082 0.7845417 0.3084477 0.2832911 0.6382082
>>>>>
>>>>> lapply(res, colSums)
>>>>> #> $RandVecOutput
>>>>> #> [1] 0.4 0.4 0.4 0.4 0.4
>>>>> #>
>>>>> #> $RandVecOutput
>>>>> #> [1] 1 1 1 1 1
>>>>>
>>>>>
>>>>> Hope this helps,
>>>>>
>>>>> Rui Barradas
>>>>>>
>>>>>>
>>>>>> library(Surrogate)
>>>>>>
>>>>>> a <- c(0.1, 0.2)
>>>>>> b <- c(0.2, 0.8)
>>>>>> mapply(\(a, b, s, n, m) RandVec(a, b, s, n, m),
>>>>>>           MoreArgs = list(s = 1, n = 2, m = 5), a, b)
>>>>>> #> $RandVecOutput
>>>>>> #>           [,1]      [,2]      [,3]      [,4]      [,5]
>>>>>> #> [1,] 0.2004363 0.1552328 0.2391742 0.1744857 0.1949236
>>>>>> #> [2,] 0.1995637 0.2447672 0.1608258 0.2255143 0.2050764
>>>>>> #>
>>>>>> #> $RandVecOutput
>>>>>> #>           [,1]      [,2]      [,3]      [,4]      [,5]
>>>>>> #> [1,] 0.2157416 0.4691191 0.5067447 0.7749258 0.7728955
>>>>>> #> [2,] 0.7842584 0.5308809 0.4932553 0.2250742 0.2271045
>>>>>>
>>>>>>
>>>>>> Hope this helps,
>>>>>>
>>>>>> Rui Barradas
>>>>>>
>>>>>>
>>>>>
>>>>>
>>>>> --
>>>>> Este e-mail foi analisado pelo software antivírus AVG para verificar a presença de vírus.
>>>>> www.avg.com
>>> Hello,
>>>
>>> The two blocks of res are the two random matrices, one for each
>>> combination of (a,b). mapply passes each of the values in its arguments
>>> list (the ellipses in the help page) and computes the anonymous function
>>> with the pairs (a[1], b[1]), (a[2], b[2]).
>>>
>>> Since a and b are two elements vectors the output res is a two members
>>> named list. Your error is to compare the result of apply(res2, 1, min)
>>> to a, when you should compare to a[2]. See the code below.
>>>
>>>
>>> library(Surrogate)
>>> a <- c(0.015, 0.005)
>>> b <- c(0.070, 0.045)
>>> set.seed(2025)
>>> res <- mapply(\(a, b, s, n, m) RandVec(a, b, s, n, m),
>>>                 MoreArgs = list(s = 0.05528650577311, n = 2, m = 10000),
>>> a, b)
>>>
>>> res1 = res[[1]]
>>> res2 = res[[2]]
>>>
>>> # first check that the sums are correct
>>> # these sums should be s = 0.05528650577311, up to floating-point accuracy
>>> lapply(res, \(x) colSums(x[, 1:5]) |> print(digits = 14L))
>>> #> [1] 0.05528650577311 0.05528650577311 0.05528650577311 0.05528650577311
>>> #> [5] 0.05528650577311
>>> #> [1] 0.05528650577311 0.05528650577311 0.05528650577311 0.05528650577311
>>> #> [5] 0.05528650577311
>>> #> $RandVecOutput
>>> #> [1] 0.05528651 0.05528651 0.05528651 0.05528651 0.05528651
>>> #>
>>> #> $RandVecOutput
>>> #> [1] 0.05528651 0.05528651 0.05528651 0.05528651 0.05528651
>>>
>>> # now check the min and max
>>> apply(res1, 1, min) > a[1L]   ## [1] TRUE TRUE
>>> #> [1] TRUE TRUE
>>> apply(res2, 1, min) > a[2L]   ## [1] TRUE TRUE
>>> #> [1] TRUE TRUE
>>>
>>> apply(res1, 1, max) < b[1L]   ## [1] TRUE TRUE
>>> #> [1] TRUE TRUE
>>> apply(res2, 1, max) < b[2L]   ## [1] TRUE TRUE
>>> #> [1] TRUE TRUE
>>>
>>>
>>>
>>> Which one should you take as final simulation of the vector? Both.
>>> The first matrix values are bounded by c(a[1], b[1]) with column sums
>>> equal to s.
>>> The second matrix values are bounded by c(a[2], b[2]) with column sums
>>> also equal to s.
>>>
>>> Hoep this helps,
>>>
>>> Rui Barradas
>>>



More information about the R-help mailing list