数学中存在这样一个序列,它充满魔力,在实际工程中也有一部分的应用。今天就打算分享一下这个序列,它在 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 。
德布鲁因序列(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^ 的全部组合。
德布鲁因序列的长度为 k^n^。
注意到,全部长度为 n 的 k 元素构成的序列总共有 k^n^。而对于德布鲁因序列中的每一个元素,刚好构成一个以此元素开头长度为 n 的子序列。因此德布鲁因序列的长度为 k^n^ 。
德布鲁因序列的数量 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)| 的数量,以下:
因为德布鲁因序列并不惟一,因此用代码能够生成其中的任意一种。
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分离出来。
分离出来之后的解法就不少种了。
能够用 for 循环,不断的右移目标数。
for ( index = -1; x > 0; x >>= 1, ++index ) ;复制代码
这种方式简单粗暴,时间复杂度为 O(n) 。
把上述循环改为二分,时间复杂度就变成了 O(lgn)
这种方式看上去比较巧,可是实际仍是利用了二分搜索的思想。
index = 0;
index += (!!(x & 0xAAAAAAAA)) * 1;
index += (!!(x & 0xCCCCCCCC)) * 2;
index += (!!(x & 0xF0F0F0F0)) * 4;
index += (!!(x & 0xFF00FF00)) * 8;
index += (!!(x & 0xFFFF0000)) * 16;复制代码
这种方式的时间复杂度也是 O(lgn) ,可是实际计算会比二分搜索快不少,由于它不须要比较运算,都位运算便可完成。
这种方式就比以前的方式都要高效了。
假设 x 有32位,因此末尾的1出现的可能只有32种。若是 x 为64,那就是64种可能,每一位上都有可能。经过哈希的方式 O(1) 的时间复杂度查询出结果。
这种方式的原理也是哈希,可是这种方式比单纯的哈希要快,由于它避免的取余的计算。
若是 x 为32位,那么哈希函数能够构形成下面这样:
(x * 0x077CB531) >> 27复制代码
0x077CB531 是32位的德布鲁因序列之一。
构造这样一个哈希函数有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…