神奇的位運算(bitwise trick)
在計算機中,資料都以二進位制補碼的形式儲存,根據這一特點,適當採用位運算(bitwise operation)可以很巧妙地解決問題,同時運算效率更高。時刻牢記,最大的負數是-1,在計算機中,它的儲存形式是全1。
位運算子
- 左移<< 和 右移>>
左移相當於乘以2,友誼相當於除以2.在計算機中,位運算比乘法、除法運算要快得多,所以適當採用移位運算取代乘除運算,能夠提高運算效率。右移時要注意符號位的擴充套件。
- 按位與&
&運算的特點是可以將某些位置為0。
&運算也可用於提取某些特定的位。做法是定義一個mask,mask中對應要提取的位置為1,而其它位為0,將整數與mask做&運算,就可以提取出整數中相應的位。
- 按位或|
|運算的特點是,可以將指定的位元置為1。
- 按位異或^
以下運算的規律在解決問題時可能是會經常用到的。
x^x=0
bit^1=~bit x^(-1)=~x
bit^0=bit x^0=x
- 按位取反~
~運算的特點是將0和1點到。以下規律值得牢記,此規律對正整數或負整數都成立。
-x=~x+1
一些小技巧
位運算結合在一起,惡意巧妙地解決很多問題,下面就是一些bitwise trick。
- 交換兩個int
void swap(int &x, int &y)
{
x = x ^y;
y = x^y; //(x^y)^y = x^(y^y) = x^0 = x;
x = x^y; //(x^y)^x = (x^x)^y = 0^y = y;
}
最早以前,我所知道的交換方法是下面這樣的
void swap(int &x,int &y)
{
x = x+y;
y = x-y;
x = x-y;
}
這樣在大多數情況下也是可以的,但是不及位運算。首先,撇開運算速度不說,通過加減交換最大的隱患在於,如果x和y的值都很大,那麼在計算x+y的時候,可能發生溢位,這在位運算中是絕對不會出現的。
- 判斷一個數是不是2的冪
void PowerOf2(int x)
{
if (x == 0)
return false;
return x&(x-1) == 0;
}
x&(x-1)
的作用是,使得x最右邊的1變為0,x是2的冪,意味著x中只有一個1,所以經過這一運算後,x&(x-1)等於0。
還有一個特點需要注意的是,如果x是2的冪,那麼x-1
的形式是,左邊全為0,右邊全為1。
另一個解為return x&(-x) ==x;
,但是具體怎麼解,我還不明所以。
x&(x-1)
的作用不容小覷,我們可以用它來計算整數中1的個數,並藉此來對整數做奇偶校驗。
- 計算整數中1的個數
int CountOf1(int x)
{
int count=0;
while(x)
{
count ++;
x = x&(x-1); //將x最低位的1置為0
}
return count;
}
- 計算m和n中不同的位的個數
int diffmn(int m,int n)
{
int count=0;
int x = m^n; //x中為1的位元代表了m和n中不同的位
while(x)
{
count ++;
x = x&(x-1);
}
return count;
}
- 整數加1和整數減1
int plus1(int x)
{
return -~x; //x+1 = ~(~x)+1 = -(~x)
}
int minus1(int x)
{
return ~-x; //x = -(-x) = ~(-x)+1
}
注意:通過上述方法計算整數的加1或減1,比直接計算是要慢的。不過,這兩個例子充分說明了位運算可以做的事情是很多的。
老實說,這兩個結果應該不是那麼容易記清的吧,起碼對我來說是這樣的,牢記-x=~x+1
,並記住大概的形式,應該就能夠通過簡單的變換很快得出正確的結果的吧。
- 整數反號
void revsign(int x)
{
return ~x+1; //-x = ~x+1
}
或
void revsign(int x)
{
return (x^(-1))+1; //(x^(-1))+1 = ~x+1 = -x
}
顯然,第二種方法是不如第一種方法的,還是那句話,通過推導,好好體會一下位運算的神奇效果吧。
- 判斷一個數是否是奇數
bool isodd(int x)
{
return x&1
}
偶數的判斷當然也很簡單了!
- 計算32位整數的絕對值
int abs(int x)
{
return (x&(x>>31))-(x>>31);
}
首先需要注意的是右移時會擴充套件符號位。如果x是正數,那麼x>>31=0
,毫無疑問,return
語句中的表示式結果就是x;如果x是負數,那麼x>>31
的結果就是全1,也就是我們必須牢記的-1,於是
(x^(x>>31)) - (x>>31)
=(x^(-1)) - (-1)
=~x + 1
=-x
所以,(x^(x>>31))-(x>>31)
就是我們要求的x的絕對值。
- 求a和b的最大值
int max(int a,int b)
{
int maxab = b&((a-b)>>31) | a&(~(a-b)>>31);
return maxab;
}
對於unsigned int,右移31位是一個很特別的操作,其作用是將所有的位都置為符號位的值,也就是說,正數移位後變為0,負數移位後變為-1。所以,如果a<b
,那麼(a-b)>>31
結果就是-1,而~(a-b)>>31
結果就是0,最終maxab = b
;反過來,如果a>b
,那麼(a-b)>>31
結果是0,而~(a-b)>>31
結果是-1,最終maxab = a
。
需要注意的是,此處假定在32位機器上進行操作,因此移位的位數是31,為了保證表示式的通用性,可以通過sizeof(int)*8 - 1
來確定移位的位數。
下面是另外一種解法,成立的條件是true=1,false=0
。
int max(int a,int b)
{
int maxab = a^((a^b)&(-(a<b)));
return maxab;
}
如果a<b
,那麼-(a<b)
就是-1,於是maxab = a^(a^b) = b
;如果a>b
,那麼-(a<b)
就是0,於是maxab = a
。
可以看到,兩種解法的核心都是將a、b的關係通過-1或0來間接表示,並利用表示結果作進一步的運算。
- 求a和b的最小值
int min(int a,int b)
{
int minab = a&((a-b)>>31) |b&(~(a-b)>>31);
return minab;
}
同樣的,只要滿足true=1,false=0
,也同樣可以採用第二種方法來求最小值。
int min(int a)
{
int minab = b^((a^b)&(-(a<b)));
return minab;
}
推導過程與注意事項同求最大值的解法是基本相同的,在此不做贅述。
- 計算兩個數的平均值
int mean(int x,int y)
{
return (x&y)+((x^y)>>1);
}
我們知道,計算兩個數的平均值,最直接的想法就是(x+y)/2
,表示式就是通過位運算實現了這一想法。表示式的核心思想是,將x+y
分解成兩部分,一部分是x和y相同的位,一部分是x和y不同的位,前者通過&運算完成相加(由於沒有進位,所以無需除以2),後者通過^運算完成相加,然後除以2得出平均值,最後,將兩部分相加,就是所求的結果了。
同樣的,通過位運算,可以成功規避相加溢位的問題。
在此,我們瞥見了位運算的一抹影子,你當然可以在其他很多地方領略到更多神奇之處,比如在演演算法筆記中。
相關推薦
神奇的位運算(bitwise trick)
在計算機中,資料都以二進位制補碼的形式儲存,根據這一特點,適當採用位運算(bitwise operation)可以很巧妙地解決問題,同時運算效率更高。時刻牢記,最大的負數是-1,在計算機中,它的儲存形式是全1。 位運算子 左移<< 和
神奇的位運算
eof googl 去掉 www 知識點 binarys 深入理解 網上 負數 位運算的威力 前言: 在學習Java二進制各種轉換時,發現對位運算很不熟悉,怪我基礎沒打好,更要好好學習了。然後從網上搜刮了一些位運算的小應用,然後有一些不熟悉的自己用Java代碼試了一下,發
神奇的位運算(不會不行啊。。。被坑了好多題了。。。。)
位運算應用口訣 清零取反要用與,某位置一可用或 若要取反和交換,輕輕鬆鬆用異或 移位運算 要點 1 它們都是雙目運算子,兩個運算分量都是整形,結果也是整形。 2 " < <" 左移:右邊空出的位上補0,左邊的位將從字頭擠掉,其值相當於乘2。
神奇的位運算——二進位制中1的個數
輸入一個整數,輸出該數二進位制表示中1的個數。其中負數用補碼錶示。 方法1:依次右移,使最右側的1依次消失。但需要注意負數右移後會在高位補1,需要將負數轉換為無符號數。 int NumberOf1(int n) { int cnt = 0;
zoj 3432 神奇的位運算。。
不得不說。。。這個真的很神奇。。又學會一招。 #include <stdio.h> #include <string.h> char str[8]; char str1[8]; int main(void) { int n; while (scan
神奇的C語言二十一:談談位運算
寫C程式時位運算還是很常用的,您肯定也很瞭解位運算的基本規則。下面是位運算的部分應用。 亦或^: 可以實現位取反,例如: char a = 0x0F; a ^= 0xFF; // 現在,a變成了0xF0 特性:兩個相同的數的亦或結果是0。多個數亦或滿足交換律。 有道ACM
javascript運算符——位運算符
javascript 二進制 二進制表示 ECMAScript中的所有數值都以IEEE-754 64位格式存儲,但位操作符並不直接操作64位的值,而是以32位帶符號的整數進行運算的,並且返回值也是一個32位帶符號的整數 這種位數轉換使得在對特殊的NaN和Infinity值應用位操作時,這兩個值都會
位運算的特殊運用
font 掌握 一個數 個數 pan nbsp 異或 二進制位 位數 位運算的特殊運用 位運算主要有。&。|。~,^等幾種。這幾種在編程方面能極大地優化程序,所以掌握他們勢在必行,所以就總計一下。 &: 主要能夠用來求某數的當中一個二進制位。經
位運算相關
使用 優化 lin 位數 pan ble 移位 移動 等於 位運算符/移位運算符 運算符 &運算符 操作數1的位操作數2的位&的結果位 1 1 1 1 0 0 0 1 0 0 0 0 |運算符 操作數1的位操作數2的位
位運算符
沒有 各種運算符 plain 簡單 center static lai 第一個 結果 java中有三種移位運算符 << : 左移運算符,num << 1,相當於num乘以2 >> : 右移運算符,num
JAVA 按位運算符的解釋
按位運算符 位運算 按位按位運算符Java定義了幾個按位運算符,可以將其應用於整數類型long,int,short,char和byte。按位運算符對位執行,並執行逐位運算。假設a = 60和b = 13; 現在以二進制格式,他們將如下 -a = 0011 1100b = 0000 1101--------
計蒜客15430 XOR Queries(Trie處理位運算問題)
ron 二進制 進制 插入 我們 整數 容易 位置 xor 題意: 給出一個長度為n的數組C,回答m個形式為(L, R, A, B)的詢問, 含義為存在多少個不同的數組下標k屬於[L, R]滿足C[k] XOR A >= B(式中XOR為異或運算)。 T組測試數
第12章第1講位運算符與表達式
表達式 pla image cnblogs mar mage 運算符 nbsp 技術 第12章第1講位運算符與表達式
進制轉換及位運算符
index 位運算 其余 dia 間接 cbe 不足 hot 十六進制 二進制與十進制之間的轉換 1 十進制轉二進制 方法為:十進制數除2取余法,即十進制數除2,余數為權位上的數,得到的商值繼續除2,依此步驟繼續向下運算直到商為0為止。 (具體
《劍指Offer》題目——位運算
ati 劍指offer 多少 amp nes 位運算 規則 -s n) 題目描述:輸入一個整數,輸出該數二進制表示中1的個數。其中負數用補碼表示。 題目分析:無論使用什麽方式,最關鍵的就是要考慮負數的處理方式 public class NumberOfOne {
【NOI2014】起床困難綜合癥 位運算+貪心
困難 hide 答案 none har class gif pre 位運算 這道題先求出0和-1經過處理後的答案 具體看代碼吧 #include<cstdio> #include<cstring> #include<algorithm>
位運算(&、|、^)與邏輯運算(&&、 ||)差別
邏輯與 清空 作用 一個數 總結 與運算 位或 定位 按位與 剛無意在一篇文章中看到了位運算(&、|)和邏輯運算(&&、||)的介紹。想起了自己薄弱的基礎知識。於是百度了幾把總結了下。 首先從概念上區分下,位
Java 位運算符 深入理解
clas 簡單的 二進制 ava white 最簡 bit ont 基於 在Java中存在著這樣一類操作符,是針對二進制進行操作的。它們各自是&、|、^、~、>>、<<、>>>幾個位操作符。不管是初始
nyist oj 138 找球號(二)(hash 表+位運算)
pan char s geo 運算 arch font msu 哈希 next 找球號(二) 時間限制:1000 ms | 內存限制:65535 KB 難度:5 描寫敘述 在某一國度裏流行著一種遊戲。遊戲規則為:現有一堆球中。每一個球上都有一個整數編號i
位運算
真值表 如果 嵌入 右移 左移 進制 邏輯或 位移 xe8 1.位與運算 & 註意:& 和 && 是有區別的。前面這個是位與運算,後者是邏輯與運算,也就是算數運算 1 1 1 1 0 1 0 1 看看位與運算的真值表 結論:全真為真。