现有一问题:如何快速判断某数字是否是2的幂?用程序实现、当然效率越快越好。
首先想到的当然是将不断环循环/2,看最终是否=1,若是则是;若否则否
其次想到,可以将上述/2改为位运算(右移>>1相当于/2)
再次思索,可以这样:
1 /// <summary> 2 /// 判断num数字是否是2的幂 3 /// </summary> 4 /// <param name="num">待判断数据</param> 5 /// <returns></returns> 6 public bool JudgeIs2Power(long num) 7 { 8 return (num & (num - 1)) == 0; 9 //证明: 10 //①先证明为什么当num是2的幂时,运算值一定是0 11 //因为num是2的幂,则num的值二进制一定形如1...0..0(首位是1,其后不含0或含N个0,N>=1) 12 //所以num-1的值二进制必然是1..1..1,即含有多个1或者不含1(即为0) 13 //所以num & (num-1)一定是0 14 //②再证明为什么当num补是2的幂时,运算值一定不是0 15 //因为num不是2的幂,则num转化为二进制一定形如1...1...(首位是1,其后含N个1,N>=1) 16 //所以num-1的值二进制必然是1...(后面任意,不做讨论)...(首位是1,其后值任意) 17 //所以num & (num-1)的值二进制首位必定是1,<>0 18 //③排除0和负数。若搞不清楚,可在①②证明里去除1的情况、在此处单独讨论 19 //综上,当且仅当num是2的幂时,运算值一定是0,即(num & (num - 1)) == 0可作为判断num是否是2的幂的方法。因为该方法是位运算、减1本身很快,所以计算速度原理上会非常快。 20 }
由此问题想到,位运算虽然不常用、但是因为运算效率之高、和计算机硬件联系密切、有若干有用的数学特性,还是得了解下。事物必须先了解,而后有问题才能想到;如果连了解、听说都做不到,那遇到问题也想不到那边去。
再来说一个,用1个数字的二进制方式来获取内部多个状态位:
现有一订单自动化定时运行程序:一个订单下单后,程序需并行或串行处理订单的若干任务,比如说超过30min未支付则自动取消、已支付6H还没有op处理、已超过出游时间还没有出游。。。这样的话,一个订单需要N个标识状态字段(是否已运行超时出游程序、是否已运行超时自动取消程序),每个字段都得标识:0-未运行,1-已运行-出错,2-已运行-成果,3-其他;可能各自还需要一个详细原因字段。 这样程序非常冗余。
优化办法:新建2字段 OrderFlags(long),OrderFlagsDesc(string)。
OrderFlags:每4位标志一种状态,比如第1-4位表示超时订单job状态、5-8位表示投保job状态。每个状态位里自定义0000,0001,0010代表什么意思(最好统一)。当然也可以每3位表示一种job状态。 原理上long类型已经可以有60位二进制=》即使4位表示一个栏目,可以表示10+个job状态。读取时可以分别>>4*N位,然后&15(1111)即可获取每个状态位的具体细节状态。
OrderFlagsDesc:结构 {"Reasons":[{"NodeTypeId":1,"BreakReason":"该订单未超时取消订单,运行正常"},{"NodeTypeId":2,"BreakReason":"投保自动化失败,游客身份证错误,身份证XXXX"}]}