【问题标题】:Generate random numbers spaced at least 10 apart生成间隔至少 10 的随机数
【发布时间】:2016-06-03 06:54:35
【问题描述】:

我想生成 100 个从 1 到 100,000 的数字,每个数字之间的间距至少为 10。

一种方法是将 100,000 除以 10,然后做样本 (1000,100) 并乘以 10 得到答案,但数字都以 0 结尾。

如何生成不仅以 0 结尾的随机数?

【问题讨论】:

    标签: r random numbers


    【解决方案1】:

    这是不依赖递归的东西:

    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))——你仍然不会得到可能的“有效”样本的宇宙,但你会更接近
    • @MichaelChirico,我同意我的解决方案可以朝这个方向改进,但是考虑到奇数块可能会出现一些复杂情况,我决定避免它们。这就是我的意思;假设n = 3 总共有 5 个块。通过禁止区块 2 和 4,我们可以通过从区块 1、3、5 中的每一个中采样一个数字来得到结果,尽管这不是一个完美的结果。通过禁止区块 1、3 和 5,我们可能必须从区块中采样 3 个数字可能发生故障的第 3、4、5 块组。
    【解决方案2】:

    尝试拒绝抽样。无限制地进行采样,如果任何两个相距小于 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
    

    【讨论】:

    • 我认为这种方法对于小型(n_desired / n_initial --i.e., 100 / 100000 这里)会非常快,但随着它的上升会经常拒绝。我的还有另一个缺陷——如果我们用完可用的抽奖,它将失败(通过鸽巢,如果 n_desired > n_initial/10,这必须发生,粗略,但是当 n_desired 接近该边界时,绝大多数绘制路径都会发生)
    【解决方案3】:

    我想类似于@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
    

    【讨论】:

      【解决方案4】:

      我想不出一种非递归的方法来做到这一点(最初的想法是在你的方法中添加“抖动”,但这几乎肯定会使一些观察结果彼此过于接近),但这会奏效:

      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
      

      【讨论】:

        猜你喜欢
        • 2022-12-04
        • 1970-01-01
        • 2011-08-10
        • 1970-01-01
        • 1970-01-01
        • 2016-08-14
        • 1970-01-01
        • 1970-01-01
        • 2011-10-03
        相关资源
        最近更新 更多