神奇的德布魯因序列

數學中存在這樣一個序列,它充滿魔力,在實際工程中也有一部分的應用。今天就打算分享一下這個序列,它在 Google S2 中是如何使用的以及它在圖論中,其餘領域中的應用。這個序列就是德布魯因序列 De Bruijn。html

一. 從一個魔術開始提及

有這樣一個撲克牌魔術。魔術師手上拿着一疊牌,給5我的(這裏的人數只能少於等於32,緣由稍後會解釋)分別檢查撲克牌,查看撲克牌的花色和點數是否都是不一樣的,即沒有相同的牌。python

檢查完撲克牌,沒有重複的牌之後,就能夠給這5我的洗牌了。讓這5我的任意的抽一疊牌從上面放到下面,即切牌。5我的輪流切完牌,牌的順序已經所有變化了。git

接着開始抽牌。魔術師讓最後一個切牌的人抽走這疊牌最上面的一張,依次給每一個人抽走最上面的一張。這時候抽走了5張牌。魔術師會說,「我已經看透了大家的心思,大家手上的牌我都知道了」。而後魔術師會讓拿黑色牌的人站起來(這一步很關鍵!)。而後魔術師會依次說出全部人手上的牌。最後每一個人翻出本身的牌,所有命中。全場歡呼。github

二. 魔術原理揭祕

在整個魔術中,有兩個地方比較關鍵。第一個是參與的人數只能少於等於32 。一副完整的撲克牌中,總共有54張牌,可是除去2張鬼牌(由於他們花色只有2種),總共就52張牌。golang

在上述魔術中,全部的牌都用二進制進行編碼,要想任意說出任意連續的5張牌,那麼必須這副牌具備全排列的特性。即枚舉全部種組合,而且每一個組合都惟一表明瞭一組排列。算法

若是窗口大小爲5,5張連續的撲克牌。二進制編碼 2^5^ = 32 ,因此須要32張牌。若是窗口大小爲6,6張連續的撲克牌,二進制編碼 2^6^ = 64,須要64張撲克牌。總共牌只有52張,因此不可能到64張。因此32我的是上限了 。數組

第二個關鍵的地方是,只有讓拿黑色的牌的人或者拿紅色的牌的人站出來,魔術師才能知道這5我的拿到的連續5張撲克牌到底是什麼。其實魔術師說「我已經知道大家全部人拿到的是什麼牌」的時候,他並不知道每一個人拿到的是什麼牌。網絡

撲克牌除了點數之外,還有4種花色。如今須要32張牌,就是1-8號牌,每號牌都有4種花色。花色用2位二進制編碼,1-8用3位二進制編碼。因而5位二進制正好能夠表示一張撲克牌全部信息。ide

如上圖,00110 表示的就是梅花6 。11000 表示的是紅桃8(由於沒有 0 號牌,因此000就表示8)函數

第一步將撲克牌編碼完成之後,第二步就須要找到一個序列,它必須知足如下的條件:由 2^n-1^個1和2^n-1^個0構成的序列或者圓排列,是否能存在在任意 n 個位置上0,1序列兩兩都不一樣。知足這個條件的序列也稱爲 n 階完備二進圓排列。

這個魔術中咱們須要找的是 5 階完備二進圓排列。答案是存在這樣一個知足條件的序列。這個序列也就是文章的主角,德布魯因序列。

上述序列就是一個窗口大小爲5的德布魯因序列。任意連續的5個二進制相互之間都是兩兩不一樣的。因此給觀衆任意洗牌,無論怎麼洗牌,只要最終挑出來是連續的5張,這5張的組合都在最終的結果之中。

將窗口大小爲5的德布魯因序列每5個二進制位都轉換成撲克牌的編碼,以下:

因此32張牌的初始順序以下:

梅花8,梅花A,梅花2,梅花4,黑桃A,方片2,梅花5,黑桃3,方片6,黑桃4,紅桃A,方片3,梅花7,黑桃7,紅桃7,紅桃6,紅桃4,紅桃8,方片A,梅花3,梅花6,黑桃5,紅桃3,方片7,黑桃6,紅桃5,紅桃2,方片5,黑桃2,方片4,黑桃8,方片8。

