我正在尝试生成总和小于1的n个随机数。
所以我不能只运行runif(3)
。但是我可以根据到那时生成的所有值的总和来调整每次迭代。
我们的想法是启动一个空向量v
,然后设置一个循环,以便每次迭代i
生成一个runif()
,但在它被接受之前v
的元素,即v[i] <- runif()
,执行测试sum(v) < 1
,最后接受FALSE
最后一个条目v[i]
, < em> BUT 如果TRUE
,即和大于1,v[i]
将从向量中抛出,并重复迭代i
。
我远没有实现这个想法,但我想按照类似于下面的内容来解决它。它不是一个实际问题,而是更多的理解循环语法的练习:
n <- 4
v <- 0
for (i in 1:n){
rdom <- runif(1)
if((sum(v) + rdom) < 1) v[i] <- rdom
}
# keep trying before moving on to iteration i + 1???? i <- stays i?????
}
我调查了while
(实际上我在标题中加入了while
函数);但是,我需要向量具有n
元素,所以如果我尝试一些基本上告诉R将随机均匀实现添加为向量v
while
{{1因为我最终可能会在sum(v) < 1
中使用少于n
个元素。
答案 0 :(得分:2)
这是一个可能的解决方案。 它不使用我编辑它以使用while
但更通用repeat
。while
并保存几行。
set.seed(0)
n <- 4
v <- numeric(n)
i <- 0
while (i < n) {
ith <- runif(1)
if (sum(c(v, ith)) < 1) {
i <- i+1
v[i] <- ith
}
}
v
# [1] 0.89669720 0.06178627 0.01339033 0.02333120
使用repeat
块,您必须检查条件,但是,除去日益严重的问题,它看起来非常相似:
set.seed(0)
n <- 4
v <- numeric(n)
i <- 0
repeat {
ith <- runif(1)
if (sum(c(v, ith)) < 1) {
i <- i+1
v[i] <- ith
}
if (i == 4) break
}
答案 1 :(得分:2)
如果你真的想保持你发布的完全相同的程序(也就是从标准均匀分布中一次一个地迭代地采样n
值,拒绝任何导致你的总和超过1的样本),那么下面的代码在数学上是等价的,更短的,更有效的:
samp <- function(n) {
v <- rep(0, n)
for (i in 1:n) {
v[i] <- runif(1, 0, 1-sum(v))
}
v
}
基本上,这段代码使用的数学事实是,如果向量的总和当前为sum(v)
,那么从标准均匀分布中采样直到得到不大于1-sum(v)
的值完全相当于采样从0到1-sum(v)
的均匀分布。使用后一种方法的优点是效率更高 - 我们不需要继续拒绝样品并再次尝试,而只需为每个元素采样一次。
要了解运行时差异,请考虑使用n=10
对100个观察值进行抽样,并与您帖子中的代码的工作实现进行比较(从我对此问题的其他答案中复制):
OP <- function(n) {
v <- rep(0, n)
for (i in 1:n){
rdom <- runif(1)
while (sum(v) + rdom > 1) rdom <- runif(1)
v[i] <- rdom
}
v
}
set.seed(144)
system.time(samples.OP <- replicate(100, OP(10)))
# user system elapsed
# 261.937 1.641 265.805
system.time(samples.josliber <- replicate(100, samp(10)))
# user system elapsed
# 0.004 0.001 0.004
在这种情况下,新方法的速度接近100,000倍。
答案 2 :(得分:1)
听起来你正试图从n
变量的空间中统一采样,其中包含以下约束:
x_1 + x_2 + ... + x_n <= 1
x_1 >= 0
x_2 >= 0
...
x_n >= 0
"hit and run" algorithm是一种数学机器,可以让你做到这一点。在二维空间中,算法将从以下三角形均匀地采样,阴影区域中的每个位置都可能被选中:
该算法在R中通过hitandrun
包提供,它要求您通过约束矩阵,方向向量和右侧向量指定定义空间的线性不等式:
library(hitandrun)
n <- 3
constr <- list(constr = rbind(rep(1, n), -diag(n)),
dir = c(rep("<=", n+1)),
rhs = c(1, rep(0, n)))
set.seed(144)
samples <- hitandrun(constr, n.samples=1000)
head(samples, 10)
# [,1] [,2] [,3]
# [1,] 0.28914690 0.01620488 0.42663224
# [2,] 0.65489979 0.28455231 0.00199671
# [3,] 0.23215115 0.00661661 0.63597912
# [4,] 0.29644234 0.06398131 0.60707269
# [5,] 0.58335047 0.13891392 0.06151205
# [6,] 0.09442808 0.30287832 0.55118290
# [7,] 0.51462261 0.44094683 0.02641638
# [8,] 0.38847794 0.15501252 0.31572793
# [9,] 0.52155055 0.09921046 0.13304728
# [10,] 0.70503030 0.03770875 0.14299089
稍微分解一下这段代码,我们生成了以下约束矩阵:
constr
# $constr
# [,1] [,2] [,3]
# [1,] 1 1 1
# [2,] -1 0 0
# [3,] 0 -1 0
# [4,] 0 0 -1
#
# $dir
# [1] "<=" "<=" "<=" "<="
#
# $rhs
# [1] 1 0 0 0
在constr$constr
的第一行读取,我们有1,1,1表示“1 * x1 + 1 * x2 + 1 * x3”。 constr$dir
的第一个元素是<=
,constr$rhs
的第一个元素是1
;把它放在一起我们有x1 + x2 + x3 <= 1
。从constr$constr
的第二行,我们读取-1,0,0,表示“-1 * x1 + 0 * x2 + 0 * x3”。 constr$dir
的第二个元素是<=
,constr$rhs
的第二个元素是0
;把它放在一起我们有-x1 <= 0
,这与说x1 >= 0
相同。其余行中也存在类似的非负性约束。
请注意,命中和运行算法具有为每个变量具有完全相同分布的良好属性:
hist(samples[,1])
hist(samples[,2])
hist(samples[,3])
同时,您的程序中样本的分布将非常不均匀,随着n
的增加,这个问题会越来越严重。
OP <- function(n) {
v <- rep(0, n)
for (i in 1:n){
rdom <- runif(1)
while (sum(v) + rdom > 1) rdom <- runif(1)
v[i] <- rdom
}
v
}
samples.OP <- t(replicate(1000, OP(3)))
hist(samples.OP[,1])
hist(samples.OP[,2])
hist(samples.OP[,3])
另一个优点是命中运行算法看起来更快 - 我在计算机上使用命中运行在0.006秒内生成了1000次重复,并且使用OP中的修改代码花了0.3秒。 / p>
答案 3 :(得分:1)
以下是我将如何操作,没有任何循环,if
或while
:
set.seed(123)
x <- runif(1) # start with the sum that you want to obtain
n <- 4 # number of generated random numbers, can be chosen arbitrarily
y <- sort(runif(n-1,0,x)) # choose n-1 random points to cut the range [0:x]
z <- c(y[1],diff(y),x-y[n-1]) # result: determine the length of the segments
#> z
#[1] 0.11761257 0.10908627 0.02723712 0.03364156
#> sum(z)
#[1] 0.2875775
#> all.equal(sum(z),x)
#[1] TRUE
这里的优点是,您可以确切地确定要获得的总和以及要为此生成的数量n
。如果您在第二行设置了x <- 1
,则向量n
中存储的z
个随机数将加起来为。{/ p>