Indexation rapide des k-combinaisons

12

Je revisite un vieux problème sur lequel je travaillais il y a quelque temps.

Un scénario typique est «3 bits sont définis dans un entier de 8 bits», c'est-à-dire 00000111.

Toutes les combinaisons uniques avec 3 bits définis peuvent facilement être générées (dans l'ordre) par des boucles imbriquées. Ce qui m'intéresse, c'est la combinaison d'index de mappage <->, c'est-à-dire que "00001011" serait la deuxième combinaison (ou la valeur "1" dans un index de base zéro).

Jusqu'à présent, j'ai parcouru toutes les combinaisons et les ai stockées dans une table, faisant de l'index de recherche -> conversation une opération O (1). L'autre direction est O (ln (n)) avec recherche de bissecte.

L'inconvénient, cependant, est que cela est évidemment lourd sur la mémoire si nous augmentons le domaine, jusqu'à un point où ce n'est pas possible.

Quelle serait une manière simple de calculer la nième combinaison ou l'indice d'une combinaison donnée? L'ordre des combinaisons serait bien, mais n'est pas obligatoire.

Eiko
la source
@MichaelT Vos liens ne répondent pas à la question - itérer sur les combinaisons n'est pas le problème. Il s'agit de cartographier des indices et des combinaisons. Étant donné "11001000", quel est l'indice (ou le dénombrement si vous voulez)? Quel code appartient à l'index 1673?
Eiko
1
Ahh, dans ce cas, vous pourriez trouver OEIS utile. Par exemple, la séquence 3,5,6,9,10,12,17,18 nous donne la somme de deux puissances distinctes de deux, ce qui est une autre façon de dire "deux bits sur" dans le jargon mathématique. Les différentes formules y montrent différentes manières de calculer la nième valeur.
1
Les entiers 8 bits n'ont que 256 combinaisons de n'importe quel bit bit qui sont triviaux à stocker (et prendraient moins d'espace que tout code intelligent). Quel est votre nombre de bits cible / estimé?
9000
1
Comme creusé ailleurs, cela est connu comme un système de nombres combinatoires , et le piratage de Gosper peut le faire dans O (1). La logique a été faite dans HACKMEM 175 et est expliquée dans ce billet de blog (l' original est assez concis).

Réponses:

4

La génération de la n-ième combinaison est appelée un algorithme "non classé". Notez que les permutations et les combinaisons peuvent souvent être assimilées par la façon dont le problème est paramétré. Sans savoir exactement quel est le problème, il est difficile de recommander la bonne approche exacte, et en fait, pour la plupart des problèmes combinatoires, il existe généralement plusieurs algorithmes de classement / classement différents.

Une bonne ressource est "Algorithmes combinatoires" de Kreher et Stinson. Ce livre a de nombreux algorithmes de bon classement et non classés clairement expliqués. Il existe des ressources plus avancées, mais je recommanderais Kreher comme point de départ. À titre d'exemple d'algorithme non classé, considérez ce qui suit:

/** PKSUL : permutation given its rank, the slots and the total number of items
 *  A combinatorial ranking is number of the permutation when sorted in lexicographical order
 *  Example:  given the set { 1, 2, 3, 4 } the ctItems is 4, if the slot count is 3 we have:
 *     1: 123    7: 213   13: 312   19: 412
 *     2: 124    8: 214   14: 314   20: 413
 *     3: 132    9: 231   15: 321   21: 421
 *     4: 134   10: 234   16: 324   22: 423
 *     5: 142   11: 241   17: 341   23: 431
 *     6: 143   12: 243   18: 342   24: 432
 *  From this we can see that the rank of { 2, 4, 1 } is 11, for example. To unrank the value of 11:
 *       unrank( 11 ) = { 11 % (slots - digit_place)!, unrank( remainder ) }
 * @param rank           the one-based rank of the permutation
 * @param ctItems        the total number of items in the set
 * @param ctSlots        the number of slots into which the permuations are generated
 * @param zOneBased      whether the permutation array is one-based or zero-based
 * @return               zero- or one-based array containing the permutation out of the set { ctItems, 1,...,ctItems }
 */
public static int[] pksul( final int rank, final int ctItems, final int ctSlots, boolean zOneBased ){
    if( ctSlots <= 0 || ctItems <= 0 || rank <= 0 ) return null;
    long iFactorial = factorial_long( ctItems - 1 ) / factorial_long( ctItems - ctSlots );
    int lenPermutation = zOneBased ? ctSlots + 1 : ctSlots;
    int[] permutation = new int[ lenPermutation ];
    int[] listItemsRemaining = new int[ ctItems + 1 ];
    for( int xItem = 1; xItem <= ctItems; xItem++ ) listItemsRemaining[xItem] = xItem; 
    int iRemainder = rank - 1;
    int xSlot = 1;
    while( true ){
        int iOrder = (int)( iRemainder / iFactorial ) + 1;
        iRemainder = (int)( iRemainder % iFactorial );
        int iPlaceValue = listItemsRemaining[ iOrder ];
        if( zOneBased ){
            permutation[xSlot] = iPlaceValue;
        } else {
            permutation[xSlot - 1] = iPlaceValue;
        }
        for( int xItem = iOrder; xItem < ctItems; xItem++ ) listItemsRemaining[xItem] = listItemsRemaining[xItem + 1]; // shift remaining items to the left
        if( xSlot == ctSlots ) break;
        iFactorial /= ( ctItems - xSlot );
        xSlot++;
    }
    if( zOneBased ) permutation[0] = ctSlots;
    return permutation;
}

Il s'agit d'une permutation sans classement, mais comme mentionné ci-dessus, dans de nombreux cas, vous pouvez convertir une combinaison sans classement en un problème de permutation équivalent.

Tyler Durden
la source