將全部的排列組合列舉出來,如上圖。當魔術師讓黑色或者紅色的牌的人出列的時候,就能肯定到具體是哪種組合了。因而也就能夠直接說出每一個人手上拿的是什麼牌了。

這個魔術中選取的德布魯因序列也很是特殊,是能夠經過一部分的遞推獲得。

這個特殊的序列,任意取出其中一個窗口,即5個連續的二進制,5個二進制的第一位和第三位,或者倒數第三位和倒數第五位相加,加法遵循二進制規則,便可獲得這個窗口緊接着的下一位。

如上圖的例子,假設當前窗口裏面的五位是 00001,左數第一位加上第三位,或者右數第三位加上第五位,獲得的是0,那麼這個窗口緊接着的後一位就是0 ,即 000010 。再舉一個例子,當前窗口裏面是 11000 ,左數第一位加上第三位爲1,因此緊接着的下一位是1,即 110001 。

三. 德布魯因序列的定義和性質

1. 定義

德布魯因序列(De Bruijn sequence),記爲B(k, n),是 k 元素構成的循環序列。全部長度爲 n 的 k 元素構成序列都在它的子序列(以環狀形式)中,出現而且僅出現一次。

例如,序列 00010111 屬於B(2,3)。 00010111 的全部長度爲3的子序列爲000,001,010,101,011,111,110,100,正好構成了 {0,1} ^3^ 的全部組合。

2. 長度

德布魯因序列的長度爲 k^n^。

注意到,全部長度爲 n 的 k 元素構成的序列總共有 k^n^。而對於德布魯因序列中的每一個元素,剛好構成一個以此元素開頭長度爲 n 的子序列。因此德布魯因序列的長度爲 k^n^ 。

3. 數量

德布魯因序列的數量 B(k,n) 的數量爲 (k!) ^ (k^n-1^) / k^n^ 。

咱們用數學概括法證實一下上述的結論。

咱們先假設德布魯因序列是二進制的,即 k = 2。想計算序列數量總共有多少個,其實能夠看這個序列每一個子序列轉換成10進制的數最大的是多少,那麼就是它的數量。

因爲每相鄰的子序列是相互依賴的關係,好比下一個子序列是前一個子序列左移一位再加上 0 或者 1,產生下一個子序列。固然最後要 mod 2^n^,這樣控制每一個子序列的長度都在 n 位之間。因而咱們能夠獲得這樣的一個式子:

s[i+1]=(2s[i]+(0|1))mod(2^n)複製代碼

利用錯位相減法,咱們能夠獲得通項公式:

|B(2,n)|= 2 ^ 2^(n−1)^ / 2^n^

最後利用數學概括法咱們能夠獲得一個通用的式子,即:

|B(k,n)| 的數量爲 (k!) ^ (k^n-1^) / k^n^

最最經常使用的德布魯因序列就是 k = 2 。計算一下 |B(2,n)| 的數量,以下:

4. 生成方式

因爲德布魯因序列並不惟一,因此用代碼能夠生成其中的任意一種。

def de_bruijn(k, n):
    """ de Bruijn sequence for alphabet k and subsequences of length n. """
    try:
        # let's see if k can be cast to an integer;
        # if so, make our alphabet a list
        _ = int(k)
        alphabet = list(map(str, range(k)))

    except (ValueError, TypeError):
        alphabet = k
        k = len(k)

    a = [0] * k * n
    sequence = []

    def db(t, p):
        if t > n:
            if n % p == 0:
                sequence.extend(a[1:p + 1])
        else:
            a[t] = a[t - p]
            db(t + 1, p)
            for j in range(a[t - p] + 1, k):
                a[t] = j
                db(t + 1, t)
    db(1, 1)
    return "".join(alphabet[i] for i in sequence)複製代碼

二進制的德布魯因序列用的比較多,接下來看看它生成的序列。

