如果我想采样数字来创建一个矢量我做:
set.seed(123)
x <- sample(1:100,200, replace = TRUE)
sum(x)
# [1] 10228
Run Code Online (Sandbox Code Playgroud)
如果我想抽样20个总和为100的随机数,然后是30个数字但仍然总和为100,那么我认为这将是一个比看上去更具挑战性的.?sample并且搜索Google并未向我提供线索.然后,如果不足够接近(例如在5之内)期望的总和,则采样的循环然后拒绝可能需要一些时间.
有没有更好的方法来实现这一目标?
一个例子是:
foo(10,100) # ten random numbers that sum to 100. (not including zeros)
# 10,10,20,7,8,9,4,10,2,20
Run Code Online (Sandbox Code Playgroud)
这是另一个尝试。它不使用sample,而是使用runif。我在显示总和的输出中添加了一条可选的“消息”,可以使用showSum参数触发该消息。还有一个Tolerance参数指定需要距离目标多近。
SampleToSum <- function(Target = 100, VecLen = 10,
InRange = 1:100, Tolerance = 2,
showSum = TRUE) {
Res <- vector()
while ( TRUE ) {
Res <- round(diff(c(0, sort(runif(VecLen - 1)), 1)) * Target)
if ( all(Res > 0) &
all(Res >= min(InRange)) &
all(Res <= max(InRange)) &
abs((sum(Res) - Target)) <= Tolerance ) { break }
}
if (isTRUE(showSum)) cat("Total = ", sum(Res), "\n")
Res
}
Run Code Online (Sandbox Code Playgroud)
这里有些例子。
注意默认设置和设置之间的区别Tolerance = 0
set.seed(1)
SampleToSum()
# Total = 101
# [1] 20 6 11 20 6 3 24 1 4 6
SampleToSum(Tolerance=0)
# Total = 100
# [1] 19 15 4 10 1 11 7 16 4 13
Run Code Online (Sandbox Code Playgroud)
您可以使用 来验证此行为replicate。Tolerance = 0这是设置并运行该函数 5 次的结果。
system.time(output <- replicate(5, SampleToSum(
Target = 1376,
VecLen = 13,
InRange = 10:200,
Tolerance = 0)))
# Total = 1376
# Total = 1376
# Total = 1376
# Total = 1376
# Total = 1376
# user system elapsed
# 0.144 0.000 0.145
output
# [,1] [,2] [,3] [,4] [,5]
# [1,] 29 46 11 43 171
# [2,] 103 161 113 195 197
# [3,] 145 134 91 131 147
# [4,] 154 173 138 19 17
# [5,] 197 62 173 11 87
# [6,] 101 142 87 173 99
# [7,] 168 61 97 40 121
# [8,] 140 121 99 135 117
# [9,] 46 78 31 200 79
# [10,] 140 168 146 17 56
# [11,] 21 146 117 182 85
# [12,] 63 30 180 179 78
# [13,] 69 54 93 51 122
Run Code Online (Sandbox Code Playgroud)
Tolerance = 5设置并运行该功能 5 次也是如此。
system.time(output <- replicate(5, SampleToSum(
Target = 1376,
VecLen = 13,
InRange = 10:200,
Tolerance = 5)))
# Total = 1375
# Total = 1376
# Total = 1374
# Total = 1374
# Total = 1376
# user system elapsed
# 0.060 0.000 0.058
output
# [,1] [,2] [,3] [,4] [,5]
# [1,] 65 190 103 15 47
# [2,] 160 95 98 196 183
# [3,] 178 169 134 15 26
# [4,] 49 53 186 48 41
# [5,] 104 81 161 171 180
# [6,] 54 126 67 130 182
# [7,] 34 131 49 113 76
# [8,] 17 21 107 62 95
# [9,] 151 136 132 195 169
# [10,] 194 187 91 163 22
# [11,] 23 69 54 97 30
# [12,] 190 14 134 43 150
# [13,] 156 104 58 126 175
Run Code Online (Sandbox Code Playgroud)
毫不奇怪,将容差设置为 0 会使函数变慢。
请注意,由于这是一个“随机”过程,因此很难猜测找到正确的数字组合需要多长时间。例如,使用set.seed(123),我连续运行了三次以下测试:
system.time(SampleToSum(Target = 1163,
VecLen = 15,
InRange = 50:150))
Run Code Online (Sandbox Code Playgroud)
第一次运行仅用时 9 秒多一点。第二次仅用了 7.5 秒多一点。第三次用时......不到 381 秒!这有很多变化!
出于好奇,我在函数中添加了一个计数器,第一次运行尝试了55026 次才得到满足我们所有条件的向量!(我没有费心去尝试第二次和第三次尝试。)
最好在函数中添加一些错误或健全性检查,以确保输入合理。例如,人们不应该能够输入SampleToSum(Target = 100, VecLen = 10, InRange = 15:50),因为范围为 15 到 50,因此无法达到 100 并且向量中有 10 个值。