循环条件为TRUE

时间:2016-02-06 18:45:41

标签: r loops while-loop

我正在尝试生成总和小于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个元素。

4 个答案:

答案 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是一种数学机器,可以让你做到这一点。在二维空间中,算法将从以下三角形均匀地采样,阴影区域中的每个位置都可能被选中:

enter image description here

该算法在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])

enter image description here

hist(samples[,2])

enter image description here

hist(samples[,3])

enter image description here

同时,您的程序中样本的分布将非常不均匀,随着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])

enter image description here

hist(samples.OP[,2])

enter image description here

hist(samples.OP[,3])

enter image description here

另一个优点是命中运行算法看起来更快 - 我在计算机上使用命中运行在0.006秒内生成了1000次重复,并且使用OP中的修改代码花了0.3秒。 / p>

答案 3 :(得分:1)

以下是我将如何操作,没有任何循环,ifwhile

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>