Do permutacji rcppalgos jest świetny. Niestety istnieje 479 milionów możliwości z 12 polami, co oznacza, że zabiera zbyt dużo pamięci dla większości ludzi:
library(RcppAlgos)
elements <- 12
permuteGeneral(elements, elements)
#> Error: cannot allocate vector of size 21.4 Gb
Istnieje kilka alternatyw.
Pobierz próbkę permutacji. To znaczy, zrób tylko 1 milion zamiast 479 milionów. Aby to zrobić, możesz użyć permuteSample(12, 12, n = 1e6). Zobacz odpowiedź @ JosephWood na nieco podobne podejście, z wyjątkiem tego, że pobiera próbki do 479 milionów permutacji;)
Zbuduj pętlę w rcpp, aby ocenić permutację podczas tworzenia. Oszczędza to pamięć, ponieważ w rezultacie budowałbyś funkcję zwracającą tylko prawidłowe wyniki.
Podejdź do problemu za pomocą innego algorytmu. Skoncentruję się na tej opcji.
Nowy algorytm z ograniczeniami

Segmenty powinny mieć 26
Wiemy, że każdy segment linii w gwiazdce powyżej musi dodać do 26. Możemy dodać to ograniczenie do generowania naszych permutacji - daj nam tylko kombinacje, które sumują się do 26:
# only certain combinations will add to 26
lucky_combo <- comboGeneral(12, 4, comparisonFun = '==', constraintFun = 'sum', limitConstraints = 26L)
Grupy ABCD i EFGH
W gwiazdce powyżej kolorowałem trzy grupy inaczej: ABCD , EFGH i IJLK . Dwie pierwsze grupy również nie mają wspólnych punktów i znajdują się również w interesujących segmentach linii. Dlatego możemy dodać kolejne ograniczenie: w przypadku kombinacji, które sumują się do 26, musimy upewnić się, że ABCD i EFGH nie nakładają się na siebie liczb. IJLK zostaną przypisane pozostałe 4 numery.
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)))
Permute poprzez grupy
Musimy znaleźć wszystkie permutacje każdej grupy. Oznacza to, że mamy tylko kombinacje, które sumują się do 26. Na przykład musimy wziąć 1, 2, 11, 12i wykonać 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]
Ostateczne obliczenia
Ostatnim krokiem jest zrobienie matematyki. Używam lapply()i Reduce()tutaj, aby robić bardziej funkcjonalne programowanie - w przeciwnym razie wiele kodu zostanie wpisanych sześć razy. Zobacz oryginalne rozwiązanie, aby uzyskać dokładniejsze objaśnienie kodu matematycznego.
# 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
Zamiana ABCD i EFGH
Na końcu powyższego kodu skorzystałem z możliwości wymiany ABCDi EFGHuzyskania pozostałych permutacji. Oto kod potwierdzający, że tak, możemy zamienić dwie grupy i być poprawnym:
# 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
Wydajność
Ostatecznie oceniliśmy tylko 1,3 miliona spośród 479 permutacji i tylko przetasowaliśmy tylko przez 550 MB pamięci RAM. Uruchomienie zajmuje około 0,7 sekundy
# 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:elementsi co ważniejszeL1 <- y[,1] + y[,3] + y[,6] + y[,8]. To naprawdę nie pomogłoby w problemie z pamięcią, więc zawsze możesz zajrzeć do rcpp