生成R中总和为1的3个随机数

时间:2012-06-12 20:02:46

标签: r random

我希望创建总和为1的3个(非负)准随机数,并反复重复。

基本上我试图在许多试验中将某些东西分成三个随机部分。

虽然我知道

a = runif(3,0,1)

我原以为我可以在下一个1-a中使用runif作为最大值,但这看起来很麻烦。

但这些当然不能归结为一个。有任何想法,哦明智的stackoverflow-ers?

6 个答案:

答案 0 :(得分:13)

这个问题涉及比最初可能显而易见的更微妙的问题。在查看以下内容之后,您可能需要仔细考虑使用这些数字表示的过程:

## My initial idea (and commenter Anders Gustafsson's):
## Sample 3 random numbers from [0,1], sum them, and normalize
jobFun <- function(n) {
    m <- matrix(runif(3*n,0,1), ncol=3)
    m<- sweep(m, 1, rowSums(m), FUN="/")
    m
}

## Andrie's solution. Sample 1 number from [0,1], then break upper 
## interval in two. (aka "Broken stick" distribution).
andFun <- function(n){
  x1 <- runif(n)
  x2 <- runif(n)*(1-x1)
  matrix(c(x1, x2, 1-(x1+x2)), ncol=3)
}

## ddzialak's solution (vectorized by me)
ddzFun <- function(n) {
    a <- runif(n, 0, 1)
    b <- runif(n, 0, 1)
    rand1 = pmin(a, b)
    rand2 = abs(a - b)
    rand3 = 1 - pmax(a, b)
    cbind(rand1, rand2, rand3)
}

## Simulate 10k triplets using each of the functions above
JOB <- jobFun(10000)
AND <- andFun(10000)
DDZ <- ddzFun(10000)

## Plot the distributions of values
par(mfcol=c(2,2))
hist(JOB, main="JOB")
hist(AND, main="AND")
hist(DDZ, main="DDZ")

enter image description here

答案 1 :(得分:10)

从(0,1)中随机抽取两位数字,如果假设它为ab,那么你得到了:

rand1 = min(a, b)
rand2 = abs(a - b)
rand3 = 1 - max(a, b)

答案 2 :(得分:8)

当您想要随机生成添加到1(或其他值)的数字时,您应该查看Dirichlet Distribution

rdirichlet包中有一个gtools函数,正在运行RSiteSearch('Dirichlet')会带来很多点击,很容易让你找到这样做的工具(这并不难对于简单的Dirichlet分布,手动编码)。

答案 3 :(得分:6)

我想这取决于你想要的数字分布,但这是一种方式:

diff(c(0, sort(runif(2)), 1))

使用replicate获取任意数量的设置:

> x <- replicate(5, diff(c(0, sort(runif(2)), 1)))
> x
           [,1]       [,2]      [,3]      [,4]       [,5]
[1,] 0.66855903 0.01338052 0.3722026 0.4299087 0.67537181
[2,] 0.32130979 0.69666871 0.2670380 0.3359640 0.25860581
[3,] 0.01013117 0.28995078 0.3607594 0.2341273 0.06602238
> colSums(x)
[1] 1 1 1 1 1

答案 4 :(得分:2)

这个问题和提出的不同解决方案引起了我的兴趣。我对建议的三种基本算法进行了一些测试,并对生成的数字产生了平均值。

choose_one_and_divide_rest
means:                [ 0.49999212  0.24982403  0.25018384]
standard deviations:  [ 0.28849948  0.22032758  0.22049302]
time needed to fill array of size 1000000 was 26.874945879 seconds

choose_two_points_and_use_intervals
means:                [ 0.33301421  0.33392816  0.33305763]
standard deviations:  [ 0.23565652  0.23579615  0.23554689]
time needed to fill array of size 1000000 was 28.8600130081 seconds

choose_three_and_normalize
means:                [ 0.33334531  0.33336692  0.33328777]
standard deviations:  [ 0.17964206  0.17974085  0.17968462]
time needed to fill array of size 1000000 was 27.4301018715 seconds

时间测量应该用一粒盐来进行,因为它们可能更受Python内存管理的影响而不是算法本身。我懒得用timeit正确地做到这一点。我在1GHz原子上做到这一点,这就解释了为什么花了这么长时间。

无论如何,choose_one_and_divide_rest是Andrie建议的算法和他/她自己的问题的海报(AND):你在[0,1]中选择一个值a,然后在[a,1]中选择一个值然后你看看你剩下的。它加起来只有一个但是就此而言,第一个分区是其他两个分区的两倍。人们可能已经猜到了......

choose_two_points_and_use_intervals是ddzialak(DDZ)接受的答案。它在区间[0,1]中需要两个点,并使用这些点创建的三个子区间的大小作为三个数字。像魅力一样工作,手段都是1/3。

choose_three_and_normalize是Anders Gustafsson和Josh O'Brien(JOB)的解决方案。它只是在[0,1]中生成三个数字并将它们归一化为1的总和。在我的Python实现中,同样令人惊讶地工作得更快。方差略低于第二种解决方案。

你有它。不知道这些解决方案对应的beta分布或者我在评论中提到的相应论文中的哪些参数,但也许其他人可以解决这个问题。

答案 5 :(得分:1)

我只需从均匀分布中随机选择3个数字,然后除以它们的和即可。 代码如下。

n <- 3
x <- runif(3, 0, 1)
y <- x/sum(x)
sum(y)== 1

n可以是您喜欢的任何数字。