使用de Bruijn序列找到整数的


11

肖恩·安德森发表位摆弄黑客包含埃里克·科尔的算法来找到一个的位整数在的操作与乘法和查找。Ñ v ø LG Ñ log2vNvO(lg(N))

该算法依赖于De Bruijn序列中的“魔术”数。谁能解释这里使用的序列的基本数学特性?

uint32_t v; // find the log base 2 of 32-bit v
int r;      // result goes here

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

v |= v >> 1; // first round down to one less than a power of 2 
v |= v >> 2;
v |= v >> 4;
v |= v >> 8;
v |= v >> 16;

r = MultiplyDeBruijnBitPosition[(uint32_t)(v * 0x07C4ACDDU) >> 27];

2
这个想法来自本文supertech.csail.mit.edu/papers/debruijn.pdf。大小为 de Brujn序列是一种非常简洁地表示大小为所有位字符串的方式:每个可能的字符串作为连续的子序列仅出现一次。因此,如果将de Bruijn序列移位位并读出最后位,则您将拥有的唯一标识符。 ķ Ñ 2 ķ ķ Ñ2kkn2kkn
Sasho Nikolov

1
顺便说一下,这只计算 ; 按照书面规定,它仅适用于32位整数。log2v
Sasho Nikolov

1
@Sasho变成答案?
Yuval Filmus 2013年

@SashoNikolov谢谢,为问题添加了上限功能
Yury Bayda

Answers:


9

首先请注意,此算法仅计算,并且在编写代码时,它仅适用于适合位字的。v 32log2vv32

最先出现的移位和-s序列具有将的前1位一直传播到最低有效位的功能。从数字,这将为您提供。2 登录2 v - 1v2log2v1

有趣的部分是de Bruijn技巧,该技巧来自Leiserson,Prokop和Randall的这篇论文(显然,麻省理工学院的教授花时间做一些hacks :))。关于de Bruijn序列,您需要了解的是,它们以尽可能压缩的方式表示给定长度的所有可能序列。准确地说,字母的de Brujn序列是长度为的二进制字符串,以使每个长度二进制字符串作为连续的子字符串恰好出现一次(允许环绕)。之所以有用,是因为如果您有一个数字其位表示是de Bruijn序列(用填充)小号2 ķ ķ X ķ ķ 2 X < ķ{0,1}s2kkXk零),则的前位唯一地标识(只要)。k2iXii<k


3
请注意,您可以使用这种方式的任何德布鲁因序列来计算给出。但是,您不能使用任意德布鲁因序列来计算给出。此处0x07C4ACDD = 00000111110001001010110011011101似乎是具有一些其他属性的de Bruijn序列,因此,附加不会破坏此方法。2 i i 2 i1 1i2ii2i11
Jukka Suomela

谢谢@JukkaSuomela,对此我有点困惑。我想您总是可以将加1 。v
Sasho Nikolov

5

一些评论(实际上不是答案)。让我们对32位整数进行如下分类:c

  • 类型X:(作为二进制字符串)是De Bruijn序列(对于所有旋转,位[27,31]是不同的)。一个例子:c

    11111011100110101100010100100000
    
  • 类型Y:对于 31,2位[27,31] 是不同的。这就是Leiserson等。用途。例子:= 0 1 312ici=0,1,...,31

    00000100011001010011101011011111
    00001111101110011010110001010010
    
  • 类型Z: [27,31]位对于。这就是我们在原始问题中需要的。例子:= 0 1 31(2i+11)ci=0,1,...,31

    00000111110001001010110011011101  (07C4ACDD)
    10000111110001001010110011011101
    01111000001110110101001100100011
    11111000001110110101001100100011
    

基于快速实验的一些观察结果(我希望我没错):

  1. X类型有65536个整数。

  2. X + Y有4096个整数。这些正是以序列'0000 ...'开头的X类型的整数。

    • 直觉:前导零,旋转=移动?
  3. X + Y + Z有256个整数。这些正是以序列'0000011111 ...'开头的X类型的整数。

    • 直觉:
  4. Y类型的所有整数也都是X类型。

  5. 但是,还有768个Z类型的整数,它们既不是X类型也不是Y类型。它们以'1000011111 ...','0111100000 ...'或'1111100000 ...'开头。


1
这是解决为什么将De Bruijn与2 ^ n-1相乘而不是2 ^ n相乘的唯一答案。如果有人可以扩展上面#3的“直觉”,我会喜欢的。埃里克·科尔(Eric Cole)如何提出这个想法?反复试验?还是对乘以2 ^ n-1时这些位实际发生什么情况有所了解?
FarmerBob 2014年

1
  • 这个常数从哪里来?

引述:“ 2009年12月10日,马克·狄金森(Mark Dickinson)通过要求v向下舍入至下一个2的幂而不是2的幂来舍弃了几次运算。” [graphics.stanford.edu/~seander/bithacks.html]

该特殊常数是具有二进制字母但具有额外属性的De Bruijn序列。我将其称为“ Marc Dickinson属性”,因为原始算法无需这些特殊的DB序列即可实现。通过附加2个额外的操作,我们可以使用任何普通的DB序列。运算:v ^ =(v >> 1); // clr级联或移位后除MSB以外的所有位。

  • 结果(暴力)

序列类型| 否。整数| 不,DBSeq。与| 没有旋转| 与Dickinson属性
B(2,3)| 256 | 16 | 2 | 1
B(2,4)| 64Ki | 256 | 16 | 4
B(2,5)| 04Gi | 64Ki | 02Ki | 256
B(2,6)| 16Ei | 04Gi | 64Mi | ??

  • 特殊财产

操作(( -)),产生32周的结果在其上,如果我们丢弃所有但5 MSB位,然后堆它们是由5位宽的列组成的LFSR,它索引5位字符串的所有32个排列0-31。其他De Bruijn B(2,5)常数可能索引5位字符串的32个排列中的31个,但是运算(v * 0x07C4ACDD )>> 27将失败。还应注意,例如,只有5个MSBit构成该LFSR。5个LSBits之和为166而不是496 。下面对角线的图案是乘法的人工制品通过( -) * 2 k 10x7C4ACDD 2k1(mod232)32k1在此处输入图片说明2k1

  • 具有Dickinson属性的按字典顺序最小的二元de Bruijn序列

    [B(2,3):0x1D] [B(2,4):0x0F2D] [B(2,5):0x7C4ACDD] [B(2,6):仍在搜索中]

如果您希望用一个优雅的数学公式来描述它们或用定理产生它们或类似的东西,我认为这将需要对数论以及可能超出我的技能范围的其他领域的深刻见解。如果我在哪里做一个疯狂的猜测,我敢打赌,它们可能是由细胞自动机产生的。这不是答案吗?在严格的基础上,但是尝试直观地理解它为什么起作用以及为什么它正常起作用,因此您可以放心使用它。

PS我没有介绍LUT的构造,如果您了解算法的工作原理,就很容易得出。


最终找到:B(2,6)0x3f08a4c6acb9dbd-具有'dickinson属性'的64位de bruijn序列。我发现至少有122K这样的序列。
2017年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.