zoukankan      html  css  js  c++  java
  • 【编程之美学习笔记】2.1求二进制数中1的个数

    问题:对于一个字节(8bit)的无符号整型变量,求其二进制表示中“1”的个数,要求算法的执行效率尽可能高。

    解法一:除、余操作

    我们知道,对于二进制操作,除以一个2,原来的数字将会减少一个0,如果除的过程中有余,那么就表示当前位置有一个1,所以可通过相除和判断余数的值来分析。

    【时间复杂度O(log2v),log2v为二进制数的位数,空间复杂度O(1)】

    1 int Count(int v){
    2     int num = 0;
    3     while(v){
    4         if(v % 2 == 1) num++;
    5         v = v/ 2;
    6     }
    7     return num;
    8 }
    View Code

     解法二:位操作

    我们也知道,向右移位操作同样可以达到相除的目的,对于如何判断移位之后是否有1存在,可以在右移位过程中,将这个八位数字与00000001进行“与”操作,如果结果为1,则表示当前八位数的最后一位为1,否则为0.

    【时间复杂度O(log2v),空间复杂度O(1)】

    1 int Count(int v){
    2     int num = 0;
    3     while(v){
    4         num += v & 0x01;
    5         v >>= 1;
    6     }
    7     return num;
    8 }
    View Code

     解法三:高效位操作

    我们发现,前面两种操作,对于一些数,比如10000000,就会浪费大量时间在无用的0上,那么能不能让算法的复杂度只与“1”的个数有关呢?

    通过观察发现有个规律:使 v = v & (v - 1),可以去除数 v 的二进制中最后一个1,

    比如 01000010 & (01000010 - 00000001)= 01000000 & 01000001 = 01000000

          01000000 & (01000000 - 00000001) = 01000000 & 00111111 = 0

    【时间复杂度O(M),即二进制中“1”的个数,空间复杂度O(1)】

    1 int Count(int v){
    2     int num = 0;
    3     while(v){
    4         v &= (v-1);
    5         num++;
    6     }
    7     return num;
    8 }
    View Code

     解法四:分支操作

    罗列出0~255的情况,并使用分支操作得到答案。但是这个方法看似直接,执行效率却可能低于上述解法,因为分支语句的执行情况要看具体字节的值,例如,若v = 0,在第一个case就能得出答案,但是若v = 255,则要比较255次!因此该解法并不可取,但提供了个很好的思路,即空间换时间的方法。

     1 int Count(int v){
     2     int num = 0;
     3     switch(v){
     4         case 0x0: num = 0; break;
     5         case 0x1:
     6         case 0x2:
     7         case 0x4:
     8         case 0x8:
     9         case 0x10:
    10         case 0x20:
    11         case 0x40:
    12         case 0x80: num = 1; break;
    13         case 0x3:
    14         case 0x6:
    15         case 0xc:
    16         case 0x18:
    17         case 0x30:
    18         case 0x60:
    19         case 0xc0: num = 2; break;
    20         //...
    21     }
    22     return num;
    23 }*
    View Code

     解法五:查表法

    典型的空间换时间的算法,应题目要求,只要把0~255中“1”的个数直接存储在数组中即可

    【时间复杂度O(1),空间复杂度O(N)】

     1 int CountTable[256]={
     2         0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4,
     3         1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5,
     4         1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5,
     5         2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
     6         1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5,
     7         2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
     8         2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
     9         3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7,
    10         1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5,
    11         2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
    12         2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
    13         3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7,
    14         2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
    15         3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7,
    16         3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7,
    17         4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8,
    18 };
    19 int Count(int v){
    20     //check parameter
    21     return CountTable[v];
    22 }
    View Code

     网上其他解法:以32位整型为例

    解法六:归并法

    相邻两两相加,然后四个四个相加,以此类推。最后就能得出各位之和。

     1 int Count(int v){
     2     int num = v;
     3     int a = 0x55555555;//01010101010101010101010101010101
     4                     //用于相邻两位相加
     5     int b = 0x33333333;//00110011001100110011001100110011
     6                     //用于相邻四位相加
     7     int c = 0x0f0f0f0f;//00001111000011110000111100001111
     8     int d = 0x00ff00ff;//00000000111111110000000011111111
     9     int e = 0x0000ffff;//00000000000000001111111111111111
    10     num = (num& a) + ((num >> 1)& a);
    11     num = (num& b) + ((num >> 2)& b);
    12     num = (num& c) + ((num >> 4)& c);
    13     num = (num& d) + ((num >> 8)& d);
    14     num = (num& e) + ((num >> 16)& e);
    15     return num;
    16 }
    View Code

    解法七:HAKMEN算法

    首先将二进制各位三个一组,求出每组中1的个数,然后相邻两组归并,得到六个一组的1的个数,最后巧妙的用除63取余得到结果。

    因为2^6 = 64,所以v_0 + v_1 * 64 + v_2 * 64 * 64 = v_0 + v_1 + v_2(mod 63),等号表示同余。(表示现在还不是很理解orz)

     1 int Count(unsigned v){
     2     unsigned n;
     3     n = (v >> 1)& 033333333333;
     4     v = v - n;
     5     n = (n >> 1)& 033333333333;
     6     v = v - n;
     7     v = (v + (v >> 3))& 030707070707;
     8     v = v % 63;
     9     return v;
    10 }
    View Code

     扩展:由解法三的规律可以轻松判断一个数是否是2的幂。

    1 int powof2(int v){
    2     return (v & (v-1)) == 0;
    3 }
    View Code

     扩展问题:

    1.如果变量是32位的DWORD,你会使用上述的哪一个算法,或者改进哪一个算法?

    解法三,六,七。(解法五不合适,因为要求的数组太大了:2^32*sizeof(int))

    2.给定两个正整数(二进制形式表示)A和B,问把A变为B需要改变多少位(bit)?也就是说,整数A和B的二进制表示中有多少位是不同的?

    答案即A^B 中 1 的个数。

  • 相关阅读:
    SpringBlade 端口占用 Web server failed to start. Port 80 was already in use.
    SpringBlade 找不到或无法加载主类 springboot.Application
    Java idea 常用快捷键
    Java Velocity
    个人 一些需求
    Java MyBatis-Plus 基本使用
    Java Spring Initializr 创建的项目 包是一层一层的,需要隐藏一下空包
    MapReduce之自定义OutputFormat
    数据链路层之PPP协议
    MapReduce之GroupingComparator分组(辅助排序、二次排序)
  • 原文地址:https://www.cnblogs.com/GraceSkyer/p/5836718.html
Copyright © 2011-2022 走看看