Indicizzazione rapida di combinazioni k


12

Sto rivisitando un vecchio problema a cui stavo lavorando qualche tempo fa.

Uno scenario tipico è "3 bit sono impostati all'interno di un numero intero a 8 bit", ovvero 00000111.

Tutte le combinazioni uniche con 3 bit impostati possono essere facilmente generate (in ordine) da loop nidificati. Quello che mi interessa è la combinazione dell'indice di mappatura <->, ovvero "00001011" sarebbe la seconda combinazione (o il valore "1" in un indice a base zero).

Finora ho analizzato tutte le combinazioni e le ho memorizzate in una tabella, rendendo l'indice di ricerca -> conversazione un'operazione O (1). L'altra direzione è O (ln (n)) con ricerca bisect.

Il rovescio della medaglia, tuttavia, è che questo ovviamente è pesante nella memoria se aumentiamo il dominio, fino a un punto in cui non è fattibile.

Quale sarebbe un modo semplice per calcolare l'ennesima combinazione o l'indice per una data combinazione? L'ordine delle combinazioni sarebbe bello, ma non è obbligatorio.



@MichaelT I tuoi link non rispondono alla domanda - iterare sulle combinazioni non è il problema. Si tratta di mappare indici e combinazioni. Dato "11001000", qual è l'indice (o il conteggio delle enumerazioni se vuoi)? Quale codice appartiene all'indice 1673?
Eiko,

1
Ah, in tal caso potresti trovare utile l'OEIS. Ad esempio, la sequenza 3,5,6,9,10,12,17,18 ci fornisce la somma di due distinti poteri di due, che è un altro modo di dire "due bit on" in gergo matematico. Le varie formule mostrano vari modi di calcolare l'ennesimo valore.

1
Gli interi a 8 bit hanno solo 256 combinazioni di schemi di bit che sono banali da memorizzare (e occuperebbero meno spazio di qualsiasi codice intelligente). Quali sono i conteggi target / stimati dei bit?
9000

1
Come scavato altrove, questo è noto come un sistema numerico combinatorio , e Gosper's Hack può farlo in O (1). La logica è stata fatta in HACKMEM 175 ed è spiegata in questo post del blog (l' originale è piuttosto conciso).

Risposte:


4

La generazione della n-esima combinazione è chiamata algoritmo "non classificabile". Si noti che le permutazioni e le combinazioni possono spesso essere equiparate al modo in cui il problema è parametrizzato. Senza sapere esattamente quale sia il problema, è difficile raccomandare l'esatto approccio corretto e, in effetti, per la maggior parte dei problemi combinatori di solito ci sono diversi algoritmi di classificazione / non classificazione possibili.

Una buona risorsa è "Algoritmi combinatori" di Kreher e Stinson. Questo libro ha molti buoni algoritmi di classificazione e non ordinamento spiegati chiaramente. Ci sono risorse più avanzate, ma consiglierei Kreher come punto di partenza. Come esempio di algoritmo non classificato, considerare quanto segue:

/** 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;
}

Questa è la suddivisione della permutazione, ma come menzionato sopra, in molti casi è possibile convertire una combinazione della suddivisione in un problema di permutazione equivalente.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.