B(2,1) 就惟一一種狀況。

i  01  s[i]
0  0    0
1   1   1複製代碼

B(2,2) 由4位二進制位組成。也是惟一一種狀況。

i  0011|0  s[i]
0  00 . . . 0
1   01      1
2  . 11 . . 3
3     1|0   2複製代碼

B(2,3) 由8位二進制位組成。有2種德布魯因序列。

i  00010111|00 s[i]    i  00011101|00 s[i]
0  000 . . . .  0      0  000 . . . .  0
1   001         1      1   001         1
2  . 010 . . .  2      2  . 011 . . .  3
3     101       5      3     111       7
4  . . 011 . .  3      4  . . 110 . .  6
5       111     7      5       101     5
6  . . . 11|0   6      6  . . . 01|0   2
7         1|00  4      7         1|00  4複製代碼

B(2,4) 由16位二進制位組成。對應有16種德布魯因序列。

0x09af  0000100110101111
0x09eb  0000100111101011
0x0a6f  0000101001101111
0x0a7b  0000101001111011
0x0b3d  0000101100111101
0x0b4f  0000101101001111
0x0bcd  0000101111001101
0x0bd3  0000101111010011
0x0cbd  0000110010111101
0x0d2f  0000110100101111
0x0d79  0000110101111001
0x0de5  0000110111100101
0x0f2d  0000111100101101
0x0f4b  0000111101001011
0x0f59  0000111101011001
0x0f65  0000111101100101複製代碼

取出其中的 0x0d2f :

i  0000110100101111|000 s[i]
 0  0000 . . . . . . . .  0
 1   0001                 1
 2  . 0011 . . . . . . .  3
 3     0110               6
 4  . . 1101 . . . . . . 13
 5       1010            10
 6  . . . 0100 . . . . .  4
 7         1001           9
 8  . . . . 0010 . . . .  2
 9           0101         5
10  . . . . . 1011 . . . 11
11             0111       7
12  . . . . . . 1111 . . 15
13               111|0   14
14  . . . . . . . 11|00  12
15                 1|000  8複製代碼

B(2,5) 由32位二進制位組成。對應有2048種德布魯因序列。因爲太多了,這裏無法一一列舉出來,任意挑選一個出來舉例:

i  00000111011010111110011000101001|0000 s[i]
 0  00000 . . . . . . . . . . . . . . . .  0
 1   00001                                 1
 2  . 00011 . . . . . . . . . . . . . . .  3
 3     00111                               7
 4  . . 01110 . . . . . . . . . . . . . . 14
 5       11101                            29
 6  . . . 11011 . . . . . . . . . . . . . 27
 7         10110                          22
 8  . . . . 01101 . . . . . . . . . . . . 13
 9           11010                        26
10  . . . . . 10101 . . . . . . . . . . . 21
11             01011                      11
12  . . . . . . 10111 . . . . . . . . . . 23
13               01111                    15
14  . . . . . . . 11111 . . . . . . . . . 31
15                 11110                  30
16  . . . . . . . . 11100 . . . . . . . . 28
17                   11001                25
18  . . . . . . . . . 10011 . . . . . . . 19
19                     00110               6
20  . . . . . . . . . . 01100 . . . . . . 12
21                       11000            24
22  . . . . . . . . . . . 10001 . . . . . 17
23                         00010           2
24  . . . . . . . . . . . . 00101 . . . .  5
25                           01010        10
26  . . . . . . . . . . . . . 10100 . . . 20
27                             01001       9
28  . . . . . . . . . . . . . . 1001|0. . 18
29                               001|00    4
30  . . . . . . . . . . . . . . . 01|000   8
31                                 1|0000 16複製代碼

B(2,6) 由64位二進制位組成。對應有67,108,864種德布魯因序列。因爲太多了,這裏無法一一列舉出來,任意挑選一個出來舉例:

