【问题标题】:R distribute weights over a vectorR在向量上分配权重
【发布时间】:2018-08-07 22:54:35
【问题描述】:

假设我在 R 中有一个向量

 0    1    0    0    1    0    0    0    0     1     0

向量中的任何位置都不能有超过 6 个“1”。所有其他元素都是 0。

我正在尝试获取所有可能的值,其中我在 1 个位置上分配“1”,每个值必须为

例如:

0    .2    0    0    .3    0    0    0    0     .5     0 . <- OK

0    .35    0    0    .4    0    0    0    0     .25     0 <- OK

然而

0    .2   0    0    .2    0    0    0    0     .6     0  <- not ok

增量可以增加 0.05。

因此,在具有 3 个“1”的向量中,最多有 20^3 种组合,其中许多组合是不好的,因为它们的总和大于 1 或值 >0.5。有没有比暴力破解更快的方法?

编辑: 我意识到我可以使用以下方法快速得出所有可能的权重:

temp <- expand.grid(replicate(sum(x),seq(0.05,.5,0.05), simplify=FALSE))

x 是我的向量。

所以现在对于 temp 中的每一个,我想把它们放在 1 所在的位置

 0    1    0    0    1    0    0    0    0     1     0

【问题讨论】:

  • This 会有所帮助。

标签: r combinations


【解决方案1】:

编辑:正如@www 在 cmets 中指出的那样,如果您依赖浮点运算,您将错过一些组合/排列。为了解决这个问题,我们需要使用整数精度(即我们需要 seq(0L, 50L, 5L) 而不是 seq(0, 0.5, 0.05))并将我们的结果除以 100。

我编写了包RcppAlgos,专门用于解决以下问题:

library(RcppAlgos)
myCombs <- comboGeneral(seq(0L,50L,5L), 6, TRUE, 
                        constraintFun = "sum", 
                        comparisonFun = "==", 
                        limitConstraints = 100L) / 100
head(myCombs, n = 10)
      [,1] [,2] [,3] [,4] [,5] [,6]
 [1,]    0    0    0 0.00 0.50 0.50
 [2,]    0    0    0 0.05 0.45 0.50
 [3,]    0    0    0 0.10 0.40 0.50
 [4,]    0    0    0 0.10 0.45 0.45
 [5,]    0    0    0 0.15 0.35 0.50
 [6,]    0    0    0 0.15 0.40 0.45
 [7,]    0    0    0 0.20 0.30 0.50
 [8,]    0    0    0 0.20 0.35 0.45
 [9,]    0    0    0 0.20 0.40 0.40
[10,]    0    0    0 0.25 0.25 0.50

tail(myCombs, n = 10)
       [,1] [,2] [,3] [,4] [,5] [,6]
[190,] 0.10 0.10 0.15 0.15 0.15 0.35
[191,] 0.10 0.10 0.15 0.15 0.20 0.30
[192,] 0.10 0.10 0.15 0.15 0.25 0.25
[193,] 0.10 0.10 0.15 0.20 0.20 0.25
[194,] 0.10 0.10 0.20 0.20 0.20 0.20
[195,] 0.10 0.15 0.15 0.15 0.15 0.30
[196,] 0.10 0.15 0.15 0.15 0.20 0.25
[197,] 0.10 0.15 0.15 0.20 0.20 0.20
[198,] 0.15 0.15 0.15 0.15 0.15 0.25
[199,] 0.15 0.15 0.15 0.15 0.20 0.20

如果你对排列感兴趣,没问题:

myPerms <- permuteGeneral(seq(0L,50L,5L), 6, TRUE, 
                          constraintFun = "sum", 
                          comparisonFun = "==", 
                          limitConstraints = 100L) / 100

head(myPerms, n = 10)
      [,1] [,2] [,3] [,4] [,5] [,6]
 [1,]    0  0.0  0.0  0.0  0.5  0.5
 [2,]    0  0.0  0.0  0.5  0.0  0.5
 [3,]    0  0.0  0.0  0.5  0.5  0.0
 [4,]    0  0.0  0.5  0.0  0.0  0.5
 [5,]    0  0.0  0.5  0.0  0.5  0.0
 [6,]    0  0.0  0.5  0.5  0.0  0.0
 [7,]    0  0.5  0.0  0.0  0.0  0.5
 [8,]    0  0.5  0.0  0.0  0.5  0.0
 [9,]    0  0.5  0.0  0.5  0.0  0.0
