Utilizzando la sequenza de Bruijn per trovare il di un intero


11

Sean Anderson ha pubblicato degli hack bit twiddling contenenti l'algoritmo di Eric Cole per trovare di un intero -bit nelle operazioni con moltiplicazione e ricerca.N v O ( lg ( N ) )log2vNvO(lg(N))

L'algoritmo si basa su un numero "magico" della sequenza di De Bruijn. Qualcuno può spiegare le proprietà matematiche fondamentali della sequenza usata qui?

uint32_t v; // find the log base 2 of 32-bit v
int r;      // result goes here

static const int MultiplyDeBruijnBitPosition[32] = 
{
  0, 9, 1, 10, 13, 21, 2, 29, 11, 14, 16, 18, 22, 25, 3, 30,
  8, 12, 20, 28, 15, 17, 24, 7, 19, 27, 23, 6, 26, 5, 4, 31
};

v |= v >> 1; // first round down to one less than a power of 2 
v |= v >> 2;
v |= v >> 4;
v |= v >> 8;
v |= v >> 16;

r = MultiplyDeBruijnBitPosition[(uint32_t)(v * 0x07C4ACDDU) >> 27];

2
L'idea nasce da questo documento supertech.csail.mit.edu/papers/debruijn.pdf . Una sequenza de Brujn di dimensione è un modo per rappresentare tutte le stringhe di bit di dimensione modo molto conciso: ogni possibile stringa appare esattamente una volta come sottosequenza contigua. Quindi, se si sposta la sequenza de Bruijn di bit e si leggono gli ultimi bit, si ha un identificatore univoco per . k n 2 k k n2KKn2KKn
Sasho Nikolov,

1
A proposito, questo calcola solo ; e come scritto funziona solo per numeri interi a 32 bit. log2v
Sasho Nikolov,

1
@Sasho Trasformati in una risposta?
Yuval Filmus,

@SashoNikolov Grazie, aggiunta una funzione soffitto alla domanda
Yury Bayda,

Risposte:


9

Prima nota che questo algoritmo calcola solo e, mentre il codice è scritto, funziona solo per v che si adatta a una parola a 32 bit.log2vv32

La sequenza di spostamenti e o-s che appare per prima ha la funzione di propagare il 1-bit iniziale di fino al bit meno significativo. Numericamente, questo ti dà 2 log 2 v - 1 .v2log2v-1

La parte interessante è il trucco di de Bruijn, che viene da questo articolo di Leiserson, Prokop e Randall (apparentemente i professori del MIT passano il tempo a fare piccoli hack :)). Quello che devi sapere sulle sequenze di De Bruijn è che rappresentano tutte le possibili sequenze di una determinata lunghezza in un modo il più compresso possibile. Precisamente, una sequenza di de Brujn sull'alfabeto è una stringa binaria s di lunghezza 2 k tale che ogni lunghezza k stringa binaria appare esattamente una volta come sottostringa contigua (è consentito l'avvolgimento). La ragione per cui è utile è che se hai un numero X{0,1}S2KKXla cui rappresentazione in bit è una sequenza de Bruijn (riempita con zeri), quindi i primi k bit di 2 i X identificano in modo univoco i (purché i < k ).KK2ioXioio<K


3
Si noti che è possibile utilizzare qualsiasi sequenza de Bruijn in questo modo per calcolare dati 2 i . Tuttavia, non è possibile utilizzare una sequenza de Bruijn arbitraria per calcolare i dato 2 i - 1 . Qui 0x07C4ACDD = 00000111110001001010110011011101 sembra essere una sequenza de Bruijn con alcune proprietà aggiuntive, grazie alla quale l'aggiunta - 1 non rovina questo approccio. io2ioio2io-1-1
Jukka Suomela,

Grazie @JukkaSuomela, ne ero un po 'confuso. Immagino che puoi sempre aggiungere solo 1 a . v
Sasho Nikolov,

5

