从数组中选择随机元素的最佳方法,在R中至少有一个min diff

时间:2018-04-25 10:14:37

标签: arrays r random

我想从一个数组中随机选择一定数量的元素,这些方面总是限制它们的倒数距离。 例如,有一个向量a <- seq(1,1000),如何选择20个元素,彼此之间的最小距离为15?

现在,我正在使用一个简单的迭代,我拒绝任何元素旁边的选择,但它很麻烦,如果要挑选的元素数量很多,往往会很长。是否有最佳实践/功能?

编辑 - 答案和分析摘要

到目前为止,我有两个工作答案,我将其包含在两个特定的功能中。

# dash2 approach
# ---------------
rand_pick_min <- function(ar, min.dist, n.picks){
  stopifnot(is.numeric(min.dist), 
            is.numeric(n.picks), n.picks%%1 == 0)
  if(length(ar)/n.picks < min.dist) 
    stop('The number of picks exceeds the maximum number of divisions that the array allows which is: ', 
         floor(length(ar)/min.dist))
  picked <- array(NA, n.picks)
  copy <- ar
  for (i in 1:n.picks) {
    stopifnot(length(copy) > 0)  
    picked[i] <- sample(copy, 1)
    copy <- copy[ abs(copy - picked[i]) >= min.dist ]
  }
  return(picked)
}

# denis approach
# ---------------
rand_pick_min2 <- function(ar, min.dist, n.picks){
  require(Surrogate)
  stopifnot(is.numeric(min.dist), 
            is.numeric(n.picks), n.picks%%1 == 0)
  if(length(ar)/n.picks < min.dist) 
    stop('The number of picks exceeds the maximum number of divisions that the array allows which is: ', 
         floor(length(ar)/min.dist))
  lar <- length(ar)
  dist <- Surrogate::RandVec(a=min.dist, b=(lar-(n.picks)*min.dist), 
                             s=lar, n=(n.picks+1), m=1, Seed=sample(1:lar, size = 1))$RandVecOutput
  return(cumsum(round(dist))[1:n.picks])
}

使用提出的相同示例我运行3次测试。首先,最低限度的有效有效期

# Libs
require(ggplot2)
require(microbenchmark)

# Inputs
a <- seq(1, 1000)            # test vector
md <- 15                     # min distance
np <- 20                     # number of picks

# Run
dist_vec <- c(sapply(1:500, function(x) c(dist(rand_pick_min(a, md, np)))))   # sol 1
dist_vec2 <- c(sapply(1:500, function(x) c(dist(rand_pick_min2(a, md, np))))) # sol 2

# Tests - break the min
cat('Any distance breaking the min in sol 1?', any(dist_vec < md), '\n')  # FALSE
cat('Any distance breaking the min in sol 2?', any(dist_vec2 < md), '\n') # FALSE

其次,我测试了所得到的距离的分布,按照解的顺序获得前两个图(sol1 [A]是dash2的sol,而sol2 [B]是denis&#39; one)。

pa <- ggplot() + theme_classic() +
  geom_density(aes_string(x = dist_vec), fill = 'lightgreen') +
  geom_vline(aes_string(xintercept = mean(dist_vec)), col = 'darkred') + xlab('Distances')
pb <- ggplot() + theme_classic() +
  geom_density(aes_string(x = dist_vec2), fill = 'lightgreen') +
  geom_vline(aes_string(xintercept = mean(dist_vec)), col = 'darkred') + xlab('Distances')
print(pa)
print(pb)

pdfs of distances 最后,我计算了两种方法所需的计算时间如下并获得最后一个数字。

comp_times <- microbenchmark::microbenchmark(
  'solution_1' = rand_pick_min(a, md, np),
  'solution_2' = rand_pick_min2(a, md, np),
  times = 500
)
ggplot2::autoplot(comp_times); ggsave('stckoverflow2.png')

Computational times

结果显示,我问自己是否应该预期距离分布,或者是因应用方法而导致的偏差。

EDIT2 - 在丹尼斯的评论之后回答最后一个问题

使用更多的采样程序(5000),我生成了一个结果位置的pdf,实际上你的方法包含一些使你的解决方案(B)偏离我需要的人工制品。尽管如此,能够强制执行特定的最终头寸分配会很有趣。 distribution of positions

2 个答案:

答案 0 :(得分:3)

如果您想避免命中和未命中方法,则必须将问题转换为距离采样,并限制距离总和。

基本上我如何翻译你想要的东西:你采样的N个位置相当于N + 1距离,从最小距离到你的矢量大小 - N * mindist(所有样本被打包在一起的情况)。然后,您需要将距离的总和约束为1000(向量的大小)。

在这种情况下,解决方案将使用Surrogate包中的Surrogate :: RandVec(参见Random sampling to give an exact sum),它允许采用固定总和进行采样。

library(Surrogate)
a <- seq(1,1000)
mind <- 15
N <- 20
dist <- Surrogate::RandVec(a=mind, b=(1000-(N)*mind), s=1000, n=(N+1), m=1, Seed=sample(1:1000, size = 1))$RandVecOutput
pos <- cumsum(round(dist))[1:20]
pos

> pos
 [1]  22  59  76 128 204 239 289 340 389 440 489 546 567 607 724 773 808 843 883 927

dist是距离的采样。您可以通过计算距离的总和来重建您的位置。它会为您提供pos,即索引位置的向量。

优势在于您可以获得任何价值,并且您的抽样应该是随机的。对于我不知道的速度部分,您需要与您的大数据案例的方法进行比较。

这是1000次尝试的直方图:

enter image description here

答案 1 :(得分:2)

我认为最好的解决方案,在某种意义上保证随机性(我不确定是什么意义!)可能是:

  1. 选择一个随机元素
  2. 删除所有太靠近该元素的元素
  3. 选择其他元素
  4. 返回2。
  5. 所以:

    min_dist <- 15
    a <- seq(1, 1000)
    picked <- integer(20)
    copy <- a
    for (i in 1:20) {
      stopifnot(length(copy) > 0)
      picked[i] <- sample(copy, 1)
      copy <- copy[ abs(copy - picked[i]) >= min_dist ]
    }
    

    这是否比采样和拒绝更快可能取决于原始矢量的特征。此外,正如您所看到的,您无法保证能够获得所需的所有元素,但在特定情况下不会出现问题,因为宽度为30的19个区间永远不会覆盖整个{{1} }。