Permütasyonlar için rcppalgos harika. Ne yazık ki, 12 alanla 479 milyon olasılık var, bu da çoğu insan için çok fazla bellek kaplıyor:
library(RcppAlgos)
elements <- 12
permuteGeneral(elements, elements)
#> Error: cannot allocate vector of size 21.4 Gb
Bazı alternatifler var.
Permütasyonlardan bir örnek alın. Yani, 479 milyon yerine sadece 1 milyon. Bunu yapmak için kullanabilirsiniz permuteSample(12, 12, n = 1e6)
. 479 milyon permütasyonu örneklemenin dışında @ JosephWood'un biraz benzer bir yaklaşımın cevabına bakın;)
Oluşturmadaki permütasyonu değerlendirmek için rcpp'de bir döngü oluşturun. Bu, bellek tasarrufu sağlar, çünkü işlevi yalnızca doğru sonuçları döndürmek için oluşturursunuz.
Soruna farklı bir algoritma ile yaklaşın. Bu seçeneğe odaklanacağım.
Kısıtlı yeni algoritma
Segmentler 26 olmalıdır
Yukarıdaki yıldızdaki her bir çizgi segmentinin 26'ya kadar eklemesi gerektiğini biliyoruz. Permütasyonlarımızı oluşturmak için bu kısıtlamayı ekleyebiliriz - bize yalnızca 26'ya kadar olan kombinasyonlar verebiliriz:
# only certain combinations will add to 26
lucky_combo <- comboGeneral(12, 4, comparisonFun = '==', constraintFun = 'sum', limitConstraints = 26L)
ABCD ve EFGH grupları
Yukarıdaki yıldızda üç grubu farklı renklendirdim: ABCD , EFGH ve IJLK . İlk iki grubun da ortak noktaları yoktur ve aynı zamanda ilgili çizgi segmentlerinde yer alırlar. Bu nedenle, başka bir kısıtlama ekleyebiliriz: 26'ya kadar olan kombinasyonlar için, ABCD ve EFGH'nin sayı çakışması olmamasını sağlamalıyız . Kalan 4 numaraya IJLK atanacaktır.
library(RcppAlgos)
lucky_combo <- comboGeneral(12, 4, comparisonFun = '==', constraintFun = 'sum', limitConstraints = 26L)
two_combo <- comboGeneral(nrow(lucky_combo), 2)
unique_combos <- !apply(cbind(lucky_combo[two_combo[, 1], ], lucky_combo[two_combo[, 2], ]), 1, anyDuplicated)
grp1 <- lucky_combo[two_combo[unique_combos, 1],]
grp2 <- lucky_combo[two_combo[unique_combos, 2],]
grp3 <- t(apply(cbind(grp1, grp2), 1, function(x) setdiff(1:12, x)))
Gruplar arasında permütasyon
Her grubun tüm permütasyonlarını bulmalıyız. Yani, sadece 26'ya kadar olan kombinasyonlarımız var. Örneğin, almamız 1, 2, 11, 12
ve yapmamız gerekiyor 1, 2, 12, 11; 1, 12, 2, 11; ...
.
#create group perms (i.e., we need all permutations of grp1, grp2, and grp3)
n <- 4
grp_perms <- permuteGeneral(n, n)
n_perm <- nrow(grp_perms)
# We create all of the permutations of grp1. Then we have to repeat grp1 permutations
# for all grp2 permutations and then we need to repeat one more time for grp3 permutations.
stars <- cbind(do.call(rbind, lapply(asplit(grp1, 1), function(x) matrix(x[grp_perms], ncol = n)))[rep(seq_len(sum(unique_combos) * n_perm), each = n_perm^2), ],
do.call(rbind, lapply(asplit(grp2, 1), function(x) matrix(x[grp_perms], ncol = n)[rep(1:n_perm, n_perm), ]))[rep(seq_len(sum(unique_combos) * n_perm^2), each = n_perm), ],
do.call(rbind, lapply(asplit(grp3, 1), function(x) matrix(x[grp_perms], ncol = n)[rep(1:n_perm, n_perm^2), ])))
colnames(stars) <- LETTERS[1:12]
Son Hesaplamalar
Son adım matematik yapmaktır. Kullandığım lapply()
ve Reduce()
burada daha işlevsel programlama yapmak - Aksi kod çok altı kez yazılabilir olacaktır. Matematik kodunun daha ayrıntılı bir açıklaması için orijinal çözüme bakın.
# creating a list will simplify our math as we can use Reduce()
col_ind <- list(c('A', 'B', 'C', 'D'), #these two will always be 26
c('E', 'F', 'G', 'H'), #these two will always be 26
c('I', 'C', 'J', 'H'),
c('D', 'J', 'G', 'K'),
c('K', 'F', 'L', 'A'),
c('E', 'L', 'B', 'I'))
# Determine which permutations result in a lucky star
L <- lapply(col_ind, function(cols) rowSums(stars[, cols]) == 26)
soln <- Reduce(`&`, L)
# A couple of ways to analyze the result
rbind(stars[which(soln),], stars[which(soln), c(1,8, 9, 10, 11, 6, 7, 2, 3, 4, 5, 12)])
table(Reduce('+', L)) * 2
2 3 4 6
2090304 493824 69120 960
Değiştirme ABCD ve EFGH
Yukarıdaki kodun sonunda, takas edebilmemiz ABCD
ve EFGH
kalan permütasyonları elde etmemizden faydalandım . Evet, iki grubu değiştirebilir ve doğru olabiliriz:
# swap grp1 and grp2
stars2 <- stars[, c('E', 'F', 'G', 'H', 'A', 'B', 'C', 'D', 'I', 'J', 'K', 'L')]
# do the calculations again
L2 <- lapply(col_ind, function(cols) rowSums(stars2[, cols]) == 26)
soln2 <- Reduce(`&`, L2)
identical(soln, soln2)
#[1] TRUE
#show that col_ind[1:2] always equal 26:
sapply(L, all)
[1] TRUE TRUE FALSE FALSE FALSE FALSE
Verim
Sonunda, 479 permütasyonun sadece 1.3 milyonunu değerlendirdik ve sadece 550 MB RAM ile karıştırdık. Çalıştırmak yaklaşık 0.7s sürer
# A tibble: 1 x 13
expression min median `itr/sec` mem_alloc `gc/sec` n_itr n_gc
<bch:expr> <bch> <bch:> <dbl> <bch:byt> <dbl> <int> <dbl>
1 new_algo 688ms 688ms 1.45 550MB 7.27 1 5
x<- 1:elements
ve daha da önemlisiL1 <- y[,1] + y[,3] + y[,6] + y[,8]
. Bu, bellek sorununuza gerçekten yardımcı olmaz, böylece her zaman rcpp'ye bakabilirsiniz