有關位運算的題

關於書寫的規範性

清晰的書寫+清晰的佈局+合理的命名

1、清晰的書寫:在手寫代碼時先形成清晰的思路並把思路用編程語言清晰的寫出來,字跡清晰

2、清晰的佈局:合理的縮進、空格、空行(循環塊 定義變量塊 等)

3、合理的命名:用完整的英文單詞組合命名變量和函數,一眼就可以看出變量、函數的用途

代碼完整性

1.在編寫代碼之前考慮單元測試,通常我們可以從功能測試、邊界值測試和負面測試三方面設計測試用例,以確保代碼完整性

*功能測試:檢查代碼是否實現基本功能

*邊界值測試:輸入一些邊界值(結束循環的邊界條件、遞歸終止邊界)看代碼是否符合條件

*負面測試:輸入不合法的值看代碼運行情況(是否能做出合理的錯誤處理)

將代碼將來的可能的需求全部考慮,在需求變化是時能儘量減少代碼改動的風險,使程序可擴展與可維護

位運算

與&0&0=01&0=00&1=01&1=1
或|0|0=01|0=10|1=11|1=1
異或^0^0=01^0=10^1=11^1=0

題目:1

實現一個函數,輸入一個整數,輸出該數的二進制表示中1的個數,例如9的二進制是1001,有兩個1,輸入9,輸出1

程序1.0

寫法1:模除法
int CountOne(int n)
{
	
	int count = 0;
	while (n)
	{
		int ret = 0;
		ret = n % 2;
		if (1 == ret)
			count++;
		n /= 2;
	}
	return count;
}

程序2.0

位運算,由於除法的效率要遠遠低於位運算,所以在需要用乘除時儘量用位運算代替

而右移運算符又會引入一個新問題,若輸入一個負數,則會導致無限循環(右移時左邊補符號位)

int CountOne(int n)
{
	int count = 0;
	while (n)
	{
		if ((n & 1) == 1)
		{
			count++;
		}
		n=n >> 1;
	}
	return count;
}

算法2.1

位運算改進,不對輸入的數進行移位

int CountOne(int n)
{
	int count = 0;
	size_t flag = 1;
	while (flag)
	{
		if (n&flag)
			count++;
		flag <<= 1;
	}
	return count;
}

算法3.0 

把一個整數減去1,再和原整數做與運算,會把該整數的最右邊的一個1變成0,那麼一個整數的二進制表示中有多少個1,就可以做多少次這樣的操作

int CountOne(int n)
{
	int count = 0;
	while (n)
	{
		++count;
		n = (n - 1)&n;
	}
 	return count;
}

*把一個整數減去1再和原來的整數做位與運算得到的結果相當於把這個整數中表示最右邊的一個1變成0

舉一反二

  1. 用一條語句判斷一個整數是不是2的整數次方,一個整數如果是2的整數次方那麼它的二進制表示中有且僅有一位是1,其他位都是0;

分析:這個數減去1再和自己位與,這個整數中唯一的1就會變成0


2. 輸入兩個整數n和m,計算需要改變m的二進制表示中的多少位才能得到n,比如10的二進制位1010,13的二進制1101,需要1010改變3位才能得到1101,

分析:第一步求這兩個數的異或,第二部統計異或結果中1的位數

題目二

    實現函數double Power(double base,int exponen),求base的exponent次方,不得使用庫函數,同時不需要考慮大數問題

程序1.0

最簡單的寫法,但是還是沒有考慮效率的問題

double Power(double base, int exponen)
{
	double ret = 1.0;

	double ex = 0.0000000001;//考慮base等於0的時候
	if (base<ex&&base>-ex)
		return 0;

	if (exponen > 0)
	{
		while (exponen > 0)
		{
			ret *= base;
			exponen--;
		}
		return ret;
	}

	if (exponen < 0)
	{
		while (exponen < 0)
		{
			ret *= (1.0 / base);
			exponen++;
		}
		return ret;
	}

	else return 1;
}

程序2.0

採用全局變量來標識是否出錯,若出錯則返回0,,設置爲全局的好處是可以把函數直接傳遞給其他變量

同時,浮點數在判斷是否相等時,採用比較它們的差是否在一個很小的範圍內,若兩數相差很小則判斷相等

bool q_InvalidInput = false;
double PowerWithUnsignedExponent(double base, unsigned int exponent)
{
	double result = 1.0;
	for (int i = 1; i <= exponent; i++)
	{
		result *= base;
	}
	return result;
}
bool equal(double num1, double num2)
{
	if ((num1 - num2 > -0.000000001) && (num1 - num2 < 0.000000001))
		return true;
	else
		return false;
}
double Power(double base, int exponent)
{
	q_InvalidInput = false;

	if (equal(base, 0.0) && exponent < 0)//判斷輸入的是否是0,若是,則在exponent<0時直接返回0
	{
		q_InvalidInput = true;
		return 0.0;
	}
	
	unsigned int absExponent = (unsigned int)(exponent);
	
	if (exponent < 0)
	{
		absExponent = (unsigned int)(-exponent);//將負數去符號
	}
	double result = PowerWithUnsignedExponent(base, absExponent);
	
	if (exponent < 0)
	{
		result = 1.0 / result;
	}
	return result;
}

程序2.1

用位操作符

在上面函數中用一次次的循環來乘出結果,若要算32的次方,我們就要做31次乘法,如果已經知道16此次,只要在16次方的基礎上再平方一次,而16是8的次方……

改寫PowerWithUnsignedExponent()

double PowerWithUnsignedExponent(double base, int exponent)
{
	if (exponent == 0)
		return 1;
	if (exponent == 1)
		return base;
	
	double result = PowerWithUnsignedExponent(base, exponent >> 1);//右移1位表示除以2
	
	result *= result;
	
	if (exponent & 0x1 == 1)//用位與代替求餘(%),來判斷是奇數還是偶數
		result *= base;
	
	return result;
}


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