数字的为反转 (Reversing bit sequences)
参考:
- http://graphics.stanford.edu/~seander/bithacks.html#BitReverseObvious
- http://stackoverflow.com/questions/746171/best-algorithm-for-bit-reversal-from-msb-lsb-to-lsb-msb-in-c
- http://blog.chinaunix.net/space.php?uid=20480343&do=blog&id=1941713
- java 中 Integer实现。
与得到1的个数相似,大概也是这样几种方法:
- 一般方法:
int reserveBit1(int i) { final int n = 31; int r = i; for (i>>>=1; i!=0; i>>>=1) { r <<= 1; r |= i&1; n--; } return r<<=n; }
可以看出这个算法的主要用意就是通过遍历每个位,然后推动最某位的位不断右移,然后直到为只有了0,然后对剩下的位全部右移。这样就得到了一个反转的数。
- 查表法:
int reserveBit2(int i) { int[] bitReverseTable256= new int[256]{...}; return (bitReverseTable256[v & 0xff] << 24) | (bitReverseTable256[(v >>> 8) & 0xff] << 16) | (bitReverseTable256[(v >>> 16) & 0xff] << 8) | (bitReverseTable256[(v >>> 24) & 0xff]); }
其中bitReverseTable256对应的就是每个数的反值,这样做的操作应该是很快,但是的确以空间为代价换取的,如果是c等可以直接操作内存的语言,会相对更快。
- 分治法及其上的更多数学分析法:
int reserveBit3(int i) { i = (((i & 0xaaaaaaaa) >>> 1) | ((i & 0x55555555) << 1)); i = (((i & 0xcccccccc) >>> 2) | ((i & 0x33333333) << 2)); i = (((i & 0xf0f0f0f0) >>> 4) | ((i & 0x0f0f0f0f) << 4)); i = (((i & 0xff00ff00) >>> 8) | ((i & 0x00ff00ff) << 8)); return((i >>> 16) | (i << 16)); }
分治法的基本思想仍然是通过相邻位置的互换,然后不断扩大这种相邻范围,从而得到了最终的互换。
而java中Integer中的实现是这样的:int reverseBit4(int i)
{ i = (i & 0x55555555) << 1 | (i >>> 1) & 0x55555555; i = (i & 0x33333333) << 2 | (i >>> 2) & 0x33333333; i = (i & 0x0f0f0f0f) << 4 | (i >>> 4) & 0x0f0f0f0f; i = (i << 24) | ((i & 0xff00) << 8) | ((i >>> 8) & 0xff00) | (i >>> 24); return i; }java中的实现操作步骤上是一样的,不一样的地方有两处,通过对操作的顺序修改减少了常量,因为前三个行的常量在同行里是一样了。然后就是将第4,5行的合并,应该是一定程度上有优化。
- 其他:
另外其他算法针对一个字节的实现算法,最后要通过对每个字节的操作,实现最后的结果。
32位数的字节反转函数:
int reverseByteBy32(byte b) { return ((b * 0x0802 & 0x22110) | (b * 0x8020 & 0x88440)) * 0x10101 >> 16; }
如果是针对long型的64位数的byte反转就是:
int reverseByteBy64(byte b) { return (b * 0x0202020202L & 0x010884422010L) % 1023L; }
如果是计算一个32位数就应该是:
int reserveBit5(int i) { byte b0 = reserveByteBy32((byte)i); byte b1 = reserveByteBy32((byte)(i>>>8&0xFF)); byte b2 = reserveByteBy32((byte)(i>>>16&0xFF)); byte b3 = reserveByteBy32((byte)(i>>>24&0xFF)); return (b0<<24) |(b1<<16)|(b2<<8)|b3 }
64位的计算类似。我觉得这样方法并不快速,因为里面操作里有一个*乘或%取模操作。