Это именно то, для чего были созданы combo/permuteGeneral
из RcppAlgos
(я автор). Поскольку у нас есть повторение определенных элементов в нашем образце вектора, мы будем находить комбинации мультимножеств , которые соответствуют нашим критериям. Обратите внимание, что это отличается от более распространенного случая генерации комбинаций с повторением, когда каждый элемент может повторяться m раз. Для многих функций генерации комбинаций мультимножества создают проблемы, так как вводятся дубликаты, и с ними нужно бороться. Это может стать узким местом в вашем коде, если размер ваших данных достаточно велик. Функции в RcppAlgos
эффективно обрабатывают эти случаи, не создавая дублирующих результатов. Я должен упомянуть, что есть несколько других замечательных библиотек, которые хорошо справляются с мультимножествами: multicool
и arrangements
.
Переходя к поставленной задаче, мы можем использовать аргументы ограничения comboGeneral
, чтобы найти все комбинации нашего вектора, которые соответствуют определенным критериям:
vec <- c(1,1,2,3,5) ## using variables from @r2evans
uni <- unique(vec)
myRep <- rle(vec)$lengths
ans <- 5
library(RcppAlgos)
lapply(seq_along(uni), function(x) {
comboGeneral(uni, x, freqs = myRep,
constraintFun = "sum",
comparisonFun = "==",
limitConstraints = ans)
})
[[1]]
[,1]
[1,] 5
[[2]]
[,1] [,2]
[1,] 2 3
[[3]]
[,1] [,2] [,3]
[1,] 1 1 3
[[4]]
[,1] [,2] [,3] [,4] ## no solutions of length 4
Эти функции высоко оптимизированы и хорошо подходят для более крупных случаев. Например, рассмотрим следующий пример, который даст более 30 миллионов комбинаций:
set.seed(42)
bigVec <- sort(sample(1:30, 40, TRUE))
rle(bigVec)
Run Length Encoding
lengths: int [1:22] 2 1 1 2 1 1 1 2 3 1 ...
values : int [1:22] 1 3 4 5 7 8 9 12 14 15 ...
bigUni <- unique(bigVec)
bigRep <- rle(bigVec)$lengths
bigAns <- 199
len <- 12
comboCount(bigUni, len, freqs = bigRep)
[1] 30904021
Все результаты 300000+ возвращаются очень быстро:
system.time(bigTest <- comboGeneral(bigUni, len, freqs = bigRep,
constraintFun = "sum",
comparisonFun = "==",
limitConstraints = bigAns))
user system elapsed
0.383 0.008 0.390
head(bigTest)
[,1] [,2] [,3] [,4] [,5] [,6] [,7] [,8] [,9] [,10] [,11] [,12]
[1,] 1 1 3 4 5 9 29 29 29 29 30 30
[2,] 1 1 3 4 5 12 26 29 29 29 30 30
[3,] 1 1 3 4 5 12 28 28 28 29 30 30
[4,] 1 1 3 4 5 12 28 28 29 29 29 30
[5,] 1 1 3 4 5 14 25 28 29 29 30 30
[6,] 1 1 3 4 5 14 25 29 29 29 29 30
nrow(bigTest)
[1] 370646
all(rowSums(bigTest) == bigAns)
[1] TRUE
Добавление
Я должен упомянуть, что, как правило, когда я вижу такую проблему, как: "поиск всех комбинаций, которые суммируются с определенным числом" , моя первая мысль - целочисленные разбиения . Например, в связанной задаче Получение всех комбинаций с суммой до 100 в R , мы можем легко решить с помощью библиотеки partitions
. Однако этот подход не распространяется на общий случай (как мы имеем здесь), где вектор содержит конкретное повторение, или у нас есть вектор, который содержит значения, которые нелегко преобразовать в целочисленный эквивалент (например, вектор (0.1, 0.2, 0.3, 0.4)
может легко следует рассматривать как 1:4
, однако обработка c(3.98486 7.84692 0.0038937 7.4879)
как целых чисел и последующее применение подхода целочисленных разбиений потребует чрезмерной вычислительной мощности, делающей этот метод бесполезным).