Alcuni commenti (non proprio una risposta). Classifichiamo gli interi a 32 bit come segue:c

  • Tipo X: (come stringa binaria) è la sequenza di De Bruijn (per tutte le rotazioni, i bit [27,31] sono distinti). Un esempio:c

    11111011100110101100010100100000
    
  • Si Tipo: bit [27,31] di sono distinti per i = 0 , 1 , . . . , 31 . Questo è ciò che Leiserson et al. usi. Esempi:2ici=0,1,...,31

    00000100011001010011101011011111
    00001111101110011010110001010010
    
  • Tipo Z: bit [27,31] di sono distinti per i = 0 , 1 , . . . , 31 . Questo è ciò di cui abbiamo bisogno nella domanda originale. Esempi:(2io+1-1)cio=0,1,...,31

    00000111110001001010110011011101  (07C4ACDD)
    10000111110001001010110011011101
    01111000001110110101001100100011
    11111000001110110101001100100011
    

Alcune osservazioni basate su esperimenti rapidi (spero di aver capito bene):

  1. Esistono 65536 numeri interi di tipo X.

  2. Esistono 4096 numeri interi di tipo X + Y. Questi sono precisamente quei numeri interi di tipo X che iniziano con la sequenza '0000 ...'

    • intuizione: con zeri iniziali, rotazione = spostamento?
  3. Esistono 256 numeri interi di tipo X + Y + Z. Questi sono esattamente quei numeri interi di tipo X che iniziano con la sequenza '0000011111 ...'

    • intuizione: ??
  4. Tutti gli interi di tipo Y sono anche di tipo X.

  5. Tuttavia, ci sono anche 768 numeri interi di tipo Z che non sono né di tipo X né di tipo Y. Questi iniziano con '1000011111 ...', '0111100000 ...' o '1111100000 ...'


1
Questa è l'unica risposta che affronta il motivo per cui la moltiplicazione di De Bruijn per 2 ^ n-1 funziona, al contrario di 2 ^ n, che è solo uno spostamento. Mi piacerebbe se qualcuno potesse espandere l '"intuizione" del n. 3 sopra. Come ha fatto Eric Cole a inventarlo? Prova ed errore? O qualche comprensione di ciò che accade effettivamente ai bit quando si moltiplica per 2 ^ n-1?
FarmerBob,

1
  • Da dove viene questa costante?

Citando: "Il 10 dicembre 2009, Mark Dickinson ha eliminato un paio di operazioni richiedendo che v sia arrotondato per eccesso a uno in meno rispetto alla potenza successiva di 2 anziché alla potenza di 2". [Graphics.stanford.edu/~seander/bithacks.html]

Questa costante particolare è una sequenza di De Bruijn con alfabeto binario ma con una proprietà extra. La chiamerò "Marc Dickinson Property" poiché l'algoritmo originale potrebbe essere implementato senza queste sequenze DB speciali. Aggiungendo 2 operazioni extra potremmo usare qualsiasi sequenza DB ordinaria. Operazione: v ^ = (v >> 1); // clr tutti i bit tranne MSB impostato dopo il collegamento a cascata o-shift.

  • Risultati (bruteforce)

Seq.Type | No. Numeri interi | No. DBSeq. con | senza rotazioni | con proprietà Dickinson
B (2, 3) | 256 | 16 | 2 | 1
B (2, 4) | 64Ki | 256 | 16 | 4
B (2, 5) | 04Gi | 64Ki | 02Ki | 256
B (2, 6) | 16Ei | 04Gi | 64Mi | ??

  • La proprietà speciale

0X7C4UNCDD *2K1(mod232)32K1inserisci qui la descrizione dell'immagine2K1

  • Sequenze de Bruijn binarie lessicograficamente più piccole con Dickinson Property

    [B (2,3): 0x1D] [B (2,4): 0x0F2D] [B (2,5): 0x7C4ACDD] [B (2,6): ricerca ancora]

Se speravi in ​​un'elegante formula matematica per descriverli o teorema per produrli o qualcosa di simile, penso che ciò richiederebbe una profonda comprensione della teoria dei numeri e possibilmente altri campi che vanno oltre la mia competenza. Se dovessi fare un'ipotesi selvaggia, scommetterei che potrebbero essere prodotti da automi cellulari. Questa non è una risposta perché? su una base rigorosa, ma un tentativo di capire intuitivamente perché funziona e perché funziona correttamente, quindi puoi usarlo con fiducia.

PS Non ho coperto la costruzione di LUT, che è facilmente deducibile se si comprendono i principi di funzionamento degli algoritmi.


Finalmente trovato: B (2,6) 0x3f08a4c6acb9dbd - una sequenza di 64 bit de bruijn con la 'proprietà dickinson'. Ho trovato almeno 122K sequenze del genere.
Dal
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.