Szybkie indeksowanie kombinacji k

12

Powracam do starego problemu, nad którym pracowałem jakiś czas temu.

Typowy scenariusz to „3 bity są ustawione w 8-bitowej liczbie całkowitej”, tj. 00000111.

Wszystkie unikalne kombinacje z 3 bitami zestawu można łatwo wygenerować (w kolejności) za pomocą zagnieżdżonych pętli. Interesuje mnie kombinacja indeksu mapowania <->, tzn. „00001011” byłby drugą kombinacją (lub wartością „1” w indeksie zerowym).

Do tej pory przeglądałem wszystkie kombinacje i zapisywałem je w tabeli, dzięki czemu indeks wyszukiwania -> konwersacja to operacja O (1). Drugi kierunek to O (ln (n)) z wyszukiwaniem dwusiecznym.

Minusem jest jednak to, że to oczywiście obciąża pamięć, jeśli zwiększymy domenę, do tego stopnia, że ​​nie jest to możliwe.

Jaki byłby prosty sposób obliczenia n-tej kombinacji lub indeksu dla danej kombinacji? Kolejność kombinacji byłaby dobra, ale nie jest obowiązkowa.

Eiko
źródło
@MichaelT Twoje linki nie uwzględniają pytania - powtarzanie kombinacji nie jest problemem. Chodzi o mapowanie indeksów i kombinacji. Biorąc pod uwagę „11001000”, jaki jest indeks (lub liczba wyliczeń, jeśli chcesz)? Jaki kod należy do indeksu 1673?
Eiko
1
Ahh, w takim przypadku może okazać się przydatny OEIS. Na przykład sekwencja 3,5, 6, 9, 10, 12, 17, 18 daje nam sumę dwóch odrębnych potęg dwóch, co jest innym sposobem na powiedzenie „dwóch bitów” w żargonie matematycznym. Różne tam formuły pokazują różne sposoby obliczania n-tej wartości.
1
8-bitowe liczby całkowite mają tylko 256 kombinacji dowolnych wzorców bitów, które są trywialne do przechowywania (i zajmowałyby mniej miejsca niż jakikolwiek sprytny kod). Jakie są twoje docelowe / szacunkowe liczby bitów?
9000
1
Jak wykopano gdzie indziej, jest to znane jako kombinatoryczny system liczb , a Hack Gospera może to zrobić w O (1). Logika została wykonana w HACKMEM 175 i jest wyjaśniona w tym poście na blogu ( oryginał jest dość zwięzły).

Odpowiedzi:

4

Generowanie n-tej kombinacji nazywa się algorytmem „nierankingowym”. Zauważ, że permutacje i kombinacje często można utożsamiać ze sposobem parametryzacji problemu. Nie wiedząc dokładnie, na czym polega problem, trudno jest zalecić dokładne właściwe podejście, aw rzeczywistości w przypadku większości problemów kombinatorycznych zwykle istnieje kilka różnych algorytmów rankingu / nierankingowania.

Jednym z dobrych zasobów są „Algorytmy kombinatoryczne” autorstwa Krehera i Stinsona. Ta książka ma wiele dobrych algorytmów rankingowych i nierankingowych. Istnieją bardziej zaawansowane zasoby, ale polecam Krehera jako punkt wyjścia. Jako przykład algorytmu nierankingowego rozważ następujące kwestie:

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

Jest to nierankingowa permutacja, ale jak wspomniano powyżej, w wielu przypadkach można przekształcić kombinację nierankingową w równoważny problem permutacyjny.

Tyler Durden
źródło