如果我们没有邻接限制,这个问题对于当前可用的工具来说就不会那么困难了
R
(见
this answer
更多信息)。有了邻接限制,我们必须做更多的工作才能得到我们想要的结果。
我们首先注意到,因为在一个向量的行中不能有两个连续的数字
n个
列(操作在它们需要的注释中澄清
n=11个
因此,我们将使用这个作为测试用例),最大值的列数等于
11 - floor(11 / 2) = 6
. 当值出现在列中时发生这种情况
1 3 5 7 9 11
. 我们还应该注意到,因为最大值被限制在0.5,并且我们需要将行求和为1,因此,具有值的列的最小数目等于2。
ceiling(1 / 0.5) = 2
. 有了这些信息,我们就可以开始进攻了。
我们首先生成11个选择2到6的每个组合。然后我们筛选出违反邻接限制的组合。后一部分可以很容易地通过采取
diff
检查每一行的任何结果差异是否等于1。观察(注意,我们使用
RcppAlgos
(我是作者)对于所有计算:
library(RcppAlgos)
vecLen <- 11L
lowComb <- as.integer(ceiling(1 / 0.5))
highComb <- 6L
numCombs <- length(lowComb:highComb)
allCombs <- lapply(lowComb:highComb, function(x) {
comboGeneral(vecLen, x)
})
validCombs <- lapply(allCombs, function(x) {
which(apply(x, 1, function(y) {
!any(diff(y) == 1L)
}))
})
combLen <- lengths(validCombs)
combLen
[1] 45 84 70 21 1
## subset each matrix of combinations using the
## vector of validCombs obtained above
myCombs <- lapply(seq_along(allCombs), function(x) {
allCombs[[x]][validCombs[[x]], ]
})
我们现在需要找到
seq(0.05, 0.5, 0.05)
上面计算出的每一个可能长度的总和为1。使用的约束特性
comboGeneral
,这是一个简单的任务:
combSumOne <- lapply(lowComb:highComb, function(x) {
comboGeneral(seq(5L,50L,5L), x, TRUE,
constraintFun = "sum",
comparisonFun = "==",
limitConstraints = 100L) / 100
})
groupLen <- sapply(combSumOne, nrow)
groupLen
1 13 41 66 78
现在,我们创建一个包含所需列数的矩阵,并使用
myCombs
以确保满足相邻性要求。
myCombMat <- matrix(0L, nrow = sum(groupLen * combLen), ncol = vecLen)
s <- g <- 1L
e <- combRow <- nrow(combSumOne[[1L]])
for (a in myCombs[-numCombs]) {
for (i in 1:nrow(a)) {
myCombMat[s:e, a[i, ]] <- combSumOne[[g]]
s <- e + 1L
e <- e + combRow
}
e <- e - combRow
g <- g + 1L
combRow <- nrow(combSumOne[[g]])
e <- e + combRow
}
## the last element in myCombs is simply a
## vector, thus nrow would return NULL
myCombMat[s:e, myCombs[[numCombs]]] <- combSumOne[[g]]
以下是一个输出的一瞥:
head(myCombMat)
[,1] [,2] [,3] [,4] [,5] [,6] [,7] [,8] [,9] [,10] [,11]
[1,] 0.5 0 0.5 0.0 0.0 0.0 0.0 0.0 0 0 0
[2,] 0.5 0 0.0 0.5 0.0 0.0 0.0 0.0 0 0 0
[3,] 0.5 0 0.0 0.0 0.5 0.0 0.0 0.0 0 0 0
[4,] 0.5 0 0.0 0.0 0.0 0.5 0.0 0.0 0 0 0
[5,] 0.5 0 0.0 0.0 0.0 0.0 0.5 0.0 0 0 0
[6,] 0.5 0 0.0 0.0 0.0 0.0 0.0 0.5 0 0 0
tail(myCombMat)
[,1] [,2] [,3] [,4] [,5] [,6] [,7] [,8] [,9] [,10] [,11]
[5466,] 0.10 0 0.10 0 0.20 0 0.20 0 0.20 0 0.20
[5467,] 0.10 0 0.15 0 0.15 0 0.15 0 0.15 0 0.30
[5468,] 0.10 0 0.15 0 0.15 0 0.15 0 0.20 0 0.25
[5469,] 0.10 0 0.15 0 0.15 0 0.20 0 0.20 0 0.20
[5470,] 0.15 0 0.15 0 0.15 0 0.15 0 0.15 0 0.25
[5471,] 0.15 0 0.15 0 0.15 0 0.15 0 0.20 0 0.20
set.seed(42)
mySamp <- sample(nrow(myCombMat), 10)
sampMat <- myCombMat[mySamp, ]
rownames(sampMat) <- mySamp
sampMat
[,1] [,2] [,3] [,4] [,5] [,6] [,7] [,8] [,9] [,10] [,11]
5005 0.00 0.05 0.00 0.05 0.00 0.15 0.00 0.35 0.00 0.4 0.00
5126 0.00 0.15 0.00 0.15 0.00 0.20 0.00 0.20 0.00 0.0 0.30
1565 0.10 0.00 0.15 0.00 0.00 0.00 0.25 0.00 0.00 0.5 0.00
4541 0.05 0.00 0.05 0.00 0.00 0.15 0.00 0.00 0.25 0.0 0.50
3509 0.00 0.00 0.15 0.00 0.25 0.00 0.25 0.00 0.00 0.0 0.35
2838 0.00 0.10 0.00 0.15 0.00 0.00 0.35 0.00 0.00 0.0 0.40
4026 0.05 0.00 0.10 0.00 0.15 0.00 0.20 0.00 0.50 0.0 0.00
736 0.00 0.00 0.10 0.00 0.40 0.00 0.00 0.00 0.00 0.0 0.50
3590 0.00 0.00 0.15 0.00 0.20 0.00 0.00 0.30 0.00 0.0 0.35
3852 0.00 0.00 0.00 0.05 0.00 0.20 0.00 0.30 0.00 0.0 0.45
all(rowSums(myCombMat) == 1)
[1] TRUE
如您所见,每一行的总和为1,并且没有相邻的值。
如果你真的想要置换,我们可以生成
顺序(0.05,0.5,0.05)
每个可能长度的总和为1(就像我们对组合所做的那样):
permSumOne <- lapply(lowComb:highComb, function(x) {
permuteGeneral(seq(5L,50L,5L), x, TRUE,
constraintFun = "sum",
comparisonFun = "==",
limitConstraints = 100L) / 100
})
groupLenPerm <- sapply(permSumOne, nrow)
groupLenPerm
[1] 1 63 633 3246 10872
并使用这些来创建所有可能的排列矩阵,这些排列总和为1,并满足我们的邻接要求:
myPermMat <- matrix(0L, nrow = sum(groupLenPerm * combLen), ncol = vecLen)
s <- g <- 1L
e <- permRow <- nrow(permSumOne[[1L]])
for (a in myCombs[-numCombs]) {
for (i in 1:nrow(a)) {
myPermMat[s:e, a[i, ]] <- permSumOne[[g]]
s <- e + 1L
e <- e + permRow
}
e <- e - permRow
g <- g + 1L
permRow <- nrow(permSumOne[[g]])
e <- e + permRow
}
## the last element in myCombs is simply a
## vector, thus nrow would return NULL
myPermMat[s:e, myCombs[[numCombs]]] <- permSumOne[[g]]
再一次,这里是输出的一瞥:
head(myPermMat)
[,1] [,2] [,3] [,4] [,5] [,6] [,7] [,8] [,9] [,10] [,11]
[1,] 0.5 0 0.5 0.0 0.0 0.0 0.0 0.0 0 0 0
[2,] 0.5 0 0.0 0.5 0.0 0.0 0.0 0.0 0 0 0
[3,] 0.5 0 0.0 0.0 0.5 0.0 0.0 0.0 0 0 0
[4,] 0.5 0 0.0 0.0 0.0 0.5 0.0 0.0 0 0 0
[5,] 0.5 0 0.0 0.0 0.0 0.0 0.5 0.0 0 0 0
[6,] 0.5 0 0.0 0.0 0.0 0.0 0.0 0.5 0 0 0
tail(myPermMat)
[,1] [,2] [,3] [,4] [,5] [,6] [,7] [,8] [,9] [,10] [,11]
[128680,] 0.15 0 0.20 0 0.20 0 0.15 0 0.15 0 0.15
[128681,] 0.20 0 0.15 0 0.15 0 0.15 0 0.15 0 0.20
[128682,] 0.20 0 0.15 0 0.15 0 0.15 0 0.20 0 0.15
[128683,] 0.20 0 0.15 0 0.15 0 0.20 0 0.15 0 0.15
[128684,] 0.20 0 0.15 0 0.20 0 0.15 0 0.15 0 0.15
[128685,] 0.20 0 0.20 0 0.15 0 0.15 0 0.15 0 0.15
all(rowSums(myPermMat) == 1)
[1] TRUE
而且,正如OP所说,如果我们想随机挑选10000个,我们可以使用
sample
要做到这一点:
set.seed(101)
mySamp10000 <- sample(nrow(myPermMat), 10000)
myMat10000 <- myPermMat[mySamp10000, ]
rownames(myMat10000) <- mySamp10000
head(myMat10000)
[,1] [,2] [,3] [,4] [,5] [,6] [,7] [,8] [,9] [,10] [,11]
47897 0.00 0.0 0.00 0.50 0.0 0.25 0.0 0.00 0.05 0.0 0.20
5640 0.25 0.0 0.15 0.00 0.1 0.00 0.5 0.00 0.00 0.0 0.00
91325 0.10 0.0 0.00 0.15 0.0 0.40 0.0 0.00 0.20 0.0 0.15
84633 0.15 0.0 0.00 0.35 0.0 0.30 0.0 0.10 0.00 0.1 0.00
32152 0.00 0.4 0.00 0.05 0.0 0.00 0.0 0.25 0.00 0.3 0.00
38612 0.00 0.4 0.00 0.00 0.0 0.35 0.0 0.10 0.00 0.0 0.15
作为
RcppAlgos公司
高效,以上所有步骤立即返回。在我的2008 Windows机器i5 2.5 GHz上,整个一代(包括排列)所需的时间不到0.04秒。