• [读书笔记]用0x077CB531计算末尾0的个数 及分析分析!!!


    在bithacks上读到了好多使用位运算和特殊常数来完成复杂运算的案例,具体链接在这里:

    http://graphics.stanford.edu/~seander/bithacks.html

    今天在 这里 看到了一篇文章分析使用常量 0x077CB531来实现 ctz的功能(count the tailing zeros),哇,学习了,因为之前也在思索为什么是这个常熟,是如何发现的或者是如何构造出来的,今天通过这篇文章 可以一窥究竟。

    bithacks当中提供的代码片段为:

    unsigned int v;  // find the number of trailing zeros in 32-bit v 
    int r;           // result goes here 
    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];

    "v & -v 的作用就是取出右起连续的 0 以及首次出现的 1", 但是核心就在于这个神奇数字0x077CB531的选取。

    0x077CB531 = (00000111011111001011010100110001)2  

    原文还提供了一张大图,就好比最近那种比较获得NSA窃听google and facebook data center的手绘草图一样,一目了然:

    很喜欢这张图。

    这里继续引用原文的描述:

    这个 01 串有一个无比牛 B 的地方:如果把它看作是循环的,它正好包含了全部 32 种可能的 5 位 01 串,既无重复,又无遗漏!其实,这样的 01 串并不稀奇,因为构造这样的 01 串完全等价于寻找一个有向图中的 Euler 回路。如下图,构造一个包含 16 个顶点的图,顶点分别命名为 0000, 0001, 0010, …, 1111 。如果某个点的后 3 位,正好等于另一个点的前 3 位,就画一条从前者出发指向后者的箭头。也就是说,只要两个顶点上的数满足 abcd 和 bcde 的关系( a 、 b 、 c 、 d 、 e 可能代表相同的数字),就从 abcd 出发,连一条到 bcde 的路,这条路就记作 abcde 。注意,有些点之间是可以相互到达的(比如 1010 和 0101 ),有些点甚至有一条到达自己的路(比如 0000 )。

    针对上图可以看到,其实就是说,abcd组成的四位数字进行循环移位,在消费了e后得到bcde,那么就可以视此为完成了状态转换,从源状态abcd经过输入e之后到达了目的状态bcde。由于输入只有两种(0和1),所以任意状态节点的入度和出度分别为2;有的状态处可能出现环,如状态0000,在输入0之后装换到自身;

    显然图中的每个顶点入度和出度都是 2 ,因此这个图一定存在 Euler 回路,我们便能轻易构造出一个满足要求的 01 串了。这样的 01 串就叫做 De Bruijn 序列。

    接下来使用De Bruijn序列来进行位数0数目的判断,基本思路就是利用上述的一一映射关系来通过查表方式来完成0的数目的判断;引用原文的描述是这么讲的:

    De Bruijn 序列在这里究竟有什么用呢?它的用途其实很简单,就是为 32 种不同的情况提供了一个唯一索引。比方说, 1000000 后面有 6 个 0 ,将 1000000 乘以 0x077CB531 ,就得到

        00000111011111001011010100110001

        -> 11011111001011010100110001000000

         相当于把 De Bruijn 序列左移了 6 位。再把这个数右移 27 位,就相当于提取出了这个数的头 5 位:

        11011111001011010100110001000000

        ->                            11011

    然后就是:

    由于 De Bruijn 序列的性质,因此当输入数字的末尾 0 个数不同时,最后得到的这个 5 位数也不同。而数组 MultiplyDeBruijnBitPosition 则相当于一个字典的功能。 11011 转回十进制是 27 ,于是我们查一查 MultiplyDeBruijnBitPosition[27] ,程序即返回 6 。

         注意到当输入数字的末尾 0 个数超过 27 个时,程序也是正确的,因为左移时低位正好是用 0 填充的。

    。。。。

  • 相关阅读:
    【9018:2221】[伪模板]可持久化线段树
    【9018:2208】可持久化线段树2
    【9018:2207】可持久化线段树1
    【POJ2187】Beauty Contest
    2017/11/22模拟赛
    2017/11/3模拟赛
    [AtCoder 2702]Fountain Walk
    [AtCoder3856]Ice Rink Game
    20170910模拟赛
    20170906模拟赛
  • 原文地址:https://www.cnblogs.com/superniaoren/p/3403855.html
Copyright © 2020-2023  润新知