K kombinasyonlarının hızlı indekslenmesi


12

Bir süre önce üzerinde çalıştığım eski bir sorunu tekrar gözden geçiriyorum.

Tipik bir senaryo, "3 bit 8 bitlik bir tamsayı içinde ayarlanır", yani 00000111'dir.

3 set bitli tüm benzersiz kombinasyonlar, iç içe döngülerle kolayca (sırayla) oluşturulabilir. İlgilendiğim şey, eşleme dizini <-> birleşimidir, yani "00001011" ikinci bileşim (veya sıfır tabanlı bir dizinde "1" değeri) olacaktır.

Şimdiye kadar, tüm kombinasyonları çalıştırdım ve bir tabloda sakladım, arama dizini -> görüşmeyi O (1) işlemi haline getirdim. Diğer yön ikiye ayırma ile O (ln (n)) 'dir.

Bununla birlikte, olumsuz, etki alanını arttırırsak, mümkün olmadığı bir noktaya kadar bellekte ağır olması.

Belirli bir kombinasyon için n.th kombinasyonunu veya dizini hesaplamanın basit bir yolu ne olabilir? Kombinasyonların sırası iyi olurdu, ancak zorunlu değildir.



@MichaelT Bağlantılarınız soruyu ele almıyor - kombinasyonlar üzerinde yineleme yapmak sorun değil. Bu, indeksleri ve kombinasyonları haritalamakla ilgilidir. "11001000" verildiğinde, dizin nedir (veya numaralandırma sayısı)? Hangi kod dizin 1673'e aittir?
Eiko

1
Ahh, bu durumda OEIS'i faydalı bulabilirsiniz. Örneğin, 3,5,6,9,10,12,17,18 dizisi, bize iki farklı gücün toplamını verir, bu da matematik jargonda "iki bit" demenin başka bir yoludur. Buradaki çeşitli formüller, n'inci değeri hesaplamanın çeşitli yollarını gösterir.

1
8 bitlik tamsayılar, saklanması önemsiz olan (ve herhangi bir akıllı koddan daha az yer kaplayan) herhangi bir bit deseninin sadece 256 kombinasyonuna sahiptir. Hedef / tahmini bit sayımlarınız nelerdir?
9000

1
Başka bir yerde kazıldıkça, bu bir kombinatoryal sayı sistemi olarak bilinir ve Gosper'ın Hack'i O (1) 'de yapabilir. Mantık HACKMEM 175'te yapıldı ve bu blog yazısında açıklandı ( orijinal oldukça kısa).

Yanıtlar:


4

N'inci kombinasyonun oluşturulmasına "çözülmeyen" algoritma denir. Permütasyonların ve kombinasyonların genellikle problemin parametrelendirilme yöntemiyle denkleştirilebileceğini unutmayın. Sorunun tam olarak ne olduğunu bilmeden, kesin doğru yaklaşımı önermek zordur ve aslında çoğu kombinatorik problem için genellikle birkaç farklı sıralama / çözme algoritması mümkündür.

İyi bir kaynak Kreher ve Stinson tarafından "Kombinatoryal Algoritmalar" dır. Bu kitap açık bir şekilde çok iyi sıralama ve unranking algoritmalarına sahiptir. Daha gelişmiş kaynaklar var, ancak Kreher'i bir başlangıç ​​noktası olarak tavsiye ederim. Hatalı bir algoritma örneği olarak aşağıdakileri göz önünde bulundurun:

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

Bu permütasyon unranking'idir, ancak yukarıda belirtildiği gibi, birçok durumda unranking'in bir kombinasyonunu eşdeğer permütasyon problemine dönüştürebilirsiniz.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.