web-dev-qa-db-fra.com

Permutations et combinaisons avec / sans remplacement et pour des articles distincts / non distincts / multiset

Dans ce fil, j'essaie d'inclure ici toutes les questions fréquemment posées et leurs réponses. J'espère que cela sera utile pour quelqu'un.

Question générale : Comment générer des séquences d'objets r à partir d'objets n?

  • combinaison vs permutation.
  • avec remplacement vs sans remplacement.
  • éléments distincts vs éléments non distincts (multisets).

Il y a au total 2^3=8 questions de ce type.

[Mise à jour]

Josh O'Brien suggère que les 8 questions sont liées à douze fois . En effet, les questions "distinctes" sont incluses en douze volets, tandis que les questions "non distinctes" ne sont pas incluses. Quoi qu'il en soit, il est intéressant de comparer les 8 questions ici de manière décuplée. Voir les commentaires pour d'autres lectures.

26
Randy Lai

[~ # ~] modifier [~ # ~]: J'ai mis à jour la réponse pour utiliser un package plus efficace arrangements

Commencer à utiliser arrangement

arrangements contient quelques générateurs et itérateurs efficaces pour les permutations et les combinaisons. Il a été démontré que arrangements surpasse la plupart des packages existants de type similaire. Quelques repères ont pu être trouvés ici .

Voici les réponses aux questions ci-dessus

# 1) combinations: without replacement: distinct items

combinations(5, 2)

      [,1] [,2]
 [1,]    1    2
 [2,]    1    3
 [3,]    1    4
 [4,]    1    5
 [5,]    2    3
 [6,]    2    4
 [7,]    2    5
 [8,]    3    4
 [9,]    3    5
[10,]    4    5


# 2) combinations: with replacement: distinct items

combinations(5, 2, replace=TRUE)

      [,1] [,2]
 [1,]    1    1
 [2,]    1    2
 [3,]    1    3
 [4,]    1    4
 [5,]    1    5
 [6,]    2    2
 [7,]    2    3
 [8,]    2    4
 [9,]    2    5
[10,]    3    3
[11,]    3    4
[12,]    3    5
[13,]    4    4
[14,]    4    5
[15,]    5    5



# 3) combinations: without replacement: non distinct items

combinations(x = c("a", "b", "c"), freq = c(2, 1, 1), k = 2)

     [,1] [,2]
[1,] "a"  "a" 
[2,] "a"  "b" 
[3,] "a"  "c" 
[4,] "b"  "c" 



# 4) combinations: with replacement: non distinct items

combinations(x = c("a", "b", "c"), k = 2, replace = TRUE)  # as `freq` does not matter

     [,1] [,2]
[1,] "a"  "a" 
[2,] "a"  "b" 
[3,] "a"  "c" 
[4,] "b"  "b" 
[5,] "b"  "c" 
[6,] "c"  "c" 

# 5) permutations: without replacement: distinct items

permutations(5, 2)

      [,1] [,2]
 [1,]    1    2
 [2,]    1    3
 [3,]    1    4
 [4,]    1    5
 [5,]    2    1
 [6,]    2    3
 [7,]    2    4
 [8,]    2    5
 [9,]    3    1
[10,]    3    2
[11,]    3    4
[12,]    3    5
[13,]    4    1
[14,]    4    2
[15,]    4    3
[16,]    4    5
[17,]    5    1
[18,]    5    2
[19,]    5    3
[20,]    5    4



# 6) permutations: with replacement: distinct items

permutations(5, 2, replace = TRUE)

      [,1] [,2]
 [1,]    1    1
 [2,]    1    2
 [3,]    1    3
 [4,]    1    4
 [5,]    1    5
 [6,]    2    1
 [7,]    2    2
 [8,]    2    3
 [9,]    2    4
[10,]    2    5
[11,]    3    1
[12,]    3    2
[13,]    3    3
[14,]    3    4
[15,]    3    5
[16,]    4    1
[17,]    4    2
[18,]    4    3
[19,]    4    4
[20,]    4    5
[21,]    5    1
[22,]    5    2
[23,]    5    3
[24,]    5    4
[25,]    5    5


# 7) permutations: without replacement: non distinct items

permutations(x = c("a", "b", "c"), freq = c(2, 1, 1), k = 2)

     [,1] [,2]
[1,] "a"  "a" 
[2,] "a"  "b" 
[3,] "a"  "c" 
[4,] "b"  "a" 
[5,] "b"  "c" 
[6,] "c"  "a" 
[7,] "c"  "b" 



# 8) permutations: with replacement: non distinct items

permutations(x = c("a", "b", "c"), k = 2, replace = TRUE)  # as `freq` doesn't matter

      [,1] [,2]
 [1,] "a"  "a" 
 [2,] "a"  "b" 
 [3,] "a"  "c" 
 [4,] "b"  "a" 
 [5,] "b"  "b" 
 [6,] "b"  "c" 
 [7,] "c"  "a" 
 [8,] "c"  "b" 
 [9,] "c"  "c" 

Comparer avec d'autres packages

Il y a peu d'avantages à utiliser arrangements par rapport aux packages existants.

  1. Framework intégré: vous n'avez pas besoin d'utiliser différents packages pour différentes méthodes.

  2. C'est très efficace. Voir https://randy3k.github.io/arrangements/articles/benchmark.html pour certains repères.

  3. Il est efficace en mémoire, il est capable de générer les 13! permutation de 1 à 13, les packages existants ne le feront pas en raison de la limitation de la taille de la matrice. La méthode getnext() des itérateurs permet aux utilisateurs d'obtenir les arrangements un par un.

  4. Les dispositions générées sont dans l'ordre du dictionnaire, ce qui peut être souhaité pour certains utilisateurs.

22
Randy Lai

Une promenade à travers une tranche de combinatoire dans R *

Ci-dessous, nous examinons les packages équipés des capacités de génération de combinaisons et de permutations. Si j'ai omis un colis, veuillez me pardonner et laisser un commentaire ou mieux encore, modifier ce post.

Aperçu de l'analyse:

  1. Introduction
  2. Combinaisons
  3. Permutations
  4. Multisets
  5. Résumé
  6. Mémoire

Avant de commencer, nous notons que les combinaisons/permutations avec remplacement des éléments distincts et non distint choisis m at un temps sont équivalents. Il en est ainsi, car lorsque nous avons un remplacement, ce n'est pas spécifique. Ainsi, quel que soit le nombre de fois où un élément particulier se produit à l'origine, la sortie aura une ou des instances de cet élément répétées de 1 à m fois.

1. Introduction

  1. gtools v 3.8.1
  2. combinat v 0.0-8
  3. multicool v 0,1-10
  4. partitions v 1.9-19
  5. RcppAlgos v 2.0.1 (je suis l'auteur)
  6. arrangements v 1.1.0
  7. gRbase v 1.8-3

Je n'ai pas inclus permute, permutations ou gRbase::aperm/ar_perm Car ils ne sont pas vraiment destinés à attaquer ces types de problèmes.

| --------------------------------------- [ ~ # ~] présentation [~ # ~] ------------------------------- ------

|_______________| gtools | combinat | multicool | partitions | 
|      comb rep |  Yes   |          |           |            | 
|   comb NO rep |  Yes   |   Yes    |           |            | 
|      perm rep |  Yes   |          |           |            |  
|   perm NO rep |  Yes   |   Yes    |    Yes    |    Yes     |
| perm multiset |        |          |    Yes    |            |  
| comb multiset |        |          |           |            |  
|accepts factors|        |   Yes    |           |            |  
|   m at a time |  Yes   |  Yes/No  |           |            |  
|general vector |  Yes   |   Yes    |    Yes    |            |
|    iterable   |        |          |    Yes    |            |
|parallelizable |        |          |           |            |
|  big integer  |        |          |           |            |

|_______________| iterpc | arrangements | RcppAlgos | gRbase |
|      comb rep |  Yes   |     Yes      |    Yes    |        |
|   comb NO rep |  Yes   |     Yes      |    Yes    |  Yes   |   
|      perm rep |  Yes   |     Yes      |    Yes    |        |
|   perm NO rep |  Yes   |     Yes      |    Yes    |   *    |
| perm multiset |  Yes   |     Yes      |    Yes    |        |
| comb multiset |  Yes   |     Yes      |    Yes    |        |
|accepts factors|        |     Yes      |    Yes    |        |
|   m at a time |  Yes   |     Yes      |    Yes    |  Yes   |
|general vector |  Yes   |     Yes      |    Yes    |  Yes   |
|    iterable   |        |     Yes      | Partially |        |
|parallelizable |        |     Yes      |    Yes    |        |
|  big integer  |        |     Yes      |           |        |

Les tâches, m at a time Et general vector, Font référence à la capacité de générer des résultats " m à la fois" (lorsque m = est inférieur à la longueur du vecteur) et réorganise un "vecteur général" par opposition à 1:n. Dans la pratique, nous nous préoccupons généralement de trouver des réarrangements d'un vecteur général, donc tous les examens ci-dessous le refléteront (si possible).

Tous les benchmarks ont été exécutés sur 3 configurations différentes.

  1. Macbook Pro i7 16Gb
  2. Macbook Air i5 4Gb
  3. Lenovo sous Windows 7 i5 8 Go

Les résultats répertoriés ont été obtenus à partir de la configuration n ° 1 (c'est-à-dire MBPro). Les résultats pour les deux autres systèmes étaient similaires. De plus, gc() est périodiquement appelée pour s'assurer que toute la mémoire est disponible (voir ?gc).

2. Combinaisons

Tout d'abord, nous examinons les combinaisons sans remplacement choisies m à la fois.

  1. RcppAlgos
  2. combinat (ou utils)
  3. gtools
  4. arrangements
  5. gRbase

Comment:

library(RcppAlgos)
library(arrangements)
library(microbenchmark)
options(digits = 4)
set.seed(13)
testVector1 <- sort(sample(100, 17))
m <- 9
t1 <- comboGeneral(testVector1, m)  ## returns matrix with m columns
t3 <- combinat::combn(testVector1, m)  ## returns matrix with m rows
t4 <- gtools::combinations(17, m, testVector1)  ## returns matrix with m columns
identical(t(t3), t4) ## must transpose to compare
#> [1] TRUE
t5 <- combinations(testVector1, m)
identical(t1, t5)
#> [1] TRUE
t6 <- gRbase::combnPrim(testVector1, m)
identical(t(t6)[do.call(order, as.data.frame(t(t6))),], t1)
#> [1] TRUE

Référence:

microbenchmark(cbRcppAlgos = comboGeneral(testVector1, m),
               cbGRbase = gRbase::combnPrim(testVector1, m),
               cbGtools = gtools::combinations(17, m, testVector1),
               cbCombinat = combinat::combn(testVector1, m),
               cbArrangements = combinations(17, m, testVector1),
               unit = "relative")
#> Unit: relative
#>            expr     min      lq    mean  median      uq    max neval
#>     cbRcppAlgos   1.064   1.079   1.160   1.012   1.086  2.318   100
#>        cbGRbase   7.335   7.509   5.728   6.807   5.390  1.608   100
#>        cbGtools 426.536 408.807 240.101 310.848 187.034 63.663   100
#>      cbCombinat  97.756  97.586  60.406  75.415  46.391 41.089   100
#>  cbArrangements   1.000   1.000   1.000   1.000   1.000  1.000   100

Maintenant, nous examinons les combinaisons avec le remplacement choisi m à la fois.

  1. RcppAlgos
  2. gtools
  3. arrangements

Comment:

library(RcppAlgos)
library(arrangements)
library(microbenchmark)
options(digits = 4)
set.seed(97)
testVector2 <- sort(rnorm(10))
m <- 8
t1 <- comboGeneral(testVector2, m, repetition = TRUE)
t3 <- gtools::combinations(10, m, testVector2, repeats.allowed = TRUE)
identical(t1, t3)
#> [1] TRUE
## arrangements
t4 <- combinations(testVector2, m, replace = TRUE)
identical(t1, t4)
#> [1] TRUE

Référence:

microbenchmark(cbRcppAlgos = comboGeneral(testVector2, m, TRUE),
               cbGtools = gtools::combinations(10, m, testVector2, repeats.allowed = TRUE),
               cbArrangements = combinations(testVector2, m, replace = TRUE),
               unit = "relative")
#> Unit: relative
#>            expr     min      lq   mean  median      uq     max neval
#>     cbRcppAlgos   1.000   1.000  1.000   1.000   1.000 1.00000   100
#>        cbGtools 384.990 269.683 80.027 112.170 102.432 3.67517   100
#>  cbArrangements   1.057   1.116  0.618   1.052   1.002 0.03638   100

3. Permutations

Tout d'abord, nous examinons les permutations sans remplacement choisies m à la fois.

  1. RcppAlgos
  2. gtools
  3. arrangements

Comment:

library(RcppAlgos)
library(arrangements)
library(microbenchmark)
options(digits = 4)
set.seed(101)
testVector3 <- as.integer(c(2, 3, 5, 7, 11, 13, 17, 19, 23, 29))

## RcppAlgos... permuteGeneral same as comboGeneral above
t1 <- permuteGeneral(testVector3, 6)
## gtools... permutations same as combinations above
t3 <- gtools::permutations(10, 6, testVector3)
identical(t1, t3)
#> [1] TRUE
## arrangements
t4 <- permutations(testVector3, 6)
identical(t1, t4)
#> [1] TRUE

Référence:

microbenchmark(cbRcppAlgos = permuteGeneral(testVector3, 6),
               cbGtools = gtools::permutations(10, 6, testVector3),
               cbArrangements = permutations(testVector3, 6),
               unit = "relative")
#> Unit: relative
#>            expr     min     lq   mean median     uq   max neval
#>     cbRcppAlgos   1.079  1.027  1.106  1.037  1.003  5.37   100
#>        cbGtools 158.720 92.261 85.160 91.856 80.872 45.39   100
#>  cbArrangements   1.000  1.000  1.000  1.000  1.000  1.00   100

Ensuite, nous examinons les permutations sans remplacement par un vecteur général (renvoyant toutes les permutations).

  1. RcppAlgos
  2. gtools
  3. combinat
  4. multicool
  5. arrangements

Comment:

library(RcppAlgos)
library(arrangements)
library(microbenchmark)
options(digits = 4)
set.seed(89)
testVector3 <- as.integer(c(2, 3, 5, 7, 11, 13, 17, 19, 23, 29))
testVector3Prime <- testVector3[1:7]
## For RcppAlgos, & gtools (see above)

## combinat
t4 <- combinat::permn(testVector3Prime) ## returns a list of vectors
## convert to a matrix
t4 <- do.call(rbind, t4)
## multicool.. we must first call initMC
t5 <- multicool::allPerm(multicool::initMC(testVector3Prime)) ## returns a matrix with n columns
all.equal(t4[do.call(order,as.data.frame(t4)),],
          t5[do.call(order,as.data.frame(t5)),])
#> [1] TRUE

Référence:

microbenchmark(cbRcppAlgos = permuteGeneral(testVector3Prime, 7),
               cbGtools = gtools::permutations(7, 7, testVector3Prime),
               cbCombinat = combinat::permn(testVector3Prime),
               cbMulticool = multicool::allPerm(multicool::initMC(testVector3Prime)),
               cbArrangements = permutations(x = testVector3Prime, k = 7),
               unit = "relative")
#> Unit: relative
#>            expr      min       lq     mean   median       uq     max neval
#>     cbRcppAlgos    1.152    1.275   0.7508    1.348    1.342  0.3159   100
#>        cbGtools  965.465  817.645 340.4159  818.137  661.068 12.7042   100
#>      cbCombinat  280.207  236.853 104.4777  238.228  208.467  9.6550   100
#>     cbMulticool 2573.001 2109.246 851.3575 2039.531 1638.500 28.3597   100
#>  cbArrangements    1.000    1.000   1.0000    1.000    1.000  1.0000   100

Maintenant, nous examinons les permutations sans remplacement pour 1:n (Renvoyant toutes les permutations).

  1. RcppAlgos
  2. gtools
  3. combinat
  4. multicool
  5. partitions
  6. arrangements

Comment:

library(RcppAlgos)
library(arrangements)
library(microbenchmark)
options(digits = 4)
set.seed(89)
t1 <- partitions::perms(7)  ## returns an object of type 'partition' with n rows
identical(t(as.matrix(t1)), permutations(7,7))
#> [1] TRUE

Référence:

microbenchmark(cbRcppAlgos = permuteGeneral(7, 7),
               cbGtools = gtools::permutations(7, 7),
               cbCombinat = combinat::permn(7),
               cbMulticool = multicool::allPerm(multicool::initMC(1:7)),
               cbPartitions = partitions::perms(7),
               cbArrangements = permutations(7, 7),
               unit = "relative")
#> Unit: relative
#>            expr      min       lq     mean   median       uq      max
#>     cbRcppAlgos    1.235    1.429    1.412    1.503    1.484    1.720
#>        cbGtools 1152.826 1000.736  812.620  939.565  793.373  499.029
#>      cbCombinat  347.446  304.866  260.294  296.521  248.343  284.001
#>     cbMulticool 3001.517 2416.716 1903.903 2237.362 1811.006 1311.219
#>    cbPartitions    2.469    2.536    2.801    2.692    2.999    2.472
#>  cbArrangements    1.000    1.000    1.000    1.000    1.000    1.000
#>  neval
#>    100
#>    100
#>    100
#>    100
#>    100
#>    100

Enfin, nous examinons les permutations avec remplacement.

  1. RcppAlgos
  2. iterpc
  3. gtools
  4. arrangements

Comment:

library(RcppAlgos)
library(arrangements)
library(microbenchmark)
options(digits = 4)
set.seed(34)
testVector3 <- as.integer(c(2, 3, 5, 7, 11, 13, 17, 19, 23, 29))
t1 <- permuteGeneral(testVector3, 5, repetition = TRUE)
t3 <- gtools::permutations(10, 5, testVector3, repeats.allowed = TRUE)
t4 <- permutations(x = testVector3, k = 5, replace = TRUE)

Ce prochain benchmark est un peu surprenant compte tenu des résultats obtenus jusqu'à présent.

microbenchmark(cbRcppAlgos = permuteGeneral(testVector3, 5, TRUE),
               cbGtools = gtools::permutations(10, 5, testVector3, repeats.allowed = TRUE),
               cbArrangements = permutations(x = testVector3, k = 5, replace = TRUE),
               unit = "relative")
#> Unit: relative
#>            expr   min     lq  mean median    uq   max neval
#>     cbRcppAlgos 1.106 0.9183 1.200  1.030 1.063 1.701   100
#>        cbGtools 2.426 2.1815 2.068  1.996 2.127 1.367   100
#>  cbArrangements 1.000 1.0000 1.000  1.000 1.000 1.000   100

Ce n'est pas une faute de frappe ... gtools::permutations Est presque aussi rapide que les autres fonctions compilées. J'encourage le lecteur à consulter le code source de gtools::permutations Car il s'agit de l'un des affichages de programmation les plus élégants (R ou autre).

4. Multisets

Tout d'abord, nous examinons les combinaisons de multisets.

  1. RcppAlgos
  2. arrangements

Pour trouver des combinaisons/permutations de multisets, avec RcppAlgos utilisez les arguments freqs pour spécifier combien de fois chaque élément du vecteur source, v, est répété.

library(RcppAlgos)
library(arrangements)
library(microbenchmark)
options(digits = 4)
set.seed(496)
myFreqs <- sample(1:5, 10, replace = TRUE)
## This is how many times each element will be repeated
myFreqs
#>  [1] 2 4 4 5 3 2 2 2 3 4
testVector4 <- as.integer(c(1, 2, 3, 5, 8, 13, 21, 34, 55, 89))
t1 <- comboGeneral(testVector4, 12, freqs = myFreqs)
t3 <- combinations(freq = myFreqs, k = 12, x = testVector4)
identical(t1, t3)
#> [1] TRUE

Référence:

microbenchmark(cbRcppAlgos = comboGeneral(testVector4, 12, freqs = myFreqs),
               cbArrangements = combinations(freq = myFreqs, k = 12, x = testVector4),
               unit = "relative")
#> Unit: relative
#>            expr   min    lq  mean median    uq   max neval
#>     cbRcppAlgos 1.000 1.000 1.000  1.000 1.000 1.000   100
#>  cbArrangements 1.254 1.221 1.287  1.259 1.413 1.173   100

Pour les permutations de multi-ensembles choisis m à la fois, nous avons:

  1. RcppAlgos
  2. arrangements

Comment:

library(RcppAlgos)
library(arrangements)
library(microbenchmark)
options(digits = 4)
set.seed(8128)
myFreqs <- sample(1:3, 5, replace = TRUE)
testVector5 <- sort(runif(5))
myFreqs
#> [1] 2 2 2 1 3
t1 <- permuteGeneral(testVector5, 7, freqs = myFreqs)
t3 <- permutations(freq = myFreqs, k = 7, x = testVector5)
identical(t1, t3)
#> [1] TRUE

Référence:

microbenchmark(cbRcppAlgos = permuteGeneral(testVector5, 7, freqs = myFreqs),
               cbArrangements = permutations(freq = myFreqs, k = 7, x = testVector5),
               unit = "relative")
#> Unit: relative
#>            expr   min    lq  mean median    uq   max neval
#>     cbRcppAlgos 1.461 1.327 1.282  1.177 1.176 1.101   100
#>  cbArrangements 1.000 1.000 1.000  1.000 1.000 1.000   100

Pour les permutations de multi-ensembles renvoyant toutes les permutations, nous avons:

  1. RcppAlgos
  2. multicool
  3. arrangements

Comment:

library(RcppAlgos)
library(arrangements)
library(microbenchmark)
options(digits = 4)
set.seed(8128)
myFreqs2 <- c(2,1,2,1,2)
testVector6 <- (1:5)^3
## For multicool, you must have the elements explicitly repeated
testVector6Prime <- rep(testVector6, times = myFreqs2)
t3 <- multicool::allPerm(multicool::initMC(testVector6Prime))

## for comparison
t1 <- permuteGeneral(testVector6, freqs = myFreqs2)
identical(t1[do.call(order,as.data.frame(t1)),],
          t3[do.call(order,as.data.frame(t3)),])
#> [1] TRUE

Référence:

microbenchmark(cbRcppAlgos = permuteGeneral(testVector6, freqs = myFreqs2),
               cbMulticool = multicool::allPerm(multicool::initMC(testVector6Prime)),
               cbArrangements = permutations(freq = myFreqs2, x = testVector6),
               unit = "relative")
#> Unit: relative
#>            expr      min       lq    mean   median      uq     max neval
#>     cbRcppAlgos    1.276    1.374   1.119    1.461    1.39  0.8856   100
#>     cbMulticool 2434.652 2135.862 855.946 2026.256 1521.74 31.0651   100
#>  cbArrangements    1.000    1.000   1.000    1.000    1.00  1.0000   100

5. Résumé

gtools et combinat sont des packages bien établis pour réorganiser les éléments d'un vecteur. Avec gtools, il y a quelques autres options (voir l'aperçu ci-dessus) et avec combinat, vous pouvez réorganiser factors. Avec multicool, on est capable de réorganiser les multisets. Bien que partitions et gRbase soient limités pour les besoins de cette question, ce sont des puissances remplies de fonctions très efficaces pour traiter respectivement les partitions et les objets de tableau.

arrangements

  1. La sortie est dans l'ordre du dictionnaire.
  2. Permet à l'utilisateur de spécifier le format via l'argument layout (r = row-major, c = column-major Et l = list).
  3. Offre des méthodes pratiques telles que collect & getnext lorsque vous travaillez avec des itérateurs.
  4. Permet la génération de plus de 2^31 - 1 Combinaisons/permutations via getnext. N.B. RcppAlgos (via lower/upper voir ci-dessous) et multicool (via nextPerm) sont également capables de le faire.
  5. En parlant de getnext, cette fonction permet un nombre spécifique de résultats en utilisant l'argument d.
  6. Prend en charge les grands entiers de gmp pour calculer le nombre de combinaisons/permutations.

Observer:

library(arrangements)
icomb <- icombinations(1000, 7)
icomb$getnext(d = 5)
#>      [,1] [,2] [,3] [,4] [,5] [,6] [,7]
#> [1,]    1    2    3    4    5    6    7
#> [2,]    1    2    3    4    5    6    8
#> [3,]    1    2    3    4    5    6    9
#> [4,]    1    2    3    4    5    6   10
#> [5,]    1    2    3    4    5    6   11

Cette fonctionnalité est vraiment sympa lorsque vous ne voulez que quelques combinaisons/permutations. Avec les méthodes traditionnelles, vous devrez générer toutes les combinaisons/permutations puis sous-ensemble. Cela rendrait l'exemple précédent impossible car il y a plus de 10^17 Résultats (c'est-à-dire ncombinations(1000, 7, bigz = TRUE) = 194280608456793000).

Cette fonctionnalité ainsi que les améliorations apportées aux générateurs dans arrangements, lui permettent d'être très efficace en termes de mémoire.

RcppAlgos

  1. La sortie est dans l'ordre du dictionnaire.
  2. Il existe des fonctionnalités de contrainte pratiques que nous ne discuterons pas ici car elles sont hors sujet pour cette question. Je noterai seulement que les types de problèmes qui peuvent être résolus en utilisant ces fonctionnalités ont été la motivation pour créer ce package.
  3. Il existe un argument upper (formellement rowCap) qui est analogue à l'argument d de getnext.

Observer:

library(RcppAlgos)
comboGeneral(1000, 7, upper = 5)
#>      [,1] [,2] [,3] [,4] [,5] [,6] [,7]
#> [1,]    1    2    3    4    5    6    7
#> [2,]    1    2    3    4    5    6    8
#> [3,]    1    2    3    4    5    6    9
#> [4,]    1    2    3    4    5    6   10
#> [5,]    1    2    3    4    5    6   11
  1. De plus, à partir de 2.0.0, Il existe un argument appelé lower qui permet de démarrer la génération à une combinaison/permutation spécifique. Cela se met bien en place pour la parallélisation et permet une génération rapide au-delà de 2^31 - 1 Car les morceaux sont générés indépendamment.

Exemple parallèle avec plus de 6 milliards de combinaisons:

system.time(parallel::mclapply(seq(1,6397478649,4390857), function(x) {
        a <- comboGeneral(25, 15, freqs = c(rep(1:5, 5)), lower = x, upper = x + 4390856)
        ## do something
        x
    }, mc.cores = 7))
#>     user  system elapsed 
#>  510.623 140.970 109.496

Au cas où vous vous demanderiez comment chaque package évolue, je vous laisse avec cet exemple final qui mesure la vitesse à laquelle chaque package peut générer plus de 100 millions de résultats (NB gtools::combinations Est laissé ici car il générera l'erreur: evaluation nested too deeply...). En outre, nous appelons explicitement combn à partir du package utils car je n'ai pas pu obtenir une exécution réussie à partir de combinat::combn. Les différences d'utilisation de la mémoire entre ces deux sont assez bizarres étant donné qu'elles ne sont que légèrement différentes (voir ?utils::combn Dans la section "Auteurs").

Observer:

library(RcppAlgos)
library(arrangements)
library(microbenchmark)
options(digits = 4)
set.seed(2187)
testVector7 <- sort(sample(10^7, 10^3))
system.time(utils::combn(testVector7, 3))
#>    user  system elapsed 
#> 179.956   5.687 187.159
system.time(RcppAlgos::comboGeneral(testVector7, 3))
#>    user  system elapsed 
#>   1.136   0.758   1.937
system.time(arrangements::combinations(x = testVector7, k = 3))
#>    user  system elapsed 
#>   1.963   0.930   2.910
system.time(RcppAlgos::permuteGeneral(testVector7[1:500], 3))
#>    user  system elapsed 
#>   1.095   0.631   1.738
system.time(arrangements::permutations(x = testVector7[1:500], k = 3))
#>    user  system elapsed 
#>   1.399   0.584   1.993

6. Mémoire

Lors de l'exécution de comboGeneral ainsi que arrangements::combinations, La mémoire sautera de près de 2 Go avant d'appeler gc. Cela semble à peu près correct comme #rows * #nols * bytesPerCell / 2^30 bytes = choose(1000,3) * 3 * 4 / 2^30 bytes = (166167000 * 3 * 4)/2^30 = 1.857 Gbs). Cependant, lors de l'exécution de combn, le comportement de la mémoire était effacé (par exemple, il utilisait parfois les 16 Go de mémoire et d'autres fois, il ne dépassait que quelques Go). Lorsque je testais cela sur la configuration Windows, cela plantait souvent.

Nous pouvons le confirmer en utilisant Rprof avec summaryRporf. Observer:

Rprof("RcppAlgos.out", memory.profiling = TRUE)
t1 <- RcppAlgos::comboGeneral(testVector7, 3)
Rprof(NULL)
summaryRprof("RcppAlgos.out", memory = "both")$by.total
                          total.time total.pct mem.total self.time self.pct
"CombinatoricsRcpp"              1.2       100    1901.6       1.2      100
"RcppAlgos::comboGeneral"        1.2       100    1901.6       0.0        0

Rprof("arrangements.out", memory.profiling = TRUE)
t3 <- arrangements::combinations(10^3, 3, testVector7)
Rprof(NULL)
summaryRprof("arrangements.out", memory = "both")$by.total
                             total.time total.pct mem.total self.time self.pct
".Call"                            2.08     99.05    1901.6      2.08    99.05

Avec RcppAlgos & arrangements, mem.total Enregistre un peu plus de 1900 Mb.

Et voici le profil de mémoire sur un vecteur plus petit comparant gtools, utils et combinat.

testVector7Prime <- testVector7[1:300]

Rprof("combinat.out", memory.profiling = TRUE)
t3 <- combinat::combn(testVector7Prime, 3)
Rprof(NULL)
summaryRprof("combinat.out", memory = "both")$by.total
                  total.time total.pct mem.total self.time self.pct
"combinat::combn"       3.98    100.00    1226.9      3.72    93.47

Rprof("utils.out", memory.profiling = TRUE)
t4 <- utils::combn(testVector7Prime, 3)
Rprof(NULL)
summaryRprof("utils.out", memory = "both")$by.total
               total.time total.pct mem.total self.time self.pct
"utils::combn"       2.52    100.00    1952.7      2.50    99.21

Rprof("gtools.out", memory.profiling = TRUE)
t5 <- gtools::combinations(300, 3, testVector7Prime)
Rprof(NULL)
summaryRprof("gtools.out", memory = "both")$by.total
                      total.time total.pct mem.total self.time self.pct
"rbind"                     4.94     95.00    6741.6      4.40    84.62

Il est intéressant de noter que utils::combn Et combinat::combn Utilisent différentes quantités de mémoire et prennent différents temps pour s'exécuter. Cela ne tient pas avec des vecteurs plus petits:

microbenchmark(combinat::combn(2:13, 6), utils::combn(2:13, 6))
Unit: microseconds
                    expr     min      lq     mean  median       uq      max neval
combinat::combn(2:13, 6) 527.378 567.946 629.1268 577.163 604.3270 1816.744   100
   utils::combn(2:13, 6) 663.150 712.872 750.8008 725.716 771.1345 1205.697   100

Et avec gtools la mémoire totale utilisée est un peu plus de 3x autant que utils. Il convient de noter que pour ces 3 packages, j'obtenais des résultats différents à chaque fois que je les exécutais (par exemple pour combinat::combn Parfois j'obtiendrais 9000 Mb puis j'obtiendrais 13000 Mb).

Pourtant, aucun ne peut correspondre à RcppAlgos [~ # ~] ou [~ # ~] arrangements. Les deux n'utilisent que 51 Mo lorsqu'ils sont exécutés sur l'exemple ci-dessus.

script de référence: --- (https://Gist.github.com/randy3k/bd5730a6d70101c7471f4ae6f453862e (rendu par https://github.com/tidyverse/reprex )

*: Un hommage à Une promenade à travers la combinatoire par Miklós Bóna

17
Joseph Wood