神祕常量0x077CB531,德布萊英序列的恩賜

本文發佈於遊戲程序員劉宇的我的博客, 轉載請註明來源http://www.javashuo.com/article/p-dpbionnn-bq.htmlhtml

某天我在優化遊戲的算法,在將一個個關鍵數據結構優化所有成位操做後,最終來到最後一座大山前,如何快速計算出這個數值的二進制表示中最後一位的1在哪一位?程序員

首先,咱們已知:算法

將二進制只保留最後一位1的算法:

v & -v 的原理
已知IEEE對有符號整數中負數的定義是全部數值位取反+1,首位填1,首位這樣正負數加起來既能夠爲0。
例如:一個8位的整數 
A = 0001 1000, 取反 0110 0111, 取反加1 0110 1000,首位填1獲得  -A = 1110 1000
A + -A 正好加到最高一位進位後爲 0000 0000

由於取反的時候加1,因此A最後一個爲1的位取反後爲0,下面咱們稱爲第N位
取反後的第N位爲0,後面全爲1,再加1後的數值上第N位變成1,後面全爲0
此時A和-A裏,第N位以後的位全爲0,第N位以前的位全爲反
因此兩個數進行與操做,只有第N位爲1
即: 0001 1000 & 1110 1000 = 0000 1000

那麼,如何將v&-v轉換成N呢?編程

德布萊英序列

我看到了一段代碼:數組

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

計算過程能夠理解爲:數據結構

0x077CB531U的二進制:優化

00000111011111001011010100110001

乘以 v&-v,即左移N位,再右移27位,獲得的常數在MultiplyDeBruijnBitPosition裏查表,獲得的結果便是N。ui

例如乘以 100 0000,(6個0,左移6位)
 00000111011111001011010100110001
-> 11011111001011010100110001000000
再右移27位
-> 11011
獲得的數字是27,在數組裏是6

很神奇,不是嗎?spa

仔細分析一下這個數字,能夠發現,這個數字從每一位分別開始看,連續5位(到結尾循環),是全部5位的二進制數字的全集,並且左移28-31位時,結尾填0,正好序列開始的幾個數字也是0。.net

那麼不難理解,從這個數列的第X位任意取5位,均可以獲得一個0-31的數字,而且根據查表取出這個數字對應是左移過幾位。

 

爲何會存在這樣的序列

把二進制依次寫出,若是是兩位,咱們讓每一個兩位數字的最後一位等於下一個兩位數字的第一位, 00-01-11-10,寫出 0011,長度爲4。

三位,咱們讓每一個三個數字的後兩位等於下一個數字前兩位,001-011-111-110-101-010-000,寫出00111010,長度爲8。

四位,見圖:

 

 

 

依此類推,到第N位,咱們可讓每一個數的後N-1位等於下一個數字的前N-1位,獲得長度爲 2的N次方長度的2進制序列。

這就是德布萊英原理:必定存在長度爲2的N次方長度的二進制串,循環來看,一位位移動,能夠完整描述全部N位長度的二進制數字的集合。

連接1:https://en.wikipedia.org/wiki/De_Bruijn_sequence

連接2:https://baike.baidu.com/item/德布萊英序列/18898516?fr=aladdin

 

 咱們能夠任意生成這樣的序列嗎

稍微通過研究能夠發現,Debrujin序列是密碼學中運用很普遍的序列,已知原理,能夠編程來實現自動求序列的代碼。

1. 暴力遍歷

2. 遞歸法 https://blog.csdn.net/lusongno1/article/details/51104737

3. 本原多項式方法 https://blog.csdn.net/sea_sky_cloud/article/details/80932402

相關文章
相關標籤/搜索