i  0000001000101111110111010110001111001100100101010011100001101101|00000 s[i]
 0  000000 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .  0
 1   000001                                                                 1
 2  . 000010 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .  2
 3     000100                                                               4
 4  . . 001000 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .  8
 5       010001                                                            17
 6  . . . 100010 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 34
 7         000101                                                           5
 8  . . . . 001011 . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11
 9           010111                                                        23
10  . . . . . 101111 . . . . . . . . . . . . . . . . . . . . . . . . . . . 47
11             011111                                                      31
12  . . . . . . 111111 . . . . . . . . . . . . . . . . . . . . . . . . . . 63
13               111110                                                    62
14  . . . . . . . 111101 . . . . . . . . . . . . . . . . . . . . . . . . . 61
15                 111011                                                  59
16  . . . . . . . . 110111 . . . . . . . . . . . . . . . . . . . . . . . . 55
17                   101110                                                46
18  . . . . . . . . . 011101 . . . . . . . . . . . . . . . . . . . . . . . 29
19                     111010                                              58
20  . . . . . . . . . . 110101 . . . . . . . . . . . . . . . . . . . . . . 53
21                       101011                                            43
22  . . . . . . . . . . . 010110 . . . . . . . . . . . . . . . . . . . . . 22
23                         101100                                          44
24  . . . . . . . . . . . . 011000 . . . . . . . . . . . . . . . . . . . . 24
25                           110001                                        49
26  . . . . . . . . . . . . . 100011 . . . . . . . . . . . . . . . . . . . 35
27                             000111                                       7
28  . . . . . . . . . . . . . . 001111 . . . . . . . . . . . . . . . . . . 15
29                               011110                                    30
30  . . . . . . . . . . . . . . . 111100 . . . . . . . . . . . . . . . . . 60
31                                 111001                                  57
32  . . . . . . . . . . . . . . . . 110011 . . . . . . . . . . . . . . . . 51
33                                   100110                                38
34  . . . . . . . . . . . . . . . . . 001100 . . . . . . . . . . . . . . . 12
35                                     011001                              25
36  . . . . . . . . . . . . . . . . . . 110010 . . . . . . . . . . . . . . 50
37                                       100100                            36
38  . . . . . . . . . . . . . . . . . . . 001001 . . . . . . . . . . . . .  9
39                                         010010                          18
40  . . . . . . . . . . . . . . . . . . . . 100101 . . . . . . . . . . . . 37
41                                           001010                        10
42  . . . . . . . . . . . . . . . . . . . . . 010101 . . . . . . . . . . . 21
43                                             101010                      42
44  . . . . . . . . . . . . . . . . . . . . . . 010100 . . . . . . . . . . 20
45                                               101001                    41
46  . . . . . . . . . . . . . . . . . . . . . . . 010011 . . . . . . . . . 19
47                                                 100111                  39
48  . . . . . . . . . . . . . . . . . . . . . . . . 001110 . . . . . . . . 14
49                                                   011100                28
50  . . . . . . . . . . . . . . . . . . . . . . . . . 111000 . . . . . . . 56
51                                                     110000              48
52  . . . . . . . . . . . . . . . . . . . . . . . . . . 100001 . . . . . . 33
53                                                       000011             3
54  . . . . . . . . . . . . . . . . . . . . . . . . . . . 000110 . . . . .  6
55                                                         001101          13
56  . . . . . . . . . . . . . . . . . . . . . . . . . . . . 011011 . . . . 27
57                                                           110110        54
58  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 101101 . . . 45
59                                                             01101|0     26
60  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1101|00  . 52
61                                                               101|000   40
62  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 01|0000  16
63                                                                 1|00000 32複製代碼

B(2,5) 和 B(2,6) 在實際生產中都有普遍的用途。

四. 在圖論中的應用:歐拉回路 和 漢密爾頓迴路

在圖論中,有這樣一種無向連通圖,有一條通路,能通過這個圖的每條邊一次而且僅一次的路徑被稱爲歐拉回路。這個問題也是最多見的哥尼斯堡七橋問題:能不能一次走遍全部的7座橋,而且每座橋只通過一次。其實就是判斷是否存在歐拉回路。

