1. 程式人生 > >利用異或判斷二進位制數中的1的個數的奇偶性

利用異或判斷二進位制數中的1的個數的奇偶性

文章目錄

異或壓縮奇偶性資訊

二進位制數中的1的個數的奇偶性: 如果一個二進位制數中的1的個數為奇數,那麼返回1;如果為偶數,那麼返回0。

給定二進位制數01,它只有兩位,那麼它的奇偶性可以通過0^1 = 1獲得,這裡返回1,與它是奇數相符合。
給定二進位制數11,它只有兩位,那麼它的奇偶性可以通過1^1 = 0

獲得,這裡返回0,與它是偶數相符合。
總結:異或可以壓縮兩位上1個數的奇偶性。即壓縮奇偶性資訊為1或者0.

上面給出了兩位的例子,下面給出四位的例子,以0111為例,它的1的個數為奇數,那麼它的奇偶性就應該返回1。
在這裡插入圖片描述
利用壓縮資訊的規律,可以一步一步異或最終得出0111的奇偶性,過程如上,講解如下:
1.開始異或是從左起第一個1開始的,因為第一個1左邊只會是0,而它們對1的個數沒有影響。

2.異或時,是需要保證異或的兩個運算元所表示的壓縮資訊的範圍是不重疊的:

(1)第一個異或操作中,左邊運算元代表的是第2位的奇偶性,右邊運算元代表的是第1位的奇偶性。
(2)第二個異或操作中,左邊運算元代表的是第1,2位的奇偶性,右邊運算元代表的是第0位的奇偶性。

3.異或應該到右起第一個1停止,這是最精確的做法,但到第0位停止是一種更為簡單的判斷方法。

總結:壓縮後的奇偶性資訊,可以繼續和別的不重疊範圍的奇偶性資訊,進行進一步的壓縮。

在這裡插入圖片描述
同樣利用壓縮資訊的規律,但這裡利用二叉樹性質或者說歸併思想,每次倆倆進行壓縮資訊,每行進行壓縮時,當前行每個元素的奇偶性資訊代表的範圍都是前一行的2倍。
注意這裡也滿足,異或的兩個運算元所表示的壓縮資訊的範圍是不重疊的。

一位一位地異或

long fun_a(unsigned long x){
	long val = 0;
	while(x){
		val ^= x;
		x >>= 1;
} return val & 0x1; }

此程式碼的過程如本文圖一所示,程式碼來自《深入理解計算機系統》練習題3.26。以x為0111為例分析程式:
在這裡插入圖片描述
程式的執行過程如上圖所示:
1.首先分析迴圈會執行幾次,看while(x)x >>= 1,所以將x的二進位制中的左起第一個1移出去後,迴圈就會停止(此時x的二進位制裡面都是0),所以迴圈的次數為左起第一個1到第0位的長度。

2.x左起第一個1的索引位是2。每次異或的結果val中,從第2到第0位,每一位都代表著當前表示範圍的奇偶性資訊(上圖箭頭所指的灰色數字)。

3.當退出迴圈時,val的第0位就代表著第2到第0位的奇偶性資訊了,所以最後通過val & 0x1取出第0位。

利用二叉樹思想異或

bool OddOnes(int x)
{
	x = x ^ (x >> 1);
	x = x ^ (x >> 2);
	x = x ^ (x >> 4);
	x = x ^ (x >> 8);
	x = x ^ (x >> 16);
	return x & 1;
}

此程式碼的過程如本文圖二所示。還是以x為0111為例分析程式:
在這裡插入圖片描述
原始碼的x是int型的即32位二進位制。這裡以x為4位二進位制表示。
1.假設x為4位二進位制,那麼在x ^ (x >> 2)後就應該返回了。
2.如果x的二進位制位數為w,那麼應該執行 l o g 2 w log_2w x ^ (x >> n)的操作。因為int是32位,所以如上程式碼執行了5次。n從1開始,每次乘2。
3.最終,x的第0位,就會代表著從最高位到最低位範圍的奇偶性資訊。
4.上述程式碼看起來不是很優美,可以進行如下重構。

bool OddOnes(int x)
{
	int max = sizeof(x) * 8;//獲得x有多少位二進位制位,這裡是32
	int n = 1;
	while(n < max){
		x = x ^ (x >> n);
		n *= 2;
	}
	return x & 1;
}

