关于deBruijn序列、它的生成、以及它的用途。
一、关于deBruijn序列
deBruijn序列是一串字符,满足如下条件:deBruijn序列的长度为n的所有子循环序列不重复。这里的子循环序列,包括字符尾部到头部拼接的序列。
举两个例子:
(1)序列00010111,为长度3的deBruijn序列。
其所有长度为3的子循环序列为:
000 001 010 101 011 111 110(尾部两个字符+头部一个字符) 100(尾部一个字符+头部两个字符)
刚好都不重复,所以此序列为3阶deBruijn序列。
(2)序列aabb,为长度2的deBruijn序列。
其所有长度2的子循环序列为:
aa
ab
bb
ba(尾部一个字符+头部一个字符)
刚好也不重复,所以此序列为2阶deBruijn序列。
这里有几个基本概念:
n:子循环序列的长度,上面例子1中为3,例子2中为2
A:字母表,即deBruijn序列包含的字符,上面例子1中即为[0,1],例子2中即为[a,b]
k:字母表包含的字符个数,上面两个例子中均为2
B(k,n):deBruijn序列,其长度为k**n(n次方),上面两个例子中分别为8和4;总共有((k!)^(k**(n-1))/(k**(n))个不同的deBruijn序列
后续会以这几个变量进行计算
二、deBruijn序列的生成
如下为一种生成方式:
import sys 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) if __name__ == "__main__": if len(sys.argv) == 3: k=sys.argv[1] if k.isdigit(): k=int(k) n=int(sys.argv[2]) print(k,n) print(de_bruijn(k,n)) exit() else: print("Usage: %s k n"%sys.argv[0])
三、deBruijn序列的用途
(1)典型的用途:快速查找数据最后0的个数,或者最后一个1是第几位(最低位为第0位)。(如果数据全0,则返回位数)
如数字int8 x=80(0101 0000),最后4个0。使用deBruijn序列可以快速计算出来。 分为如下几步:
首先,通过如下运算,隔离出最后一个1:
y = x & (-x)
由于x=0101 0000,则-x=1011 0000。
y = x & (-x) = 0001 0000 = 16 即为仅存在最后一个1的数字。
其次,建立一个hash索引,使y能够索引到最后的结果4,同时确保hash表最小。
在这里,int8有8位,因此最小的hash表项为8个,可以利用长度为8的deBruijn序列的子序列不重复的特性来构造。
deBruijn序列B(k=2,n=3):00010111 子序列-》映射值 000 -》1 001 -》2 010 -》3 101 -》4 011 -》5 111 -》6 110 -》7 100 -》8
最后,从y计算出对应的子序列z:
z = (y*deBruijn) >> (k**n-n)
这里int8 z = (16 * 23) >>(8-3) = 011
对应的代码如下:
import math def setupN(debruijnN,mapN,N): n = int(math.log(N,2)) mask = (1<for i in range(N): mapN[((debruijnN << i) & mask ) >> (N-n)]=i def getIndex8(x): mask = (1<<8) -1 y = x&(-x) i = ((y*debruijn8) & mask ) >> (8-3) z = int((y - 1) >> 4 & 8) return map8[i] + z def getIndex32(x): mask = (1<<32) -1 y = x&(-x) i = ((y*debruijn32) & mask ) >> (32-5) z = int((y - 1) >> 26 & 32) return map32[i] + z def getIndex64(x): mask = (1<<64) -1 y = x&(-x) i = ((y*debruijn64) & mask ) >> (64-6) z = int((y - 1) >> 57 & 64) return map64[i] + z debruijn8str = de_bruijn(2,3) debruijn32str = de_bruijn(2,5) debruijn64str = de_bruijn(2,6) debruijn8 = int("0b%s"%debruijn8str, 2) debruijn32 = int("0b%s"%debruijn32str, 2) debruijn64 = long("0b%s"%debruijn64str, 2) map8={} map32={} map64={} setupN(debruijn8, map8,8) setupN(debruijn32, map32,32) setupN(debruijn64, map64,64) print(getIndex8(0x10)) print(getIndex8(0x1)) print(getIndex8(0)) print(getIndex32(0x11000000)) print(getIndex32(0x1)) print(getIndex32(0)) print(getIndex64(0x1100000000000000)) print(getIndex64(0x11)) print(getIndex64(0))
在getIndexN函数中,由于python不支持如int8, int32的类型,因此加入了mask来去除溢出的位。 其他语言支持对应类型的则不需要。
附:参考资料
wiki:https://en.wikipedia.org/wiki/De_Bruijn_sequence