操作二進位制的(記錄)
問題1
給你一個值(char,int....),當然他們在記憶體中都是二進位制存在的,怎麼反轉它們的值呢?
int型
比如 (0x00006CBA) 0000 0000 0000 0000 0110 1100 1011 1010 (27834)
二進位制和十六進位制不熟,直接計算器(囧)
這個數比較小,只是舉個例子, 我們直接用眼睛瞅可以得出:
0101 1101 0011 0110 0000 0000 0000 0000 => 1563820032 (0x5D360000)
顯然,容易瞅錯,要是別人問你,或者你面試,面試官難為你,你這不就笑話了麼....還得隨帶個計算器...
扯淡哈.....
這就涉及到一個演算法,叫做 分治法
我的粗糙理解就是,取最小的問題解決,然後擴大(一般是倍數),直到解決問題,在實現上遞迴式比較常用的。
扯遠...
用到這個題上就是,你想倒轉,那我從最小組的倒轉,自己和自己換沒意義,那我就相鄰的兩個換。各兩個互換後,他倆成為一組在和相鄰的別的組互換....當只有兩組的時候,他倆一換,全部倒騰完了...
參見一個小夥伴的講解:
當然 出現了這麼幾個神奇的二進位制數
0x5555555555555555(long) (0101010101010101010101010101010101010101010101010101010101010101)
太長了,一下用x86int來解說:
0x55555555 = (01010101010101010101010101010101) (1431655765)如果你的偵錯程式好你可以看到整數。
0xAAAAAAAA = (10101010101010101010101010101010) (2863311530)
0x33333333 = (00110011001100110011001100110011)
0xCCCCCCCC =(11001100110011001100110011001100)
0x0f0f0f0f
0xf0f0f0f0
0x00ff00ff
0xff00ff00
0x0000ffff
0xffff0000 以下自行二進位制............
看著16進位制好怪,無非就是二進位制各位不同,就是每一位和其餘都不相同意思,由於是二進位制,所以一定是10,擴大組就變成
1100 1111000 .... 依次
用幹啥呢,用來反轉的
num = ((num >> 1) & 0x55555555) | ((num << 1) & 0xAAAAAAAA);
當引數 v >> 右移1,與(0x05) 0101 相與(留奇),得到 把偶數位放到奇數上,把偶數上位變零
當引數 v << 左移1,與(0x0A) 1010 相與(留偶),得到 把奇數位放到偶數上,把奇數上位變零
然後相加(+)或者相或(|),就達到了兩位兩位反轉的目的。
然後利用遞迴原理,擴大分組, << 右移2 >>左移 2 , 與 0011(留奇) 1100(留偶)
<< 右移4 >>左移 4 , 與 0001111(留奇) 11110000(留偶)
至於多大取決於你的位元組長度,如果是int,那麼 16 就可以了 32 位麼,最後一次到移位16 就完成目標啦,
當然你要是long或者更大,那就看你自己平臺了,long 應為 64 位,八個位元組,那就在>>32吧。
所以有一下演算法:類C實現(int)
private static long Number5(uint num)
{
num = ((num >> 1) & 0x55555555) | ((num << 1) & 0xaaaaaaaa);
num = ((num >> 2) & 0x33333333) | ((num << 2) & 0xcccccccc);
num = ((num >> 4) & 0x0f0f0f0f) | ((num << 4) & 0xf0f0f0f0);
num = ((num >> 8) & 0x00ff00ff) | ((num << 8) & 0xff00ff00);
num = ((num >> 16) & 0x0000ffff) | ((num << 16) & 0xffff0000);
return num;
}
神奇有沒有,對於不熟悉二進位制的上層程式設計師,不研究一會真不知道他們整的是啥....
問題2
查出一個int型別數二進位制有多少個1。
我個菜鳥,絞盡腦汁:
private static int Number(int figure)
{
var cout = 0;
var temp = figure;
while (temp != 0)
{
var c = (temp & 1);
if (c == 1) cout++;
temp = temp >> 1;
}
return cout;
}
然後看見更好的演算法...
private static int Number(int figture)
{
int num = 0;
while (figture > 0)
{
figture &= (figture - 1);
num++;
}
return num;
}
然後分治法又出來了...
private static int Number2(int a)
{
a = (a & 0x55555555) + ((a >> 1) & 0x55555555);
a = (a & 0x33333333) + ((a >> 2) & 0x33333333);
a = (a & 0x0f0f0f0f) + ((a >> 4) & 0x0f0f0f0f);
a = (a & 0x00ff00ff) + ((a >> 8) & 0x00ff00ff);
a = (a & 0x0000ffff) + ((a >> 16) & 0x0000ffff);
return a;
}
看看小夥伴的文章:
然後這個我也看懂了:
private static int Number4(long x)
{
x -= (x >> 1) & 0x5555555555555555;
x = (x & 0x3333333333333333) + ((x >> 2) & 0x3333333333333333);
x = (x + (x >> 4)) & 0x0f0f0f0f0f0f0f0f;
x += x >> 8;
x += x >> 16;
x += x >> 32;
return x &0xff;
}
然後看不懂了...
int static Number5(long x) {
x -= (x >> 1) & m1;
x = (x & m2) + ((x >> 2) & m2);
x = (x + (x >> 4)) & m4;
return (x * h01)>>56; //returns left 8 bits of x + (x<<8) + (x<<16) + (x<<24) + ...
}
行吧,程式猿的世界,總是有看不懂程式碼,先記下來,沒事再燒燒腦....