德布鲁因序列与indexing 1
写在前面
在数值计算中,为了控制精度以及避免越界,需要严格控制数值的范围,有时需要知道二进制表示中"left-most 1"或"right-most 1”的位置,这篇文章就来介绍一下通过德布鲁因序列(De Bruijn sequence)来快速定位的方法。
标记left-most 1与right-most 1
对于一个二进制数\(v\),如何仅保留最低位或最高位的1?
最低位的1,即right-most 1,其特点是这一位右侧均为0,可通过v & -v或者v & ((~v)+1)来标记最低位的1。
比如0101 1010,取反后为1010 0101,再加1为1010 0110,与后为0000 0010。
最高位的1,即left-most 1,其特点是这一位左侧均为0,可通过下面来标记最高位的1。
uint32_t keepHighestBit( uint32_t n )
{
n |= (n >> 1);
n |= (n >> 2);
n |= (n >> 4);
n |= (n >> 8);
n |= (n >> 16);
return n - (n >> 1);
}
前5行移位将最高位1右侧的所有位均置为1,n-(n >> 1)再将他们清0。
至此,我们已经得到了一个二进制的“one hot”表示,只有1位为1,它标记了最高位或最低位1的位置。
确定位置
假设,得到的“one hot”表示为0000 0100 0000 0000,如何确定1在哪一位呢?
比较直接的想法是通过移位计数,不断右移,并计数,直到最低位为1。
有没有更好的方法?
令得到的“one hot”表示为h,对于uint32,h只有32种,我们希望找到的这32种one hot表示与\(0\sim 31\)的映射关系,即\(f(h) \rightarrow 0\sim 31\)。
- 查表:以
h对应的uint32数为下标,构建数组,通过查表方式得到,但h最大为\(2^{31}\),直接构建数组不现实 - 哈希:再增加一层映射,\(f(g(h)) \rightarrow 0\sim 31\),即找到一个hash函数\(g\),先将\(h\)映射到\(0 \sim 31\),再通过查表\(0\sim 31 \rightarrow 0\sim 31\),但一般哈希会涉及到取余操作,还要考虑不要有碰撞
对这个特殊问题,可以使用 德布鲁因序列——可视为一种特殊的哈希,不需要取余,且绝不会发生碰撞。
德布鲁因序列(De Bruijn sequence)
先看一个德布鲁因序列的例子,令字符集\(A = \{0, 1\}\),字符有\(k=2\)种,子串长度\(n=2\),则所有可能的子串有\(\{00, 01, 10, 11\}\),则循环序列\(0011\)是一个德布鲁因序列,\(0011\)的所有连续子串恰好为\(\{00, 01, 10, 11\}\),都出现且只出现一次,同样,循环序列\(1001\)也是一个德布鲁因序列。

可见,德布鲁因序列并不唯一,且是个循环序列,长度恰好为\(k^n\),与所有可能子串的数量相同。
wiki上的定义如下,
In combinatorial mathematics, a de Bruijn sequence of order \(n\) on a size-\(k\) alphabet A is a cyclic sequence in which every possible length-\(n\) string on \(A\) occurs exactly once as a substring (i.e., as a contiguous subsequence). Such a sequence is denoted by \(B(k, n)\) and has length \(k^n\), which is also the number of distinct strings of length \(n\) on \(A\).
——from wiki De Bruijn sequence
再举一个\(B(2, 4)\)的例子,序列长度为\(2^4=16\),如下
\]
其所有循环子串如下,
.png)
每个位置的子串均不相同,所有子串对应着\(0\sim 2^n-1\)范围的整数,恰好形成了\(2^n\)个位置与\(2^n\)个数的映射。
德布鲁因序列的使用
将h与德布鲁因序列相乘,相当于左移操作,把某位置的子串移到了最左端,再将该子串右移至最右,即仅保留该子串,可知道该子串是什么,因为序列中每个子串的位置都是唯一的,根据映射关系可知道该子串的位置,相当于知道了h。为此需要建立 子串与位置 对应关系的检索表。
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];
// The index of the LSB in v is stored in r
//return the index of the most significant bit set from a 32 bit unsigned integer
uint8_t highestBitIndex( uint32_t b )
{
static const uint32_t deBruijnMagic = 0x06EB14F9;
static const uint8_t deBruijnTable[32] = {
0, 1, 16, 2, 29, 17, 3, 22, 30, 20, 18, 11, 13, 4, 7, 23,
31, 15, 28, 21, 19, 10, 12, 6, 14, 27, 9, 5, 26, 8, 25, 24,
};
return deBruijnTable[(keepHighestBit(b) * deBruijnMagic) >> 27];
}
因为德布鲁因序列是循环序列,而左移操作会自动在最低位填0,所以习惯将全0子串放在序列的最高位,这样比较方便,不需要特殊处理。
德布鲁因序列的生成与索引表的构建
德布鲁因序列可以通过构建德布鲁因图得到,图中每条哈密顿路径(Hamiltonian path)都对应一个德布鲁因序列,

