Utilisation de la séquence de de Bruijn pour trouver le

11

Sean Anderson a publié des hacks twiddling contenant l'algorithme d'Eric Cole pour trouver le log2v d'un entier à N bits v dans les opérations O(lg(N)) avec multiplication et recherche.

L'algorithme repose sur un nombre "magique" de la séquence de De Bruijn. Quelqu'un peut-il expliquer les propriétés mathématiques fondamentales de la séquence utilisée ici?

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];
Yury Bayda
la source
2
L'idée vient de cet article supertech.csail.mit.edu/papers/debruijn.pdf . Une séquence de Brujn de taille est un moyen de représenter toutes les chaînes de bits de taille k de façon très concise: chaque chaîne possible apparaît exactement une fois comme sous-séquence contiguë. Donc, si vous décalez la séquence de Bruijn de n 2 k bits et lisez les k derniers bits, vous avez un identifiant unique pour n . 2kkn2kkn
Sasho Nikolov
1
Soit dit en passant, cela ne calcule que ; et comme écrit, cela ne fonctionne que pour les entiers 32 bits. Journal2v
Sasho Nikolov
1
@Sasho Se transformer en réponse?
Yuval Filmus
@SashoNikolov Merci, a ajouté une fonction de plafond à la question
Yury Bayda

Réponses:

9

Notez d'abord que cet algorithme ne calcule que et que le code est écrit, il ne fonctionne que pour v qui tient dans un mot de 32 bits.Journal2vv32

La séquence de décalages et de ou-s qui apparaît en premier a pour fonction de propager le bit 1 de tête de jusqu'au bit le moins significatif. Numériquement, cela vous donne 2 log 2 v - 1 .v2Journal2v-1

La partie intéressante est l'astuce de Bruijn, qui provient de cet article de Leiserson, Prokop et Randall (apparemment, les professeurs du MIT passent du temps à faire des hacks bit :)). Ce que vous devez savoir sur les séquences de Bruijn, c'est qu'elles représentent toutes les séquences possibles d'une longueur donnée d'une manière aussi compressée que possible. Précisément, une séquence de De Brujn sur l'alphabet est une chaîne binaire s de longueur 2 k telle que chaque chaîne binaire de longueur k apparaît exactement une fois comme une sous-chaîne contiguë (le bouclage est autorisé). La raison pour laquelle cela est utile est que si vous avez un nombre X{0,1}s2kkXdont la représentation binaire est une séquence de Bruijn (complétée par zéros), alors les k premiers bits de 2 i X identifient de manière unique i (tant que i < k ).kk2jeXjeje<k

Sasho Nikolov
la source
3
Notez que vous pouvez utiliser n'importe quelle séquence de Bruijn de cette manière pour calculer étant donnéje . Cependant, vous ne pouvez pas utiliser une séquence de Bruijn arbitraire pour calculer i étant donné 2 i - 1 . Ici, 0x07C4ACDD = 00000111110001001010110011011101 semble être une séquence de Bruijn avec quelques propriétés supplémentaires, grâce auxquelles le supplémentaire - 1 ne ruine pas cette approche. 2jeje2je-1-1
Jukka Suomela
Merci @JukkaSuomela, j'étais un peu confus à ce sujet. Je suppose que vous pouvez toujours simplement ajouter 1 à . v
Sasho Nikolov
5

Quelques commentaires (pas vraiment une réponse). Classons les entiers 32 bits comme suit:c

  • Le type X: (en tant que chaîne binaire) est une séquence de De Bruijn (pour toutes les rotations, les bits [27,31] sont distincts). Un exemple:c

    11111011100110101100010100100000
    
  • Type Y: bits [27,31] de sont distincts pour i = 0 , 1 , . . . , 31 . C'est ce que Leiserson et al. les usages. Exemples:2jecje=0,1,...,31

    00000100011001010011101011011111
    00001111101110011010110001010010
    
  • Type Z: bits [27,31] de sont distincts pouri=0,1,. . . ,31. C'est ce dont nous avons besoin dans la question initiale. Exemples:(2je+1-1)cje=0,1,...,31

    00000111110001001010110011011101  (07C4ACDD)
    10000111110001001010110011011101
    01111000001110110101001100100011
    11111000001110110101001100100011
    

Quelques observations basées sur des expériences rapides (j'espère avoir bien compris):

  1. Il y a 65536 entiers de type X.

  2. Il y a 4096 entiers de type X + Y. Ce sont précisément ces entiers de type X qui commencent par la séquence '0000 ...'

    • intuition: avec des zéros non significatifs, rotation = décalage?
  3. Il y a 256 entiers de type X + Y + Z. Ce sont précisément ces entiers de type X qui commencent par la séquence '0000011111 ...'

    • intuition: ??
  4. Tous les entiers de type Y sont également de type X.

  5. Cependant, il existe également 768 entiers de type Z qui ne sont ni de type X ni de type Y. Ils commencent par «1000011111 ...», «0111100000 ...» ou «1111100000 ...».

Jukka Suomela
la source
1
C'est la seule réponse qui explique pourquoi la multiplication de De Bruijn par 2 ^ n-1 fonctionne, par opposition à 2 ^ n, qui n'est qu'un changement. Je serais ravi que quelqu'un puisse développer "l'intuition" de # 3 ci-dessus. Comment Eric Cole en est-il arrivé là? Essai et erreur? Ou une certaine compréhension de ce qui arrive réellement aux bits lorsque vous multipliez par 2 ^ n-1?
FarmerBob
1
  • D'où vient cette constante?

Citant: "Le 10 décembre 2009, Mark Dickinson a réduit quelques opérations en exigeant que v soit arrondi à un de moins que la puissance de 2 suivante plutôt que la puissance de 2". [graphics.stanford.edu/~seander/bithacks.html]

Cette constante particulière est une séquence de De Bruijn avec alphabet binaire mais avec une propriété supplémentaire. Je vais l'appeler la «propriété Marc Dickinson» car l'algorithme original pourrait être implémenté sans ces séquences DB spéciales. En ajoutant 2 opérations supplémentaires, nous pourrions utiliser n'importe quelle séquence DB ordinaire. Opération: v ^ = (v >> 1); // clr tous les bits sauf MSB mis après la cascade ou le décalage.

  • Résultats (bruteforce)

Seq.Type | Nombre d'entiers | Non. DBSeq. avec | sans rotations | avec Dickinson propriété
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 propriété spéciale

L'opération ( ( 2 k -0X7C4UNEC 2k1(mod232)32k1entrez la description de l'image ici2k1 )

  • Séquences binaires de Bruijn les plus petites lexicographiquement avec la propriété Dickinson

    [B (2,3): 0x1D] [B (2,4): 0x0F2D] [B (2,5): 0x7C4ACDD] [B (2,6): recherche en cours]

Si vous espériez une formule mathématique élégante pour les décrire ou un théorème pour les produire ou quelque chose de similaire, je pense que cela nécessiterait un aperçu approfondi de la théorie des nombres et éventuellement d'autres domaines qui dépassent mes compétences. Si je devais deviner, je parierais qu'ils pourraient être produits par des automates cellulaires. Ce n'est pas une réponse pourquoi? sur une base rigoureuse mais une tentative de comprendre intuitivement pourquoi cela fonctionne et pourquoi cela fonctionne correctement, afin que vous puissiez l'utiliser en toute confiance.

PS Je n'ai pas couvert la construction LUT, qui se déduit facilement si vous comprenez les principes de fonctionnement des algorithmes.

FranG
la source
Enfin trouvé: B (2,6) 0x3f08a4c6acb9dbd - une séquence de bruijn 64 bits avec la «propriété dickinson». J'ai trouvé au moins 122K de telles séquences.
2017