使用R查找所有小于给定数字的3个数字组合

时间:2019-08-10 13:26:53

标签: r dynamic combinations

我有以下一组数字10、17、5、7、15。从这些数字中,我需要找到总和小于或等于35的所有3个数字组合。在一个这样的组合中,特定数字不应包含多个。  例如:10,10,5是错误的组合,因为10重复了两次。

我尝试了这段代码,但没有给出我所需要的。

import { BreakpointObserver, Breakpoints, BreakpointState } from '@angular/cdk/layout';
import { Component, OnInit } from '@angular/core'


@Component({
  selector: 'navbar',
  templateUrl: './navbar.component.html',
  styleUrls: ['./navbar.component.css']
})
export class NavbarComponent implements OnInit {
  htmlStyles: string;

  constructor(private breakpointObserver: BreakpointObserver) { }

  ngOnInit() {
    this.breakpointObserver.observe([Breakpoints.Small])
      .subscribe((state: BreakpointState) => {
        if (state.matches) {
          this.htmlStyles = 'dummy1';
        } else {
          this.htmlStyles = 'dummy2';
        }
      });
  }
}

上述代码输出的一部分如下,

library(data.table)
df=expand.grid(x1=c(10,17,5,7,15),
               x2=c(10,17,5,7,15),
               x3=c(10,17,5,7,15)
               )
setDT(df)
df[(x1+x2+x3) <= 35]

根据上述输出,可以观察到一个数字出现多次。 有人可以提示获得预期结果的提示吗?

谢谢

5 个答案:

答案 0 :(得分:6)

请尝试以下操作,看看问题是否出在此。

x <- c(10,17,5,7,15)
i <- combn(x, 3, sum) <= 35

combn(x, 3)[, i]
#     [,1] [,2] [,3] [,4] [,5] [,6] [,7]
#[1,]   10   10   10   10   10   17    5
#[2,]   17   17    5    5    7    5    7
#[3,]    5    7    7   15   15    7   15

以上是一般想法。下面f2是一种更有效的内存和速度实现方式。

f1 <- function(x, n = 3, thres = 35){
  i <- combn(x, n, sum) <= thres
  combn(x, n)[, i]
}
f2 <- function(x, n = 3, thres = 35){
  cmb <- combn(x, n)
  cmb[, colSums(cmb) <= thres]
}

检查结果是否全部具有不同的数字。

res <- f2(x)
apply(res, 2, function(y){
  all(y[-1] != y[1])
})
#[1] TRUE TRUE TRUE TRUE TRUE TRUE TRUE

identical(f1(x), f2(x))
#[1] TRUE

现在是时间功能了。

microbenchmark::microbenchmark(f1 = f1(x), 
                               f2 = f2(x))
#Unit: microseconds
# expr     min      lq      mean   median      uq     max neval cld
#   f1 105.150 107.383 110.66616 108.6535 109.896 238.899   100   b
#   f2  62.779  65.568  67.65754  66.4290  67.145 122.119   100  a 

答案 1 :(得分:5)

软件包comboGeneral(我是作者)中的函数RcppAlgos是专门为此任务设计的。

library(RcppAlgos)
x <- c(10,17,5,7,15)

comboGeneral(x, 3, 
             constraintFun = "sum",
             comparisonFun = "<=",
             limitConstraints = 35)
     [,1] [,2] [,3]
[1,]    5    7   10
[2,]    5    7   15
[3,]    5    7   17
[4,]    5   10   15
[5,]    5   10   17
[6,]    7   10   15
[7,]    7   10   17

这也是非常有效的。观察:

set.seed(42)
s <- sample(100, 25)
s
[1] 92 93 29 81 62 50 70 13 61 65 42 91 83 23 40 80 88 10 39 46 73 11 78 85  7

system.time(a <- comboGeneral(s, 10, 
                              constraintFun = "sum",
                              comparisonFun = "<=",
                              limitConstraints = 600))
 user  system elapsed 
0.232   0.046   0.278

dim(a)
[1] 2252362      10

与@RuiBarradas发布的更高效的函数f2和@Cole发布的dt_checker相比:

system.time(b <- f2(s, 10, 600))
 user  system elapsed 
3.283   0.093   3.418

system.time(a2 <- dt_checker(s, 10, 600))
 user  system elapsed 
1.803   0.319   0.646