與歐拉問題很是相似的是漢密爾頓迴路的問題。該問題起源於英國數學家威廉漢密爾頓(Willian Hamilton)於1857年發明的一個關於正十二面體的數學遊戲:正十二面體的每一個棱角上標有一個當時很是有名的城市,遊戲的目的是「環繞地球」旅行,也就是說,尋找一個環遊路線使得通過每一個城市一次且剛好一次。

若是把正十二面體的20個棱角當作圖中的頂點,將正十二面體畫成如上圖的平面圖,那麼問題就轉換成:可否在圖中找到一條迴路,通過每一個頂點一次有且僅有一次。上圖就給出了一條符合要求的迴路。

歐拉回路的問題通常求解方法有兩種,DFS 和 Fleury 佛羅萊算法。可是漢密爾頓圖沒有一個有效的判斷方法,它只是給出了一些充分條件或者必要條件,並不是充要條件。

德布魯因序列 和 歐拉回路,漢密爾頓迴路 有緊密的聯繫。

若由 k 種符號組成的全部長度爲 n 的序列列表爲有向圖的頂點,則圖中有 k^n^ 個頂點, 若頂點 m 去掉第一個符號並在尾端添加一個符號即可得頂點 n ,則有一個有向邊由 m 指向 n,此時圖就是 德布魯因圖。以下圖,k = 2, n = 3 的圖中,頂點 010 有兩條對外的邊,分別指向 101 和 100 。

咱們以 B(2,3) 舉例。

在德布魯因圖中的漢密爾頓迴路 即爲 德布魯因序列。以下圖,左圖中紅色的漢密爾頓迴路 ,圖中對應的德布魯因序列是 00010111,且這個漢密爾頓迴路等價於窗口長度爲 2 的德布魯因序列中的一個歐拉回路,見下右圖中標記的歐拉回路對應的順序編號。

因此,窗口爲 n 的德布魯因圖中的漢密爾頓迴路能夠等價轉換爲窗口爲 n - 1 的德布魯因圖中的歐拉回路。

固然,一個德布魯因圖中的漢密爾頓迴路並不必定惟一。如上左圖,一樣是 k = 2,n = 3 的德布魯因圖,還能夠找到一條漢密爾頓迴路。對應的歐拉回路的順序也對應的發生了變化,見右圖。

這也說明了,k = 2,n = 3 的時候,德布魯因序列存在多個,並不惟一。

再進一步,既然說高階的德布魯因圖的漢密爾頓迴路能夠轉換成低級的歐拉回路。那麼咱們就用 k = 2,n = 3 的德布魯因圖的歐拉回路去構造高階的漢密爾頓圖,能夠麼?答案是固然能夠。

如上圖,用 k = 2,n = 3 的德布魯因圖中的一條歐拉回路,咱們構造出了 k = 2,n = 4 的德布魯因序列。

同理,當 k = 3,n = 2 的時候,德布魯因圖中依舊能夠找到一條漢密爾頓迴路,與之對應的 n = 1 的窗口的歐拉回路也存在。以下圖。

五. 位掃描器

德布魯因序列用的比較普遍的一點應用就是 位掃描器。在 Google S2 中也是這個做用。

先來看一個比較常見的問題。

有一個非0的正數,它用二進制表示的。問如何快速的找到它二進制位中最後的1所在的位置。例如,0101010010010100,它的最後一個1所在的位置是從右往左數的第2位(從0開始數)。

這道題有幾種作法,從粗糙到最優依次分析分析。

最直接的想法是能把這個二進制數轉換成只有一位爲1的狀況。若是上面這個問題轉換成只有一個位上爲1的狀況,那很好解決。

那麼問題轉化爲如何把末尾的1分離出來。若是這個數只有2個位置上爲1,能夠直接用位運算進行分離。

x &= (~x+1)

// 或者

x &= -x複製代碼

經過上面的操做能夠把最後一位的1分離出來。

分離出來之後的解法就不少種了。

1. 循環