[10,]    0  0.5  0.5  0.0  0.0  0.0

tail(myPerms, n = 10)
         [,1] [,2] [,3] [,4] [,5] [,6]
[41109,] 0.15 0.15 0.20 0.20 0.15 0.15
[41110,] 0.15 0.20 0.15 0.15 0.15 0.20
[41111,] 0.15 0.20 0.15 0.15 0.20 0.15
[41112,] 0.15 0.20 0.15 0.20 0.15 0.15
[41113,] 0.15 0.20 0.20 0.15 0.15 0.15
[41114,] 0.20 0.15 0.15 0.15 0.15 0.20
[41115,] 0.20 0.15 0.15 0.15 0.20 0.15
[41116,] 0.20 0.15 0.15 0.20 0.15 0.15
[41117,] 0.20 0.15 0.20 0.15 0.15 0.15
[41118,] 0.20 0.20 0.15 0.15 0.15 0.15

结果立竿见影:

system.time(permuteGeneral(seq(0L,50L,5L), 6, TRUE, 
                           constraintFun = "sum", 
                           comparisonFun = "==", 
                           limitConstraints = 100L) / 100)
 user  system elapsed 
0.005   0.001   0.006


快速思考
人们可能会试图将这个问题作为一个加法整数分区问题来解决。存在从seq(0, 0.5, 0.05)0:11 的映射以及从seq(0, 1, 0.05)0:20 的映射。后者可能不清楚为什么它有帮助,但确实如此。有一个非常棒的包,叫做partitions,它配备了一个生成受限分区(即给定长度的分区)的功能。

library(partitions)
myParts <- t(as.matrix(restrictedparts(20, 6))) / 20

head(myParts)
     [,1] [,2] [,3] [,4] [,5] [,6]
[1,] 1.00 0.00    0    0    0    0
[2,] 0.95 0.05    0    0    0    0
[3,] 0.90 0.10    0    0    0    0
[4,] 0.85 0.15    0    0    0    0
[5,] 0.80 0.20    0    0    0    0
[6,] 0.75 0.25    0    0    0    0

如您所见,我们已经违反了数字大于 0.5 的要求。所以我们必须做一些额外的工作才能得到我们的最终结果:

myMax <- apply(myParts, 1, max)
myFinalParts <- myParts[-which(myMax > 0.5), ]

head(myFinalParts)
     [,1] [,2] [,3] [,4] [,5] [,6]
[1,] 0.50 0.50 0.00    0    0    0
[2,] 0.50 0.45 0.05    0    0    0
[3,] 0.50 0.40 0.10    0    0    0
[4,] 0.45 0.45 0.10    0    0    0
[5,] 0.50 0.35 0.15    0    0    0
[6,] 0.45 0.40 0.15    0    0    0

tail(myFinalParts, n = 10)
       [,1] [,2] [,3] [,4] [,5] [,6]
[190,] 0.35 0.15 0.15 0.15 0.10 0.10
[191,] 0.30 0.20 0.15 0.15 0.10 0.10
[192,] 0.25 0.25 0.15 0.15 0.10 0.10
[193,] 0.25 0.20 0.20 0.15 0.10 0.10
[194,] 0.20 0.20 0.20 0.20 0.10 0.10
[195,] 0.30 0.15 0.15 0.15 0.15 0.10
[196,] 0.25 0.20 0.15 0.15 0.15 0.10
[197,] 0.20 0.20 0.20 0.15 0.15 0.10
[198,] 0.25 0.15 0.15 0.15 0.15 0.15
[199,] 0.20 0.20 0.15 0.15 0.15 0.15

如您所见,我们有与上述完全相同的解决方案(请参阅myCombs),只是列的顺序不同。

all.equal(myCombs, myFinalParts[,6:1])
[1] TRUE

对于置换部分,这些实际上被称为受限整数compositions。我们可以调用partitions::compositions 并与上面类似地继续,我们需要清除那些违反我们规则的行(即删除包含大于 0.5 的最大值的行)。使用分区可以获得所需的结果,只是涉及一些额外的步骤。