还应该注意,comboGeneral之后的算法会在获得解决方案后立即终止。因此,在不同的约束条件下,时序会有所不同。观察:

system.time(a <- comboGeneral(s, 10, 
                              constraintFun = "sum",
                              comparisonFun = "<=",
                              limitConstraints = 400))
 user  system elapsed 
0.003   0.001   0.003

但是,在其他解决方案中,必须先创建所有组合然后进行过滤(这不会花很长时间),因此时序与之前相似。

system.time(b <- f2(s, 10, 400))
 user  system elapsed 
2.933   0.039   2.973

system.time(a2 <- dt_checker(s, 10, 400))
 user  system elapsed 
1.786   0.276   0.627

作为最终基准,我们对所有约束条件下的所有结果进行基准测试:

system.time(a <- lapply(seq(200, 600, 25), function(x) {
    t <- comboGeneral(s, 10, 
                      constraintFun = "sum",
                      comparisonFun = "<=",
                      limitConstraints = x)
    dim(t)
}))
 user  system elapsed 
0.498   0.125   0.623

system.time(a2 <- lapply(seq(200, 600, 25), function(x) {
    t <- dt_checker(s, 10, x)
    dim(t)
}))
  user  system elapsed 
34.448   4.633  10.693

identical(a, a2)
[1] TRUE

答案 2 :(得分:4)

我们可以删除值为any duplicated的行,然后选择总和为<= 35的行

df1 <- df[!apply(df, 1, function(x) any(duplicated(x))), ]
df1[rowSums(df1) <= 35, ]

#    x1 x2 x3
#8    5 17 10
#9    7 17 10
#12  17  8 10
#13   5  8 10
#14   7  8 10

OP代码中的原始df具有c(10,17,5,7,15)的所有可能组合,并且有很多重复。使用apply循环,我们删除所有重复值的行。因此具有10、10的行将被删除,而具有17、17和其他重复的行将被删除。 df1是没有重复数字的数据框。现在,我们仅对总和小于35的那些行进行子集化。

答案 3 :(得分:4)

您可能不想在更多列上执行此操作,但这很简单:

df[(x1+x2+x3) <= 35 & x1 != x2 & x2 != x3 & x3 != x1] 

如果您认为10,17,5与5,10,17相同,因此只需保留一次,则:

df[(x1+x2+x3) <= 35 & x1 < x2 & x2 < x3] 

答案 4 :(得分:3)

这里的答案取决于def appcompat_version = "1.1.0-rc01" def material_version = "1.1.0-alpha09" // Java implementation "androidx.appcompat:appcompat:$appcompat_version" implementation "com.google.android.material:material:$material_version" 的非等额联接。大部分时间都花在操纵字符向量上,以便在data.table调用中求值。

dt

我主要是这样做的,以了解我能否比library(data.table) dt_checker <- function(y, n, criteria) { x_dt <- data.table(x1 = y) setkey(x_dt, x1) x_res <- copy(x_dt)[seq_len(length(y)-(n-1))] for (i in seq_len(n)[-1]) { setnames(x_dt, paste0('x', i)) cols <- paste0('x', seq_len(i)) cols2 <- cols cols2[i-1] <- paste0('x.', cols2[i-1]) x_res <- x_res[x_dt, on = paste(cols[c(i-1, i)], collapse = '<'), ..cols2, allow.cartesian = T, nomatch = 0L] setnames(x_res, cols) } x_res[x_res[, rowSums(.SD)<= criteria] ,] } dt_checker(x, 3, 35) x1 x2 x3 1: 5 7 10 2: 5 7 15 3: 5 10 15 4: 7 10 15 5: 5 7 17 6: 5 10 17 7: 7 10 17 解决方案更快地得到data.table。我不能,在花时间使所有这些连接自动化的逻辑上,我大概可以在RcppAlgos中找到它:)。

Rcpp

此外,对于较小的数据集,这也将起作用。但是对于较小的数据集,@ Rui的解决方案几乎与system.time(a <- comboGeneral(s, 10, + constraintFun = "sum", + comparisonFun = "<=", + limitConstraints = 600)) user system elapsed 0.10 0.13 0.23 system.time(a2 <- dt_checker(s, 10, 600)) user system elapsed 0.54 0.09 0.57 system.time(a3 <- f2(s, 10, 600)) user system elapsed 3.98 0.00 4.01 一样快,并且是基本解决方案。

RcppAlgos