能夠用 for 循環,不斷的右移目標數。

for ( index = -1; x > 0; x >>= 1, ++index ) ;複製代碼

這種方式簡單粗暴,時間複雜度爲 O(n) 。

2. 二分搜索

把上述循環改爲二分,時間複雜度就變成了 O(lgn)

3. 構造特殊數字進行位運算

這種方式看上去比較巧,可是實際仍是利用了二分搜索的思想。

index = 0;
index += (!!(x & 0xAAAAAAAA)) * 1;
index += (!!(x & 0xCCCCCCCC)) * 2;
index += (!!(x & 0xF0F0F0F0)) * 4;
index += (!!(x & 0xFF00FF00)) * 8;
index += (!!(x & 0xFFFF0000)) * 16;複製代碼

這種方式的時間複雜度也是 O(lgn) ,可是實際計算會比二分搜索快不少,由於它不須要比較運算,都位運算便可完成。

5. 哈希

這種方式就比以前的方式都要高效了。

假設 x 有32位,因此末尾的1出現的可能只有32種。若是 x 爲64,那就是64種可能,每一位上都有可能。經過哈希的方式 O(1) 的時間複雜度查詢出結果。

6. 德布魯因序列

這種方式的原理也是哈希,可是這種方式比單純的哈希要快,由於它避免的取餘的計算。

若是 x 爲32位,那麼哈希函數能夠構形成下面這樣:

(x * 0x077CB531) >> 27複製代碼

0x077CB531 是32位的德布魯因序列之一。

構造這樣一個哈希函數有2點優勢:

  1. 二進制數自己是二的次方,因此任何一個數字乘以這個二進制的數,都至關於左移運算。
  2. 德布魯因序列至關因而全排列,枚舉了全部的狀況。因此它的兩兩子序列之間確定不相同,這就是完美的哈希。

最後又移動27位是爲了保證能取出開頭的5位。

在 Go 的原生代碼包中,有一個 nat.go 文件,在這個文件裏面有這樣一段代碼:

const deBruijn32 = 0x077CB531

var deBruijn32Lookup = []byte{
    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,
}

const deBruijn64 = 0x03f79d71b4ca8b09

var deBruijn64Lookup = []byte{
    0, 1, 56, 2, 57, 49, 28, 3, 61, 58, 42, 50, 38, 29, 17, 4,
    62, 47, 59, 36, 45, 43, 51, 22, 53, 39, 33, 30, 24, 18, 12, 5,
    63, 55, 48, 27, 60, 41, 37, 16, 46, 35, 44, 21, 52, 32, 23, 11,
    54, 26, 40, 15, 34, 20, 31, 10, 25, 14, 19, 9, 13, 8, 7, 6,
}複製代碼

在這個文件中,一樣有一個函數在解決上述的問題,只不過它換了一個角度。

求一個二進制數的末尾1所在的位置,其實能夠轉化爲求這個二進制數末尾連續0有多少個的問題。

這個經典的問題在圖靈獎得到者 Donald Ervin Knuth 的著做 《計算機程序設計藝術》的第四卷,section 7.3.1 上有,感興趣的同窗能夠看看這個問題。

// trailingZeroBits returns the number of consecutive zero bits on the right
// side of the given Word.
// See Knuth, volume 4, section 7.3.1
func trailingZeroBits(x Word) int {
    // x & -x leaves only the right-most bit set in the word. Let k be the
    // index of that bit. Since only a single bit is set, the value is two
    // to the power of k. Multiplying by a power of two is equivalent to
    // left shifting, in this case by k bits. The de Bruijn constant is
    // such that all six bit, consecutive substrings are distinct.
    // Therefore, if we have a left shifted version of this constant we can
    // find by how many bits it was shifted by looking at which six bit
    // substring ended up at the top of the word.
    switch _W {
    case 32:
        return int(deBruijn32Lookup[((x&-x)*deBruijn32)>>27])
    case 64:
        return int(deBruijn64Lookup[((x&-x)*(deBruijn64&_M))>>58])
    default:
        panic("Unknown word size")
    }

    return 0
}複製代碼