myComps <- t(as.matrix(compositions(20, 6))) / 20
myMax <- apply(myComps, 1, max)
temp <- myComps[-which(myMax > 0.5), ]
myFinalComps <- temp[do.call(order, as.data.frame(temp)), ]
all.equal(myPerms[do.call(order, as.data.frame(myPerms)), ], myFinalComps)
[1] TRUE

【讨论】:

  • 您不认为您没有考虑到您只需要具有仅在 1 指示的位置具有值的向量这一事实吗?
  • 为漂亮的包装和良好的演示点赞。如果你不介意,我有一个关于你的包裹的相关问题。我尝试了以下代码myCombs &lt;- permuteGeneral(seq(0.05, 0.95, 0.05), 3, TRUE, constraintFun = "sum", comparisonFun = "==", limitConstraints = 1),它给了我一个 162 行的矩阵。但是,我找不到像0.9, 0.05, 0.05 这样的组合。你能给出一些我可能做错的步骤的提示吗?
  • 这是由于缺乏精确性。如果你改为myCombs &lt;- permuteGeneral(seq(5L, 95L, 5L), 3, TRUE, constraintFun = "sum", comparisonFun = "==", limitConstraints = 100L)/100,你会得到正确的答案。我将更新我的答案以反映这一点。谢谢
  • @www,从版本 2.2.0 开始,已修复精度不足的问题。如果传递的向量的类是numeric(即双精度数据类型),则在机器精度范围内比较结果。例如,如果我们正在寻找结果,比如说 comb 与特定数字 a 相加,而不是检查 comb = a,我们检查查看 abs(comb - a) 是否满足。所有这些都是在几乎没有额外开销的情况下完成的。
  • @JosephWood 感谢您的出色工作并分享此信息。
【解决方案2】:

这是一种可能的选择。 dat5 是最终输出。

# Create all possible combination from 1 to 19
dat1 <- expand.grid(L1 = 1:19, 
                    L2 = 1:19,
                    L3 = 1:19)

# Filter for the rows with sum = 20
dat2 <- dat1[rowSums(dat1) == 20L, ]

# Filter for the rows with no any numbers larger than 10
dat3 <- dat2[rowSums(dat2 > 10) == 0L, ]

# Convert the values by multiplied 0.05
dat4 <- dat3 * 0.05

# Convert the data frame to a list of vectors
dat4$ID <- 1:nrow(dat4)

dat5 <- lapply(split(dat4, f = dat4$ID), function(x){
  c(0, x$L1, 0, 0, x$L2, 0, 0, 0, 0, x$L3, 0)
})

【讨论】:

    【解决方案3】:

    我相信我们只需要替换给定向量中的 1。在这种情况下,零点保持不变:

       s = c(0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 0)
       m = expand.grid(replicate(sum(s==1),seq(0,0.5,0.05),F))
        indx = replace(replace(s,s==1,1:ncol(m)),s==0,ncol(m)+1)
    
        dat = unname(cbind(m[rowSums(m)==1,],0)[indx])
        head(dat)
    
    121 0 0.50 0 0 0.50 0 0 0 0 0.00 0
    231 0 0.50 0 0 0.45 0 0 0 0 0.05 0
    241 0 0.45 0 0 0.50 0 0 0 0 0.05 0
    341 0 0.50 0 0 0.40 0 0 0 0 0.10 0
    351 0 0.45 0 0 0.45 0 0 0 0 0.10 0
    361 0 0.40 0 0 0.50 0 0 0 0 0.10 0
     tail(dat)
    
    1271 0 0.25 0 0 0.25 0 0 0 0 0.5 0
    1281 0 0.20 0 0 0.30 0 0 0 0 0.5 0
    1291 0 0.15 0 0 0.35 0 0 0 0 0.5 0
    1301 0 0.10 0 0 0.40 0 0 0 0 0.5 0
    1311 0 0.05 0 0 0.45 0 0 0 0 0.5 0
    1321 0 0.00 0 0 0.50 0 0 0 0 0.5 0 
    

    【讨论】:

      猜你喜欢
      • 2020-06-11
      • 2014-10-08
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-03-25
      • 1970-01-01
      • 2015-08-01
      相关资源
      最近更新 更多