位掩码的介绍与使用(小白鼠试毒问题)

1. 位掩码

  掩码(英语:Mask)在计算机学科及数字逻辑中指的是一串二进制数字,通过与目标数字的按位操作,达到屏蔽指定位而实现需求,C++中有很多运算符,常用的有与(&)、或(|)、非(~)、异或(^)、移位(<<和>>)等,部分位操作如下:

非(~) 
~ 0000 0001 
= 1111 1110

使用 
int a = 1; 
int b = ~a;

或(|) 
0000 0001 
| 0000 0010 
= 0000 0011

使用 
int a = 1; 
int b = 2; 
int c = a | b;

与(&) 
& 0000 0101 
0000 0110 
= 0000 0100

使用 
int a = 5; 
int b = 6; 
int c = a & b;

2. 位掩码使用介绍

  bitmask中每个二进制位都有两种取值情况:0或者1,我们采用一个二进制位来记录状态,每一个二进制位表达一个状态,ps:这种做法相对于枚举(仅作用于表示状态的枚举)来说会更能节省内存,这样一个int型数据,可以表达32种状态,同时,也可以对多个状态进行CRUD的操作,常用的基本操作如下: 

a&~b: 清除标志位b; 
a|b: 添加标志位b; 
a&b: 取出标志位b; 
a^b: 取出a与b的不同部分;

  举个例子,每个项目可能都会用到用户证件信息,不同的项目需要得到支持的证件不同,比如火车票使用身份证,机票可能需要身份证和护照,这里就可以用位掩码实现:

  ID_CARD = 1 << 0 转换成 0001 右边数第一个二进制位表示为支持身份证;
  PASSPORT = 1 << 1 转换成 0010 右边数第二个二进制位表示为支持护照。

3. 小白鼠试毒

  有1000瓶水,其中有一瓶有毒,小白鼠只要尝一点带毒的水24小时后就会死亡,问至少要多少只小白鼠才能在24小时鉴别出哪瓶水有毒,刚看类似的问题,一般人一定是一脸懵逼的状态的,分析一下,喝了带毒的水24小时才会毒发,目前需求是要24小时就要鉴别出来哪瓶有毒,所以小白鼠只够喝一次水的时间,完全摸不到头脑……

解决办法

  给1000个瓶分别标上如下标签(10位长度): 0000000001 (第1瓶) 0000000010 (第2瓶) 0000000011 (第3瓶) ...... 1111101000 (第1000瓶) 从编号最后1位是1的所有的瓶子里面取出1滴混在一起(比如从第一瓶,第三瓶,。。。里分别取出一滴混在一起)并标上记号为1。以此类推,从编号第一位是1的所有的瓶子里面取出1滴混在一起并标上记号为10。现在得到有10个编号的混合液,小白鼠排排站,分别标上10,9,。。。1号,并分别给它们灌上对应号码的混合液。24小时过去了,过来验尸吧: 从左到右,死了的小白鼠贴上标签1,没死的贴上0,最后得到一个序号,把这个序号换成10进制的数字,就是有毒的那瓶水的编号。 检验一下:假如第一瓶有毒,按照0000000001 (第1瓶),说明第1号混合液有毒,因此小白鼠的生死符为0000000001(编号为1的小白鼠挂了),0000000001二进制标签转换成十进制=1号瓶有毒;假如第三瓶有毒,0000000011 (第3瓶),第1号和第2号混合液有毒,因此小白鼠的生死符为00000011(编号为1,2的鼠兄弟挂了),0000000011二进制标签转换成十进制=3号瓶有毒。

解决思路

  逻辑上来解释这个问题,首先看瓶子,二进制数,10位能够表示的最大数位2的10次方-1=1023,大于1000,所以足够覆盖1000以内的二进制展示,有毒的那瓶水的二进制插入在竖直排列的数据中,混合液为获取当前二进制位上为1的所有水的混合,当小白鼠喝了over以后,说明有毒的水当前二进制位上的数值是1,否则,说明当前二进制的数值为0,所以当小白鼠最终是否over,根据转换后的十进制就能获取到有毒的那瓶水。

原文地址:https://www.cnblogs.com/jmliao/p/8535578.html