關於有符號數和算術右移

直覺上講,上述兩種方法要是使用邏輯右移是肯定沒有問題的。但到底使用算術右移還是邏輯右移是由編譯器決定的,當編譯器發現變數是無符號數時,使用邏輯右移;變數是補碼數時,使用算術右移。
在方法利用二叉樹思想異或中,雖然會使用到算術右移,但拓展出來的符號位並不會影響到最終第0位表示的範圍。
同樣的,在方法一位一位地異或中,也可以將變數x改成int型,原因如上。

可以想到,int型變數右移31位後,32位上的值將都會是符號位的值。

利用x &= x-1求二進位制1個數

原理:一個二進位制數減1,若右起第一個1的索引位置是m,那麼從m到0位的二進位制會逐位取反。所以,x &= x-1會使得從m到0位的二進位制都會變成0。

示例:x is 0100x-1 is 0011x &= x-1x = 0000

bool OddOnes(int x) 
{
	int cnt = 0;
	while(x)
	{
		cnt++;
		x &= x-1;
	}
	return cnt & 1;//獲得奇偶性
}

每執行一次x &= x-1就會去掉x二進位制中的右起第一個1,當所有的1都被去掉後,迴圈停止,所以變數cnt記錄了1的個數。

利用邏輯右移求二進位制1個數

原理:就算傳進來的是一個int型,直接轉成unsigned int型就好,因為轉換後位級表示不會變,且可以使用到邏輯右移了。但這個方法比較笨,需要從左起第一個1開始遍歷到第0位,如果是負數那麼符號位為1,那就慘了,得全部遍歷了。

int count_one_bits(unsigned int n)//這裡強行轉換成無符號數
{
	int count=0;
	while(n)
	{
		if(n&1)
			count++;
 
			n=n>>1;
	}
	return count;
}

但是要注意轉換型別大小要一致:
1)從大轉到小,肯定不對。都截斷了,除非被截斷的部分一個1都沒有,才可能返回正確結果。
2)從小轉到大。如果本來實參是無符號數還好,拓展出來的位都是0,對結果沒有影響。但如果實參是補碼數且是負數,那麼轉換過程是,先拓展大小,再轉換有無符號。在拓展大小時,使用的是符號拓展,所以就會莫名多出來好多1。
所以建議就是,最好保持轉換型別大小,但上述程式碼並沒有此項保證,讀者可以自行優化。比如,變數如是單位元組就轉unsigned char;雙位元組就unsigned short;四位元組就unsigned int;八位元組就uint64_t(注意long在32位編譯器也是4位元組的哦,所以得用uint64_t);

兩個二進位制數異或後結果的1個數的奇偶性

在這裡插入圖片描述
假設數一和數二的二進位制位數都為w位。數一有x個0,數二有y個0。我們需要關注的是,數一的0與數二的1的匹配、數二的0與數一的1的匹配。

1.首先假設數一中有k個0和數二的1匹配上了。所以會有k個1產生了。

2.那麼數一中,剩下的x-k個0,就只能和數二的0匹配了。這裡不會有1產生。

3.現在開始關注數二的0與數一的1的匹配,因為2步驟所以數二中的0能和1匹配的個數只有y-(x-k)了,因為12步驟中已經將數一中的0分配完了,所以數二剩下的y-(x-k)個0就只能和數一中的1的匹配了。這裡產生y-(x-k)個1。

故異或結果的1的總個數為:k+y-(x-k)=2*k+y-x
且變數的二進位制位數w必為偶數。

分為三種情況討論:
1.擁有奇數個1的二進位制數與擁有奇數個1的二進位制數的異或運算:w為偶數,所以x,y均為奇數,此時y-x結果為偶數。

2.擁有奇數個1的二進位制數與擁有偶數個1的二進位制數的異或運算:同上,此時x為奇數,y為偶數,y-x結果為奇數。

3.擁有偶數個1的二進位制數與擁有偶數個1的二進位制數的異或運算:此時x為偶數,y為偶數,y-x為偶數或0.

【參考部落格】:
[1] https://blog.csdn.net/luckyxiaoqiang/article/details/7249099
[2] https://blog.csdn.net/zhang_yang_43/article/details/72818933