zoukankan      html  css  js  c++  java
  • 位运算总结&拾遗

    JavaScript 位运算总结&拾遗

    最近补充了一些位运算的知识,深感位运算的博大精深,此文作为这个系列的总结篇,在此回顾下所学的位运算知识和应用,同时也补充下前文中没有提到的一些位运算知识。

    把一个数变为大于等于该数的最小的2的幂


    一个数为2的幂,那么该数的二进制码只有最高位是1。

    根据这个性质,我们来举个栗子,比如有数字10,转为二进制码后为:

    1 0 1 0

    我们只需把 0 bit的位置全部用1填充,然后再把该二进制码加1就ok了。而x | (x + 1)正好可以把最右边的0置为1,可是问题来了,当二进制码变成 1 1 1 1后,我们无法判断二进制码已经全是1了,继续操作的话会变成1 1 1 1 1,于是,该法失败...

    我们可以采用类似迭代的方法,又有点分组的意思。因为最高位肯定是1,我们把init的数右移一位,和原数作与运算,这样就能把次高位也置为1,然后继续右移,这时最前面两位都是1了,右移两位后,做与运算,这时前四位都是1了:

    function change2Pow(n) {
      if(!(n & (n-1)) && n > 0) return n;
      n |= n >>> 1;
      n |= n >>> 2;
      n |= n >>> 4;
      n |= n >>> 8;
      n |= n >>> 16;
      return n + 1;
    }

    计算二进制中1的个数


    leetcode中有一道这样的题目-Number of 1 Bits,正好拿来验证程序的正确性。

    常规解法不用多说,直接上代码:

    var hammingWeight = function(n) {
      var ans = 0;
      while (n) {
        ans += n & 1;
        n >>>= 1;
      }
      return ans;
    };

    因为题目中有说是unsigned int32的整数,所以要用有符号右移>>>来操作符号位。

    位运算解法和求逆序一样,也采用分组的思想。

    我们以16位下的12345举例(6个1),先写出它的二进制码表示:

    0011000000111001

    第一步:每两个为一组,组内高低位相加

    00 10 00 00 00 10 01 01

    第二步:每四个为一组,组内高低位相加

    0010 0000 0010 0010

    第三步:每8个为一组,组内高低位相加

    00000010 00000100

    第四步,每16个为1组,组内高低位相加

    0000000000000110

    最后得到的数字6即为12345二进制码中1的个数。而实际中,因为int32是32位的,所以一共要进行5步。求解思路和求逆序类似,逆序是要交换,所以要分别左移右移,而求1的个数是相加,所以只需移动一次就够了。

    完整代码:

    var hammingWeight = function(n) {
      n = ((n & 0xAAAAAAAA) >>> 1) + (n & 0x55555555);
      n = ((n & 0xCCCCCCCC) >>> 2) + (n & 0x33333333);
      n = ((n & 0xF0F0F0F0) >>> 4) + (n & 0x0F0F0F0F);
      n = ((n & 0xFF00FF00) >>> 8) + (n & 0x00FF00FF);
      n = ((n & 0xFFFF0000) >>> 16) + (n & 0x0000FFFF);
      return n;
    };

    因为是unsigned int32,所以要用>>>如前所述。

    再次击败100%的JavaScript code...

    计算二进制中1的个数的奇偶性


    我们可以先计算1的个数,然后再判断奇偶。当然既然作为一道独立的题目,肯定有更简便的方法。

    整个过程可以用分治来解。第1步统计相邻2位的1的个数奇偶性,保存到这2位的低位中。第2步统计相邻4位的1的个数奇偶性,保存到这4位的低位中。……第5步统计相邻2位的1的个数奇偶性,保存到这32位的低位中,即x的最低位。

    function bit1OddEven(x){ //奇数个为1,偶数个为0
      x ^= x >>> 1;    //相邻 2位中1的奇偶性
      x ^= x >>> 2;    //相邻 4位中1的奇偶性
      x ^= x >>> 4;    //相邻 8位中1的奇偶性
      x ^= x >>> 8;    //相邻16位中1的奇偶性
      x ^= x >>> 16;   //相邻32位中1的奇偶性
      return x & 1;   
    }

    统计二进制前导0、末尾0的个数


    先排除为0的特殊情况。然后先看前16位是否全0,如果全0,增加计数,并把这个数左移16位删除已经计数的16个0。然后看前8位是否全0。一直到只剩一位时可以直接计算。整个过程的核心是二分思想

    统计末尾0的个数时思想类似,只是变成了统计后面16位、8位等是否全0。

    function countLeading0(x) {
      if (!x) return 32;
      var n = 1;
      if ((x >>> 16) == 0) n += 16, x <<= 16;
      if ((x >>> 24) == 0) n += 8, x <<= 8;
      if ((x >>> 28) == 0) n += 4, x <<= 4;
      if ((x >>> 30) == 0) n += 2, x <<= 2;
      return n - (x >>> 31);
    }
    
    
    function countTrailing0(x) {
      if (!x) return 32;
      var n = 1;
      if ((x << 16) == 0) n += 16, x >>>= 16;
      if ((x << 24) == 0) n += 8, x >>>= 8;
      if ((x << 28) == 0) n += 4, x >>>= 4;
      if ((x << 30) == 0) n += 2, x >>>= 2;
      return n - (x & 0x01);
    }

    当然计算末尾0的个数,我们也可以这样:

    function countTrailing0(a) {
      return Math.log(a & (-a)) / Math.LN2;
    }

    这个系列暂时结束了,但我知道对于位运算的学习,这只是起点。

    附位运算系列目录

    1. javascript 位运算
    2. 常用位运算整理
    3. 【位运算经典应用】 标志位与掩码
    4. 【位运算经典应用】 N皇后问题
    5. 【位运算经典应用】 求二进制逆序
    6. 【位运算经典应用】 寻找那个唯一的数
    7. leetcode - 位运算题目汇总(上)
    8. leetcode - 位运算题目汇总(下)
    9. JavaScript 位运算总结&拾遗
     
  • 相关阅读:
    幂不等式
    一个对数级数的求和
    数列极限
    求幂级数的和函数
    证明整数为平方数
    java获取当前时间戳的方法
    《编程小白的第一本python入门书》笔记 二
    《编程小白的第一本python入门书》笔记 一
    python班级群中的问题记录-2016.12.30
    python班级群中的问题记录-2016.12.22
  • 原文地址:https://www.cnblogs.com/Leo_wl/p/4803839.html
Copyright © 2011-2022 走看看