【发布时间】:2016-06-03 06:54:35
【问题描述】:
我想生成 100 个从 1 到 100,000 的数字,每个数字之间的间距至少为 10。
一种方法是将 100,000 除以 10,然后做样本 (1000,100) 并乘以 10 得到答案,但数字都以 0 结尾。
如何生成不仅以 0 结尾的随机数?
【问题讨论】:
我想生成 100 个从 1 到 100,000 的数字,每个数字之间的间距至少为 10。
一种方法是将 100,000 除以 10,然后做样本 (1000,100) 并乘以 10 得到答案,但数字都以 0 结尾。
如何生成不仅以 0 结尾的随机数?
【问题讨论】:
这是不依赖递归的东西:
mysample <- function(n, lower, upper, space) {
b <- ceiling((upper - lower + 1) / (space - 1))
bs <- sample(seq(2, b - 1, by = 2), n - 1)
gr <- split(setdiff(1:b,bs), cumsum(c(0, diff(setdiff(1:b, bs))) != 1))
out <- sapply(gr, function(x) (x[1] - 1) * (space - 1) + ceiling(runif(1) * length(x) * (space - 1)))
out[n] <- min(out[n], upper)
out
}
set.seed(123)
min(replicate(min(diff(mysample(100, 1, 100000, 10))), n = 1000))
# [1] 10
mysample 返回一个已经排序的序列,当然你也可以使用sample(mysample(...))。
该函数的思想是将区间 [lower, upper] 划分为长度为 space - 1 的块,并从第 2、4、6、...块中采样 n-1 块;这些将是“禁止”块,即我们不会从这些块中获得任何数字。然后剩余的“允许”块可以是例如第 1、2、5 名;在这种情况下,我们有两组连续的块(第一和第二;第五),我们从对应于这两组块的间隔中采样两个数字。我还添加了一个小检查,最大数字是否超过上限。
一些结果:
set.seed(123)
upper <- 100000
benchmark(
mysample(100, 1, upper, 10), MichaelChirico(100, 1, upper, 10),
Grothendieck(100, 1, upper, 10), Jonathan(100, 1, upper, 10),
replications = 1, columns = c("test", "relative"))
# test relative
# 3 Grothendieck(100, lower, upper, 10) 1
# 4 Jonathan(100, lower, upper, 10) 344
# 2 MichaelChirico(100, lower, upper, 10) 2133
# 1 mysample(100, lower, upper, 10) 4
upper <- 10000
benchmark(
mysample(100, 1, upper, 10), MichaelChirico(100, 1, upper, 10),
Grothendieck(100, 1, upper, 10), Jonathan(100, 1, upper, 10),
replications = 1, columns = c("test", "relative"))
# test relative
# 3 Grothendieck(100, lower, upper, 10) 132.5
# 4 Jonathan(100, lower, upper, 10) 56.0
# 2 MichaelChirico(100, lower, upper, 10) 27.5
# 1 mysample(100, lower, upper, 10) 1.0
在哪里
MichaelChirico <- function(n, lower, upper, space) {
include <- lower:upper
smpl <- integer(n)
for (i in 1:length(smpl)){
smpl[i] <- si <- sample(include, 1)
include <- setdiff(include, (si - space):(si + space))
}
smpl
}
Grothendieck <- function(n, lower, upper, space) {
repeat {
s <- sample(lower:upper, n)
mindiff <- min(diff(sort(s)))
if (mindiff >= space) break
}
s
}
Jonathan <- function(n, lower, upper, space) {
min_gap <- space
samp_vec <- sample(seq(lower,upper,1), 1)
for (isamp in 1:n) {
possible_new_value <- samp_vec[1]
while(any(abs(samp_vec - possible_new_value) < min_gap)) {
possible_new_value <- sample(seq(lower,upper,1), 1)
}
samp_vec <- c(samp_vec, possible_new_value)
}
samp_vec
}
【讨论】:
seq(sample(2, 1), b - 1, by = 2))——你仍然不会得到可能的“有效”样本的宇宙,但你会更接近
n = 3 总共有 5 个块。通过禁止区块 2 和 4,我们可以通过从区块 1、3、5 中的每一个中采样一个数字来得到结果,尽管这不是一个完美的结果。通过禁止区块 1、3 和 5,我们可能必须从区块中采样 3 个数字可能发生故障的第 3、4、5 块组。
尝试拒绝抽样。无限制地进行采样,如果任何两个相距小于 10,则丢弃结果并再次执行直到成功:
repeat {
s <- sample(100000, 100)
mindiff <- min(diff(sort(s)))
if (mindiff >= 10) break
}
从问题的措辞中不清楚排序时连续数字的差异是否必须大于或等于 10,或者它们是否必须大于 10。我假设前者,但如果是后者,则在 if 语句中使用 > .
示例从种子 123 开始,循环只需要 3 次迭代(与其他答案的 100 和 100,000 次迭代相比)。下面我们添加了一个 set.seed 声明以实现可重复性和一个 print 声明,以便我们查看使用了多少次迭代。
set.seed(123)
repeat {
s <- sample(100000, 100)
mindiff <- min(diff(sort(s)))
print(mindiff)
if (mindiff >= 10) break
}
给予:
[1] 8
[1] 1
[1] 17
【讨论】:
我想类似于@G.Grothendieck,重复抽样,但如果不符合您的标准则拒绝。
set.seed(1337)
## minimum separation
min_gap <- 10
## first entry
samp_vec <- sample(seq(1,1e5L,1), 1)
for (isamp in 1:100) {
possible_new_value <- samp_vec[1]
while(any(abs(samp_vec - possible_new_value) < min_gap)) {
possible_new_value <- sample(seq(1,1e5L,1), 1)
}
samp_vec <- c(samp_vec, possible_new_value)
}
min(abs(diff(samp_vec)))
# 92
【讨论】:
我想不出一种非递归的方法来做到这一点(最初的想法是在你的方法中添加“抖动”,但这几乎肯定会使一些观察结果彼此过于接近),但这会奏效:
set.seed(102438)
include <- 1:100000
smpl <- integer(100)
for (i in 1:length(smpl)){
smpl[i] <- si <- sample(include, 1)
#now remove anything within 10 of the current draw
include <- setdiff(include, (si - 10L):(si + 10L))
}
min(abs(diff(smpl)))
# [1] 1105
【讨论】: