chess:programming:de_bruijn_sequence:8-bit_example
Table of Contents
Chess - Programming - de Bruijn Sequence - 8-bit Example
An 8-bit value has 8 digits.
- The position can be represented by 3 bits.
- Log2(8) = 3.
Using a de Bruijn sequence
int lsb_pos(std::uint8_t v) { // 1. Define a De Bruijn Bit Table. static constexpr int DeBruijnBitTable[8] = { 1 , 2 , 7 , 3 , 8 , 6 , 5 , 4 }; // 2. Get the LSB. std::uint8_t pop_lsb = (v & -v); // 3. Get which Index to use in the De Bruijn Bit Table. std::uint8_t hash = std::uint8_t (pop_lsb * 0x1DU) >> 5; // 4. Get the specific Table value. int r = DeBruijnBitTable[hash]; return r; }
NOTE: This shows 4 steps:
- Step 1: Describes a pre-built De Bruijn Bit Table.
- Step 2: Returns the LSB (Least Significant Bit) from the value being checked against.
- Step 3: Uses the LSB from Step 2 to determine which Index should be referred to from the De Bruijn Bit Table.
- Step 4: Uses the Index from Step 3 to return the actual hash value.
The big question is where do the following come from:
- »5: As an 8-bit number can fit into 3-bits, we want to remove the other 5 bits not needed 11111111 » 101 = 111.
- This leaves the most significant 3 bits.
- 0x1D: This value has to be calculated through trial-and-error.
- In binary is 00011101. The de Bruijn sequence.
- Using the binary number and look at it 3 characters at a time, it contains all the sequence numbers with are no duplicates:
* 000 001 011 111 110 101 010 100
- The last code requires circulating to the first bit.
The de Bruijn sequence of this 8-bit value
bit | pop_lsb | pop_lsb * 0x1D | hash | index | index order | final order |
---|---|---|---|---|---|---|
(1st 3 bits) | (index + 1) | (bit at index order position) | ||||
1 | 1 | 0001 1101 | 000 | 0 | 1 | 1 |
2 | 2 | 0011 1010 | 001 | 1 | 2 | 2 |
3 | 4 | 0111 0100 | 011 | 3 | 4 | 3 |
4 | 8 | 1110 1000 | 111 | 7 | 8 | 7 |
5 | 16 | 1101 0000 | 110 | 6 | 7 | 6 |
6 | 32 | 1010 0000 | 101 | 5 | 6 | 5 |
7 | 64 | 0100 0000 | 010 | 2 | 3 | 7 |
8 | 128 | 1000 0000 | 100 | 4 | 5 | 4 |
NOTE: This is a complete hash in which a value with only one bit set is nicely pushed into a 3-bit number.
- Using the table above with the index, will return the value of the original pop_lsb.
- hash: Uses the first 3 bits from the previous step, pop_lsb * 0x1D.
- index: The number represented by the hash.
- index order: The sequence of the numbers.
- final order: The bit at the position of the index order.
- Prepare an array with the corresponding number of digits at this position.
constexpr int table [8] = { 1 , 2 , 7 , 3 , 8 , 6 , 5 , 4 };
- This table is made by using the index order to determine the next number from the final order columns.
- The de Bruijn sequence must start with the first digit, the 0 element.
- This is to prevent overflow and to allow the whole to circulate naturally during multiplication (left shift operation).
Explaining the usage of this de Bruijn sequence
constexpr int table [8] = { 1 , 2 , 7 , 3 , 8 , 6 , 5 , 4 };
The c++ code above performs the following function.
- It takes a number from 1 to 8 to lookup.
- It multiplies this number by 0x1D, which is 29 Decimal. This is the Magic Number.
- It right-shifts this by 5 to determine the specific bit position of that lookup value.
Lookup | Lookup multiple by the magic number | Right-shifted by the offset |
---|---|---|
(29 in this example) | (5 in this example) | |
1 | 29 | 0 |
2 | 58 | 1 |
7 | 203 | 6 |
3 | 87 | 2 |
8 | 232 | 7 |
6 | 174 | 5 |
5 | 145 | 4 |
4 | 116 | 3 |
NOTE: This shows that:
- A lookup of 1 returns 0, which is the first bit.
- A lookup of 2 returns 1, which is the second bit.
- A lookup of 3 returns 2, which is the third bit.
- …and so on.
Every lookup returns a unique and distinct value.
The Magic Number
NOTE: Here, the 1D magic number was used.
- However there may be other magic numbers that might also work.
- As long as a valid De Bruijn sequence is determined that uniquely provides the bit sequences this could be used instead.
Hash Function Calculation
hash(x) = (x ∗ dEBRUIJN) >> SHIFT
NOTE:
- N: is the width of the unsigned integer type.
- dEBRUIJN: is the appropriate de Bruijn sequence of length to exhaust the combination of characters being used, 0, 1.
- SHIFT: Can be calculated using this formula: (int)(N − Log2(N)).
- Using the 8-bit example:
- Log2(8) = 2.40823996531
- 8 - 2.40823996531 = 5.59176003469
- Round down to make an integer results in 5.
References
chess/programming/de_bruijn_sequence/8-bit_example.txt · Last modified: 2021/11/30 00:53 by peter