摘要:相信大家都知道二進(jìn)制數(shù)按位運(yùn)算的規(guī)則來看一些簡單的例子單純的二進(jìn)制位之間的這些運(yùn)算相當(dāng)簡單,但對(duì)我們實(shí)際編程并沒有直接幫助,因?yàn)榫幊踢^程中需要的經(jīng)常是數(shù)字間的運(yùn)算,比如。
先來看LeetCode上的Divide Two Integers題目要求:
Divide two integers without using multiplication, division and mod operator.
就是說不用乘法,除法,求模運(yùn)算來實(shí)現(xiàn)兩個(gè)整數(shù)相除,看起來很簡單,我可以用除數(shù)減去被除數(shù),直到除數(shù)小于被除數(shù),記錄減法操作的次數(shù)即可。假設(shè)是計(jì)算m/n,那么時(shí)間復(fù)雜度為O(m/n)。用Python實(shí)現(xiàn)后,Time Limit Exceeded。我們考慮有沒有更加優(yōu)化的算法呢?
如果很難想得到,那就先來回憶下二進(jìn)制數(shù)按位運(yùn)算的一些知識(shí)。
二進(jìn)制數(shù)按位運(yùn)算計(jì)算機(jī)里面所有數(shù)據(jù)都存儲(chǔ)為0,1串,所有的運(yùn)算歸根到底都轉(zhuǎn)為二進(jìn)制數(shù)的運(yùn)算。相信大家都知道二進(jìn)制數(shù)按位運(yùn)算的規(guī)則:
來看一些簡單的例子:
1010 & 1100 = 1000 1010 | 1100 = 1110 1010 ^ 1100 = 0110 1010 << 2 = 101000 1010 >> 2 = 10 ~1010 = 0101
單純的二進(jìn)制位之間的這些運(yùn)算相當(dāng)簡單,但對(duì)我們實(shí)際編程并沒有直接幫助,因?yàn)榫幊踢^程中需要的經(jīng)常是數(shù)字間的運(yùn)算,比如 5*(2^4) 。真的是這樣嗎?接著往下看!
計(jì)算機(jī)中數(shù)字的存儲(chǔ)方式我們都知道計(jì)算機(jī)中萬物皆為0、1,將萬物變?yōu)?、1的過程叫做編碼,這里我們只討論將數(shù)字編碼為0、1的過程。
計(jì)算機(jī)中對(duì)數(shù)字的表示有三種方式:原碼,反碼,補(bǔ)碼:
原碼表示法在數(shù)值前面增加了一位符號(hào)位(即最高位為符號(hào)位):正數(shù)該位為0,負(fù)數(shù)該位為1。比如十進(jìn)制3如果用8個(gè)二進(jìn)制位來表示就是 00000011, -3就是 10000011。
反碼表示方法:正數(shù)的反碼是其本身;負(fù)數(shù)的反碼是在其原碼的基礎(chǔ)上,符號(hào)位不變,其余各個(gè)位取反。
補(bǔ)碼表示方法:正數(shù)的補(bǔ)碼是其本身;負(fù)數(shù)的補(bǔ)碼是在其原碼的基礎(chǔ)上,符號(hào)位不變,其余各位取反,最后+1。 (即在反碼的基礎(chǔ)上+1)
原碼容易被人腦直接識(shí)別并用于計(jì)算,但是對(duì)于計(jì)算機(jī)來說并不友好。所以在計(jì)算機(jī)系統(tǒng)中,數(shù)值一律用補(bǔ)碼來表示、運(yùn)算和存儲(chǔ)。使用補(bǔ)碼,可以將符號(hào)位和數(shù)值域統(tǒng)一處理,將加法和減法統(tǒng)一處理。此外,補(bǔ)碼與原碼相互轉(zhuǎn)換,其運(yùn)算過程是相同的,不需要額外的硬件電路。詳細(xì)的解釋可以參考原碼, 反碼, 補(bǔ)碼詳解。
數(shù)字的按位運(yùn)算計(jì)算機(jī)中數(shù)字存儲(chǔ)為補(bǔ)碼形式,各個(gè)數(shù)之間的運(yùn)算也是對(duì)它們的補(bǔ)碼做運(yùn)算,而且得到的結(jié)果也是補(bǔ)碼,如下圖:
各種編程語言都提供了對(duì)補(bǔ)碼的二進(jìn)制位直接進(jìn)行運(yùn)算的方法。以Python為例:
>>> 0b1010 & 0b1100 8 #1000 >>> 0b1010 | 0b1100 14 #1110 >>> 0b1010 ^ 0b1100 6 #0110 >>> 0b1010 << 2 40 #101000 >>> 0b1010 >> 2 2 #10 >>> ~0b1010 -11 #10000000 00000000 00000000 00001011 >>> type(0b1010)
上面0b開頭的0、1串表示整型數(shù)字,在32位操作系統(tǒng)中,Python中int類型一般占32個(gè)二進(jìn)制位,以最后一個(gè)求反運(yùn)算為例子,1010的補(bǔ)碼為
00000000 00000000 00000000 00001010
求反操作后為:
11111111 11111111 11111111 11110101
即為-11(原碼為:10000000 00000000 00000000 00001011)的補(bǔ)碼。(對(duì)一個(gè)數(shù)的補(bǔ)碼求補(bǔ)碼即可得到該數(shù)的原碼)
另辟蹊徑的按位運(yùn)算那么按位運(yùn)算在實(shí)際編程中可以扮演哪些角色呢?簡單點(diǎn)地,可以用來判斷奇、偶數(shù):num & 0x1,或者對(duì)一個(gè)數(shù)變換符號(hào):~num + 1;復(fù)雜點(diǎn)的可以用來交換兩個(gè)數(shù),求絕對(duì)值等等。
> 不用額外的變量實(shí)現(xiàn)兩個(gè)數(shù)字互換。
def swap(num_1, num_2): num_1 ^= num_2 num_2 ^= num_1 num_1 ^= num_2 return num_1, num_2
證明很簡單,我們只需要明白異或運(yùn)算滿足下面規(guī)律:
0^a = a;
a^a = 0;
a^b^c = a^c^b;
巧妙運(yùn)用異或可以高效解決很多問題,比如 找出數(shù)組中只出現(xiàn)了一次的數(shù)(除了一個(gè)數(shù)只出現(xiàn)一次外,其他數(shù)都是出現(xiàn)兩次),以及它的升級(jí)版:數(shù)組中只出現(xiàn)1次的兩個(gè)數(shù)字(百度面試題)。
> 不用判斷語句來實(shí)現(xiàn)求絕對(duì)值。
def bit_abs(num): negative = num >> 31 return (num ^ negative) - negative
這里假設(shè)程序運(yùn)行環(huán)境中操作系統(tǒng)為32位,int型整數(shù)(不考慮整數(shù)溢出)用32位存儲(chǔ),因此可以用 num>>31 取出符號(hào)位,后面的部分留給大伙證明。
Leetcode 題目思路回到文章開始提到的題目中,我們對(duì)除數(shù)減去被除數(shù)的過程稍作改進(jìn)。假設(shè)求m/n,我們不一次次的 m-n,而是找到n的一個(gè)倍數(shù),使得m-x*n盡可能小,這樣能減少循環(huán)減法的次數(shù),進(jìn)而提高效率。我們知道在按位操作中,n << k相當(dāng)于 n * 2^k,因此可以用2^k 來找合適的x。
我們需要這樣的一個(gè)數(shù)字k,它使得n 2^k < m < n 2^(k+1), 然后用m - n*2^k,得到新的m"。再找相應(yīng)的k",做減法,如此循環(huán)即可。代碼放在這里。
原文地址
相關(guān)閱讀
Pyhon wiki: BitwiseOperators
位操作基礎(chǔ)篇之位操作全面總結(jié)
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/37621.html
摘要:中的數(shù)字也是按照的標(biāo)準(zhǔn)存儲(chǔ)的,按位存儲(chǔ),但是操作符不會(huì)直接去操作位,會(huì)將位數(shù)轉(zhuǎn)換成位整數(shù)操作,完成運(yùn)算后再轉(zhuǎn)換成位,這個(gè)位對(duì)用戶來說是透明的。雖然經(jīng)常寫,但是還是對(duì)一些按位運(yùn)算比較迷茫。 javascript中的數(shù)字也是按照IEEE754的標(biāo)準(zhǔn)存儲(chǔ)的,按64位存儲(chǔ),但是操作符不會(huì)直接去操作64位,會(huì)將64位數(shù)轉(zhuǎn)換成32位整數(shù)操作,完成運(yùn)算后再轉(zhuǎn)換成64位,這個(gè)64位對(duì)用戶來說是透明的。...
摘要:檢查設(shè)定位操作符還有一些其他有用的位屏蔽應(yīng)用。請(qǐng)注意,位掩碼中的位將有效地關(guān)閉十進(jìn)制數(shù)中的相應(yīng)位,因?yàn)椤? 原文標(biāo)題:Interesting use cases for JavaScript bitwise operators原文地址:https://blog.logrocket.com/in... 本文首發(fā)于公眾號(hào):符合預(yù)期的CoyPan JavaScript提供了幾種運(yùn)算符,可以對(duì)...
摘要:位運(yùn)算符位運(yùn)算符與邏輯運(yùn)算符類似,但是位運(yùn)算符是對(duì)每一位進(jìn)行計(jì)算。上面說到的按位取反加,就可以寫成移位運(yùn)算符右移與無符號(hào)右移相似,是將整數(shù)所有的位向右移動(dòng)位,拋棄個(gè)低位??粘鰜淼牡臀挥玫淖罡呶恢笛a(bǔ)全。 定點(diǎn)數(shù)據(jù)再計(jì)算機(jī)中的表示方法 例如一個(gè)整數(shù)類型(int)的數(shù)據(jù)在內(nèi)存中占用了32位。通俗的講就是在內(nèi)存中挖了32個(gè)坑,每一個(gè)坑里可以放一個(gè)0或者1. 00000000 11111111 ...
摘要:例如,十進(jìn)制數(shù),用二進(jìn)制表示則為。按位操作符操作數(shù)字的二進(jìn)制形式,但是返回值依然是標(biāo)準(zhǔn)的數(shù)值。不同為真相同為假二進(jìn)制按位異或運(yùn)算從左到右按位非為真,為假對(duì)每一項(xiàng)進(jìn)行非操作,遇真則假,遇假則真。 二進(jìn)制與十六進(jìn)制 二進(jìn)制用 0 1 表示 2= 10十六進(jìn)制 前綴0x 用0123456789ABCDEF表示 2= 0x2二進(jìn)制與十六進(jìn)制的轉(zhuǎn)換十六進(jìn)制的每位 等于二進(jìn)制的四位 十六進(jìn)制 0x...
摘要:例如注意字符串中的負(fù)十六進(jìn)制數(shù)字是一個(gè)特殊情況,如果你用解析,結(jié)果是不正確的。轉(zhuǎn)換十六進(jìn)制數(shù)時(shí)要小心,如果你不知道要轉(zhuǎn)換對(duì)象的類型,不要使用。字符串轉(zhuǎn)換為數(shù)字的方式總結(jié)負(fù)十六進(jìn)制數(shù)字符串轉(zhuǎn)換為數(shù)字時(shí)。 摘要 :JavaScript 是一個(gè)神奇的語言,字符串轉(zhuǎn)數(shù)字有 5 種方法,各有各的坑法! 原文: Converting Strings to Number in Javascript...
閱讀 2646·2021-10-08 10:04
閱讀 2744·2021-09-06 15:02
閱讀 831·2019-08-30 13:50
閱讀 1560·2019-08-30 13:21
閱讀 2596·2019-08-30 11:15
閱讀 2123·2019-08-29 17:19
閱讀 1590·2019-08-26 13:55
閱讀 1268·2019-08-26 10:15