Именно для этого и строились 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 миллионов комбинаций:
## N.B. Using R 4.0.0 with new updated RNG introduced in 3.6.0
set.seed(42)
bigVec <- sort(sample(1:30, 40, TRUE))
rle(bigVec)
Run Length Encoding
lengths: int [1:22] 2 1 2 3 4 1 1 1 2 1 ...
values : int [1:22] 1 2 3 4 5 7 8 9 10 11 ...
bigUni <- unique(bigVec)
bigRep <- rle(bigVec)$lengths
bigAns <- 199
len <- 12
comboCount(bigUni, len, freqs = bigRep)
[1] 32248100
Все 300000+ результатов возвращаются очень быстро:
system.time(bigTest <- comboGeneral(bigUni, len, freqs = bigRep,
constraintFun = "sum",
comparisonFun = "==",
limitConstraints = bigAns))
user system elapsed
0.273 0.004 0.271
head(bigTest)
[,1] [,2] [,3] [,4] [,5] [,6] [,7] [,8] [,9] [,10] [,11] [,12]
[1,] 1 1 2 3 4 25 26 26 26 27 28 30
[2,] 1 1 2 3 5 24 26 26 26 27 28 30
[3,] 1 1 2 3 5 25 25 26 26 27 28 30
[4,] 1 1 2 3 7 24 24 26 26 27 28 30
[5,] 1 1 2 3 7 24 25 25 26 27 28 30
[6,] 1 1 2 3 7 24 25 26 26 26 28 30
nrow(bigTest)
[1] 280018
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)
как целых чисел и последующее применение подхода целочисленных разделов потребовали бы экстравагантной вычислительной мощности, что сделало бы этот метод бесполезным).
person
Joseph Wood
schedule
10.11.2018