《算法心得》一點整理

最近在圖書館看到本神書《算法心得:高效算法的奧祕》,主要講解計算機算法的,強調編譯器優化和計算機體系結構設計的。雖然看的不大懂,但還是給自己增長了見識和知識。少許整理些自己感興趣的算法,以備後續溫故知新。

1. 操作最右邊的位元
a. 將字組中值爲1且最靠右的位元置0,如果不存在值爲1的位元,則全部結果爲0(例如 0101 1110 => 0101 1100):
x & (x-1)
這個操作可以判斷無符號證書是不是2的冪或者0.

b. 將字組中值爲0且最靠右的位元置1,如果不存在值爲0的位元,則全部結果的每一位爲1(例如 1010 0111 => 1010 1111):
x | (x+1)
這個操作可以判斷無符號證書是不是2的冪或者0.

c. 將字組尾部的1全部變成0,如果尾部沒有1,則不變(例如 1010 0111 => 1010 0000)
x & (x+1)
這個操作可以判斷無符號證書是不是2^n-1或者0.

d. 將字組尾部的0全部變成1,如果尾部沒有0,則不變(例如 1010 1000 => 1010 1111)
x | (x-1)

e. 將字組中值爲0且最靠右的位元置1,其餘位置0,如果不存在值爲0的位元,則全部結果的每一位爲0(例如 1010 0111 => 0000 1000):
~x & (x+1)

f. 將字組中值爲1且最靠右的位元置0,其餘位置1,如果不存在值爲1的位元,則全部結果的每一位爲1(例如 1010 1000 => 1111 0111):
~x | (x-1)

g. 將字尾部所有0的位元變成1,其餘位置0,如果不存在值爲0的位元,則全部結果的每一位爲0(例如 0101 1000 => 0000 0111):
~x & (x-1) 或 ~(x | -x)

h. 將字尾部所有1的位元變成0,其餘位置1,如果不存在值爲1的位元,則全部結果的每一位爲1(例如 1010 0111 => 1111 1000):
~x | (x+1)

i. 將字組中值爲1且最靠右的位元保留,其餘位置0,如果不存在值爲1的位元,則全部結果的每一位爲0(例如 0101  1000 => 0000 1000):
x & (-x)

j. 將字組中值爲1且最靠右的位元,以及其右方所有值爲0的位元都置爲1,其餘位置0,如果不存在值爲1的位元,則全部結果的每一位爲1,而當x尾部沒有值爲0的位元是,運算結果是1(例如 0101  1000 => 0000 1111):
x ^ (x-1)

k. 將字組中值爲0且最靠右的位元,以及其右方所有值爲1的位元都置爲1,其餘位置0,如果不存在值爲0的位元,則全部結果的每一位爲1,而當x尾部沒有值爲0的位元是,運算結果是1(例如 0101  0111 => 0000 1111):
x ^ (x+1)

l. 將字組右側連續出現且值爲1的位元置爲0,(例如 0101 1100 => 0100 0000):
( ( x | (x-1)) +1 ) & x

m. 將字組右側連續出現且值爲0的位元置爲1,(例如 0100 0111 => 0111 1111):
( ( x & (x+1)) -1 ) | x

根據上面的知識規律,下面這道題就可以這樣做:
例如:求給定數 x 往上增加最近的2^n 的值。如給定5時,求出8,給定4時,求出4.
unsigned int fun1(unsigned int x)
{
x = x<<1;
while ( x & (x-1) )
     x = x & (x-1);
return x; 
}

unsigned int fun2(unsigned int x)
{
while ( ( ( x | (x-1) ) +1 ) & x )
     x = ( ( x | (x-1) ) +1 ) & x ;
return x<<1;
}

2. 德摩根定律
~(x&y) = ~x | ~y
~(x|y) = ~x & ~y
~(x+y) = ~x - y
~(x-y) = ~x + y
~-x = x-1

3. 位操作新式用法
給定一個數x,然後找出下一個比它大的數字y,該數字y中值爲1的位元數與x中的相同。這題可以用在找出元素個數爲某一定值的全部子集的算法中。
思路:給定一個表示子集位串的字組x,首先要找到連續出現在x右側且值爲1的一組位元,然後將該值“加1”,再把原來後面跟着的哪些0 補上。舉例來說,如果帶計算的位串是xxx0 1111 0000,那麼結果就應該是xxx1 0000 0111,其中xxx這三個位元值不限。該算法首先定義 s = x&-x,算出s == 0000 0001 0000,這樣就找到x中最小的那個1。然後把它與x相加,把兩書只和xxx1 0000 0000存放在r 中。此時結果中的1個位元已經計算好了,想求出其他位元,還需要把位串中剩下的n-1個“1”移到右側。要向移動到右側,首次要計算r和x的異或,在本例中就是0001 1111 0000.
上面那個值中“1”的個數太多了,而且沒有靠右對齊。爲了解決此問題,要將它與s相除,這樣就可以把哪些“1”靠右對齊了,除之前還要先向右移動2位,以便丟棄那2個多餘的位元。此結果與r取或,就得到最終答案了。
用C語言實現就是
unsigned fun3(insigned x)
{
unsigned smallest, ripple, ones;
                               //x=xxx0 1111 0000
smallest = x & -x;             //  xxx0 0001 0000
ripple = x +smallest;          //  xxx1 0000 0000
ones = x ^ ripple;             //  xxx1 1111 0000
ones = ( ones>>2 )/smallest;   //  xxx0 0000 0111
return ripple | ones;          //  xxx1 0000 0111
}

4. 結合邏輯操作的加減運算
-x = ~x+1
-~x = x+1
~-x = x-1
x^y = ( x|y ) - ( x&y )
x + y = ( x^y ) + 2( x&y )
         = ( x|y ) + ( x&y )
x - y  = ( x^y ) - 2( ~x&y )
         = ( x&~y ) - ( ~x&y )
其中 x + y = ( x^y ) + 2( x&y ) 是先對兩數做不進位加法x^y,然後再補上進位。
x - y  = ( x^y ) - 2( ~x&y ) 是先對兩數進行不進位剪髮x^y,然後再把結尾的位從結果中減去。

5. 與常數相乘
要將x乘以13(二進制1101),可執行下述操作
t1 = x<<2;
t2 = x<<3;
r = x + t1 + t2;

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章