数量共有
\]
具体生成方式和证明可查看De Bruijn sequence和神奇的德布鲁因序列。
保存子串与位置映射关系的检索表可通过如下方式生成,其中debruijn32为德布鲁因序列对应的uint32正整数。
uint8 index32[32] = {0};
void setup( void )
{
int i;
for(i=0; i<32; i++)
index32[ (debruijn32 << i) >> 27 ] = i;
}
参考
德布鲁因序列与indexing 1的更多相关文章
- Ural2004: Scientists from Spilkovo(德布鲁因序列&思维)
Misha and Dima are promising young scientists. They make incredible discoveries every day together w ...
- 神秘常量0x077CB531,德布莱英序列的恩赐
本文发布于游戏程序员刘宇的个人博客, 转载请注明来源https://www.cnblogs.com/xiaohutu/p/10950011.html 某天我在优化游戏的算法,在将一个个关键数据结构优化 ...
- De Bruijn序列
最近文章中经常出现及De Bruijin 这个关键字,网上搜索了一下,记录下来. De Bruijn序列 (德布鲁因序列) 问题:能否构造一个长度为2的n次方的二进制环状串,使得二进制环状串中总共2的 ...
- 高效的多维空间点索引算法 — Geohash 和 Google S2
原文地址:https://www.jianshu.com/p/7332dcb978b2 引子 每天我们晚上加班回家,可能都会用到滴滴或者共享单车.打开 app 会看到如下的界面: app ...
- 3D-camera结构光原理
3D-camera结构光原理 目前主流的深度探测技术是结构光,TOF,和双目.具体的百度就有很详细的信息. 而结构光也有双目结构光和散斑结构光等,没错,Iphone X 的3D深度相机就用 散斑结构光 ...
- <..................> 哈佛大学哲学系 && 历史哲学笔记文献集
哈佛大学哲学系课程表 (一)概况 (1)哈佛大学哲学系现有教师21人,其中访问教授7人,教师流动性较大,每年有一定的变化.以下为现任教师:Richard Moran(系主任 ...
- Thenao tutorial – indexing
Theano和numpy一样,支持基本的下标取值方法和高级的下标取值方法. 因为theano中没有boolean类型,所以不支持boolean类型的masks. # head file support ...
- 【循序渐进学Python】2. Python中的序列——列表和元组
序列概览 在Python中有六种内建的序列:列表.元组.字符串.Unicode字符串.buffer对象和xrange对象.在这里暂时只讨论列表和元组.列表和元组的主要区别在于:列表可以修改,元组(不可 ...
- Python列表,元组,字典,序列,引用
1.列表 # Filename: using_list.py # This is my shopping list shoplist=["apple", "mango&q ...
随机推荐
- TypeError: this.xxx.substring is not a function的解决办法
这是因为已经改变了xxx的值的类型,不再是字符串的话将不会拥有substring函数, 我当时这样写的时候,直接将number类型赋予了this.enter,所以导致了错误. 改为这样之后可以使用su ...
- Mybatis一对多或多对多只能查出一条数据解决策略
原文:https://blog.csdn.net/ren814/article/details/81742242 <resultMap id="menuModelMap" t ...
- 通信中的错误代码 (repost from https://blog.csdn.net/zzhuan_1/article/details/80066716)
• 100 - 继续.• 101 - 切换协议.• 110 重新启动标记答复.• 120 服务已就绪,在 nnn 分钟后开始.• 125 数据连接已打开,正在开始传输.• 150 文件状态正常,准备打 ...
- 技术周刊 · Lighthouse 测试报告生成
登高远眺 天高地迥,觉宇宙之无穷 基础技术 Lighthouse 测试内幕 文章分享了网易云音乐前端性能监控平台使用 Lighthouse 的实践经验,介绍了 Lighthouse 的测试流程.内部模 ...
- MDX
简介 把md文件里的图片转成base64,方便发给别人和上传博客园等博客平台 初衷 用Typora写markdown的感觉很爽,但是每当我写好一篇文章,想要发给小伙伴们炫耀炫耀,或者上传博客园,CSD ...
- 【DMCP】2020-CVPR-DMCP Differentiable Markov Channel Pruning for Neural Networks-论文阅读
DMCP 2020-CVPR-DMCP Differentiable Markov Channel Pruning for Neural Networks Shaopeng Guo(sensetime ...
- JavaWeb网上图书商城完整项目--day02-8.提交注册表单功能之dao、service实现
1.发送邮件 发送邮件的时候的参数我们都写在了配置文件中,配置文件放在src目录下,可以使用类加载器进行加载该数据 //向注册的用户发送邮件 //1读取配置文件 Properties properti ...
- 尚学堂 208.Annotation注解和内置注解
208.Annotation注解和内置注解 override:这个注释的作用是标识某一个方法是否覆盖了它的父类的方法deprecated:表示果某个类成员的提示中出现了个词,就表示这个并不建议使用这个 ...
- jmeter跨线程组获取cookie或jmeter线程组共享cookie-笔者亲测
一.Jmeter版本 此次示例采用的是apache-jmeter-5.2.1版本 二.设置配置文件使Cookie管理器保存cookie信息. 修改apache-jmeter-5.2.1/bin/jme ...
- JavaScript基础初始时期分支(018)
Init-Time Branching初始时期分支是一种用做优化的模式.如果某些条件在程序启动后就不再改变,那么我们就只需要在初始时期检查一次就可以了,而不是在每次 需要用到这些条件的时候都检查一次. ...