题目:对于一个字节的无符号整型变量,求其二进制表示中1的个数。
解法1:位操作法
(这里又细分成2中方法,详见代码)
<?php
$num = 6;
$count = 0;
while($num) {
$num = $num & ($num - 1); //时间复杂度:O(m) ,即无符号整形中(二进制)1的个数)
$count++;
}
/* another method:
while($num) {
if(($num & 1) != 0) { // 时间复杂度:O(log n) ,右移位运算就是除以2)
$count ++;
}
$num = $num>>1;
}
*/
var_dump($num);
var_dump($count);
?>
解法2:hamming_weight:对任何32位的无符号整形,都只需要5次移位运算,时间复杂度为常数,所以为O(1)
参考:Hamming weight高效统计整数二进制表示中1的个数(http://www.buguw.com)
参考:http://en.wikipedia.org/wiki/Hamming_weight
原理:这里运用了分治的思想,先计算每对相邻的2位中有几个1,再计算每相邻的4位中有几个1,下来8位,16位,32位,因为2^5=32,所以对于32位的机器,5条位运算语句就够了。
我画的一张图:
wiki上的图表:
Expression | Binary | Decimal | Comment |
A | 0110110010111010 | The original number | |
B = A & 01 01 01 01 01 01 01 01 | 01 00 01 00 00 01 00 00 | 1,0,1,0,0,1,0,0 | every other bit from A |
C = (A >> 1) & 01 01 01 01 01 01 01 01 | 00 01 01 00 01 01 01 01 | 0,1,1,0,1,1,1,1 | the remaining bits from A |
D = B + C | 01 01 10 00 01 10 01 01 | 1,1,2,0,1,2,1,1 | list giving # of 1s in each 2-bit piece of A |
E = D & 0011 0011 0011 0011 | 0001 0000 0010 0001 | 1,0,2,1 | every other count from D |
F = (D >> 2) & 0011 0011 0011 0011 | 0001 0010 0001 0001 | 1,2,1,1 | the remaining counts from D |
G = E + F | 0010 0010 0011 0010 | 2,2,3,2 | list giving # of 1s in each 4-bit piece of A |
H = G & 00001111 00001111 | 00000010 00000010 | 2,2 | every other count from G |
I = (G >> 4) & 00001111 00001111 | 00000010 00000011 | 2,3 | the remaining counts from G |
J = H + I | 00000100 00000101 | 4,5 | list giving # of 1s in each 8-bit piece of A |
K = J & 0000000011111111 | 0000000000000101 | 5 | every other count from J |
L = (J >> 8) & 0000000011111111 | 0000000000000100 | 4 | the remaining counts from J |
M = K + L | 0000000000001001 | 9 | the final answer |
<?php
/**
* @author:wusuopubupt
* @date:2013-10-24
* @url :http://en.wikipedia.org/wiki/Hamming_weight
*
* @return:Hamming Weight of a decimal number
*/
define('M1', hexdec("55555555")); //hex:0x55555555,binary: 0101...
define('M2', hexdec("33333333")); //binary: 0011..
define('M4', hexdec("0f0f0f0f")); //binary: 00001111.....
define('M8', hexdec("00ff00ff")); //binary: 0000000011111111 ...
define('M16',hexdec("0000ffff")); //binary: 00000000000000001111111111111111
//echo hexdec("f"); //15
//echo base_convert("f", 16, 2); //1111
function popcount($n) {
$n = ($n & M1 ) + (($n >> 1) & M1 ); //put count of each 2 bits into those 2 bits
$n = ($n & M2 ) + (($n >> 2) & M2 ); //put count of each 4 bits into those 4 bits
$n = ($n & M4 ) + (($n >> 4) & M4 ); //put count of each 8 bits into those 8 bits
$n = ($n & M8 ) + (($n >> 8) & M8 ); //put count of each 16 bits into those 16 bits
$n = ($n & M16) + (($n >> 16) & M16); //put count of each 32 bits into those 32 bits
return $n;
}
$count = popcount(255);
var_dump($count);