這裏還須要解釋一下 deBruijn32Lookup 和 deBruijn64Lookup 數組裏面初始裝入的數字到底表明了什麼意思。

deBruijn32 和 deBruijn64 分別是德布魯因序列。兩兩之間的子序列都是不相同的,而且全部的子序列構成了一個全排列。

const deBruijn32 = 0x077CB531
// 0000 0111 0111 1100 1011 0101 0011 0001

const deBruijn64 = 0x03f79d71b4ca8b09
// 0000 0011 1111 0111 1001 1101 0111 0001 1011 0100 1100 1010 1000 1011 0000 1001複製代碼

咱們用下面的哈希函數構造一個「完美」的哈希函數。

h(x) = (x * deBruijn) >> (n - lg n)複製代碼

n 是二進制數的位數。因而也就能夠理解 ((x&-x)deBruijn32)>>27 和 ((x&-x)(deBruijn64&_M))>>58 是什麼意思了,這就是在算對應的哈希值。

那麼數組裏面存的值就是咱們最終的結果了,即末尾1所在的位置或者末尾連續有多少個0 。

其實數組裏面存的數字是這樣算出來的:

void setup( void )
{    
    int i;
    for(i=0; i<32; i++)
        index32[ (debruijn32 << i) >> 27 ] = i;
}複製代碼

即把算出來的哈希值做爲下標,對應下標存儲的值是左移的位數。這個左移的位數就是咱們要的結果。因此先算哈希值,而後經過數組取出哈希值裏面存儲的值即爲咱們的結果了。

// findLSBSetNonZero64 returns the index (between 0 and 63) of the least
// significant set bit. Passing zero to this function has undefined behavior.
//
// This code comes from trailingZeroBits in https://golang.org/src/math/big/nat.go
// which references (Knuth, volume 4, section 7.3.1).
func findLSBSetNonZero64(bits uint64) int {
    return int(deBruijn64Lookup[((bits&-bits)*(deBruijn64&digitMask))>>58])
}複製代碼

上述程序和以前的實現方式徹底一致,只不過這裏函數名的意義表明查找末尾1的位置,和查找末尾有多少個0,徹底一致!

上述代碼也是 Google S2 中的源碼,它也是直接利用德布魯因序列來查找末尾1所在的位。

六. 工業應用

De Bruijn 序列的奇妙不只體如今魔術上。咱們還可使用它爲機器人作路標定位:將兩種不一樣顏色的小方塊排成一條長線擺在機器人行進的路上,機器人只要識別出本身先後的幾個方塊是什麼顏色,既不須要GPS,也不須要高精度探測儀,就能夠知道本身走了多少米。

研究人員利用 De Bruijn 序列設計了每次能夠產生一個用於加密的不一樣隨機數字的簡單電子元件「反饋移位寄存器」,上一個隨機數字和下一個隨機數字之間只改變一個數位和移位一下就能夠,電路構造很是簡單。

在測量工程上,德布魯因序列還能夠用在基於光柵投影模式的三維形貌快速測量系統研究中。

在基因工程中,德布魯因序列能夠用在基因組重複區域組裝上。

在人工智能算法中,神經網絡時間序列預測也有德布魯因序列的身影。


Reference:

Wiki De Bruijn sequence
Wolfram Mathworld de Bruijn Sequence
chessprogramming.wikispaces.com/De+Bruijn+s…
The On-Line Encyclopedia of Integer Sequences
De Bruijn cycle generator
On line Sequence Generator
de Bruijn cycles for neural decoding


空間搜索系列文章:

如何理解 n 維空間和 n 維時空
高效的多維空間點索引算法 — Geohash 和 Google S2
Google S2 中的四叉樹求 LCA 最近公共祖先
神奇的德布魯因序列

GitHub Repo:Halfrost-Field

Follow: halfrost · GitHub

Source: halfrost.com/go_s2_De_Br…

相關文章
相關標籤/搜索