在R中生成随机的整数而不替换

时间:2015-04-17 01:13:23

标签: r random-sample

我想绘制随机整数对而不进行替换(另一种方式我不想要任何重复对)。这个概念听起来很简单,但我想不出一个快速而简单的解决方案。

想象一下,例如,我想使用整数1:4的序列生成随机的整数对,以填充该对的元素。还假设我想生成5个随机对而无需替换。然后我希望能够生成这样的东西......

     [,1] [,2]
[1,]    1    2
[2,]    2    1
[3,]    3    3
[4,]    1    4
[5,]    4    3

在上面的例子中,没有重复对(即行)。但是,在上述矩阵的每列中存在重复的整数。因此,使用sample()分别为每列生成随机数将无效。

对我的上下文不起作用的另一个看似可能的解决方案是生成包含重复项的多对,然后追溯删除这些重复项。我不能这样做,因为我需要生成特定数量的对。

我正在寻找这个问题的有效解决方案。这似乎是一个简单的问题,它必须有一个简单的解决方案(即请不要嵌套for循环)

这是我丑陋的方法:

#This matrix maps a unique id i.e. (1:16) to a pair (i.e. the row & col of the matrix)
r.mat<-matrix(1:(4*4),4,4) 
#Drawing a random id
r.id<-sample(r.mat,5,replace=FALSE)
#Mapping the random id to a random pair
r.pair<-t(sapply(r.id, function (x) which(r.mat==x,arr.ind=TRUE)))

这适用于我的玩具示例,但是当我想从序列1:10000000中绘制大量对时,它不是那么好。

6 个答案:

答案 0 :(得分:9)

这里的关键是不要生成所有的排列,因为这是非常昂贵的内存和时间。既然你只关心两个数字,只要(number_of_possible_values) ^ 2小于双精度浮点中最大的可表示整数,我们就可以很容易地做到这一点:

size <- 1e5
samples <- 100
vals <- sample.int(size ^ 2, samples)
cbind(vals %/% size + 1, vals %% size)

基本上,我们使用整数来表示每个可能的值组合。在我们的示例中,我们对所有数字进行抽样,直至1e5 ^ 2,因为我们有1e5 ^ 21e5个数字组合。每个1e10整数代表其中一个组合。然后我们将整数分解为两个分量值,取模数作为第一个数,将整数除数作为第二个。

基准:

Unit: microseconds
                   expr        min         lq       mean
  funBrodie(10000, 100)     16.457     17.188     22.052
 funRichard(10000, 100) 542513.717 640647.919 638045.215

此外,限制应为~3x1e7,并保持相对较快:

Unit: microseconds
                  expr    min      lq     mean median      uq    max neval
 funBrodie(1e+07, 100) 18.285 20.6625 22.88209 21.211 22.4905 77.893   100

基准测试功能:

funRichard <- function(size, samples) {
  nums <- 1:size
  dt = CJ(nums, nums)
  dt[sample(1:dim(dt)[1], size = samples), ]
}
funBrodie <- function(size, samples) {
  vals <- sample.int(size ^ 2, samples)
  cbind(vals %/% size + 1, vals %% size)
}

并确认我们正在做类似的事情(注意它不是给定的,这些应该完全相同,但事实证明它们是这样的):

set.seed(1)
resB <- funBrodie(1e4, 100)
set.seed(1)
resR <- unname(as.matrix(funRichard(1e4, 100)))
all.equal(resB, resR)
# TRUE

答案 1 :(得分:4)

首先,我找到了如何在SO上生成对。但是,这并没有缩放,因此我查看了?combn并找到了expand.grid函数。

接下来,我使用data.table包,因为它可以很好地处理大数据(请参阅相关文档了解原因)。

## the data.table library does well with large data sets
library(data.table)

## Small dummy dataset
pairOne = 1:10
pairTwo = 1:2
nSamples = 3

system.time({
dt = data.table(expand.grid(pairOne, pairTwo))
dt2 = dt[sample(1:dim(dt)[1], size = nSamples), ]
})
#   user  system elapsed 
#  0.002   0.001   0.001 

## Large dummy dataset
pairOne = 1:10000
pairTwo = 1:10000
length(pairOne) * length(pairTwo)
nSamples = 1e5
system.time({
dt = data.table(expand.grid(pairOne, pairTwo))
dt2 = dt[sample(1:dim(dt)[1], size = nSamples), ]
})
#   user  system elapsed 
#  2.576   1.276   3.862 

答案 2 :(得分:2)

受David Robinson的初步刺激启发:

set.seed(1)
np <- 1000 # number of elements desired
M1 <- t(combn(1:np, 2))
sam <- sample(1:nrow(M1), np, replace = FALSE)
M2 <- M1[sam,]
anyDuplicated(M2) # returns FALSE

这将使用M1的所有可能条目,但是以随机顺序。这是你想要的吗?

答案 3 :(得分:1)

这是我的尝试。它看起来并不优雅,但它仍然比@Richard Erickson(2.0s vs 2.6s,相同尺寸)快一点。这个想法是避免创建排列,因为这可能需要花费大量时间并使用大量内存。相反,我在给定范围内创建了两个随机的ID样本,并检查是否有任何行重复(对于高范围和平均样本来说这是不太可能的)。如果它们是重复的,则创建第2列的新样本并重复所有内容。

range <- 1e8
n <- 1e5
ids1 <- sample(range, n)
ids2 <- sample(range, n)
mat1 <- cbind(ids1, ids2)
found = FALSE
while(!found) {
  if (any(duplicated(rbind(mat1, mat1[,2:1])))) {
    ids2 <- sample(range, n)
    mat1 <- cbind(ids1, ids2)
  } else {
    found=TRUE
  }
}

答案 4 :(得分:0)

怎么样:

no.pairs.needed <- 4 # or however many you want
npairs<-0
pairs <- NULL
top.sample.range <- 10000  # or whatever

while (npairs < no.pairs.needed){
  newpair <- matrix(data=sample(1:top.sample.range,2), nrow=1, ncol=2)
 if(!anyDuplicated(rbind(pairs, newpair))){
    pairs <- rbind(pairs, newpair)
    npairs <- npairs+1
  }
}

然后对象pairs将返回您需要的矩阵。似乎可以扩展好。

答案 5 :(得分:0)

这是我的解决方法。

allIDX <- seq(10000000)
prtIDX <- sample(1:10000000, 10000000/2)
chlIDX <- allIDX[-prtIDX]
pairIDX <- cbind(prtIDX,chlIDX)

但是我不必处理10000000。