文章目錄
1. 高效判斷一個數是否是3的倍數 Check if a integer is multiple of 3 efficently
(1) 題意
寫一個函數,高效判斷一個數是否是3的倍數。
Input: 612
Output: true
(2) 思路
首先,我們想到的最平庸的方法就是直接取餘,n % 3 == 0
。不過,取餘和除法操作練習密切,而除法操作消耗的時間週期巨大,能避免則避免。
第二個方法是求出 n
的所有數位之和,判斷其是否是 3
的倍數,這個降低了除法操作的消耗,基本是 的時間複雜度,r
是 n
的位數。思路易得:
假設有一個兩位數ab, ab = 10 * a + b
=> ab = 9 * a + (a + b)
=> 如果ab是3的倍數,且9a也是3的倍數,則(a + b)也是3的倍數
=> 如果ab不是3的倍數,則(a + b)也不是3的倍數
上述做法可以推廣到任意位數。比如對 612
,三位相加爲 9
,9
是 3
的倍數,則 612
也是 3
的倍數。
不過這個方法,還是不夠快,從位運算角度可以有第三種做法。做法是——對一個數,如果其二進制形式中偶數位的 1
(set bits)和奇數位的 1
(set bits)的數量的差,是 3
的倍數,則這個數也是三的倍數。
比如,23=(00..10111)
,其偶數位的 1
有一位,奇數位的 1
有三位,差是 2
,不是三的倍數,則 23
也不是三的倍數。
算法 isMultipleOf3
思路如下:
1. 求出n的絕對值 (Make n positive if n is negative)
2. 如果數n是0則返回true, 如果數n是1則返回false
3. 初始化oddCount = 0, evenCount = 0
4. 如果n != 0則循環
a) 如果最右邊rightmost的位是1, 則oddCount++;
b) 右移一位;
c) 如果最右邊rightmost的位是1, 則evenCount++;
d) 右移一位
5. return isMultipleOf3(oddCount - evenCount)
這種思路來自於第二種方法的變形。有這樣的一個題:判斷一個數是否是 11
的倍數。
假設有一個兩位數ab, ab = 10 * a + b
=> ab = 11 * a + (b - a)
=> 如果ab是11的倍數,且11a也是11的倍數,則(b - a)也是11的倍數
=>如果ab不是11的倍數,則(b - a)也不是3的倍數
三位數abc時,abc = 100 * a + 10 b + c
=> abc = 99 * a + 11 * b + (a + c - b)
=> 如果abc是11的倍數,且99a, 11b也是11的倍數,則(a + c - b)也是11的倍數
四位數abcd時, abcd = 1000 * a + 100 * b + 10 * c + d
=> abcd = 1001 * a + 99 * b + 11 * c + (d + b - c - a)
=> 如果abcd是11的倍數, 且1001a, 99b, 11c都是11的倍數, 則(b + d - a - c)也是11的倍數
上述做法可以推廣到任意位。同樣,對於二進制而言,3
就相當於十進制的 11
。這裏不做贅述。
(3) 模板代碼
//check if n is a multiple of 3
#include <bits/stdc++.h>
using namespace std;
int isMultipleOf3(int n) {
int oddCount = 0, evenCount = 0;
/*make the number positive if it's negative.
We are doing this to avoid stackoverflow is recursion. */
if (n < 0) n = -n;
if (n == 0) return true;
if (n == 1) return false;
while (n) {
if (n & 1) ++oddCount;
if (n & 2) ++evenCount;
n = n >> 2;
}
return isMultipleOf3(abs(oddCount - evenCount));
}
int main() {
int num = 18;
if (isMultipleOf3(num)) printf("%d is multiple of 3.", num);
else printf("%d is not a multiple of 3.", num);
return 0;
}
2. 快速乘7 Efficient method to Multiply with 7
(1) 題意
使用位運算符 bitwise operator
,將一個數快速乘以 7
。
Input: 7
Output: 49
(2) 思路
首先,如果用普通的乘法的話,我們可能需要更多的加法和移位操作。以普通的二進制乘法器爲例,其主要操作是加法和移位操作:
- ① 擴展被乘數爲兩倍的位寬,高
4
位爲0
,低4
位爲被乘數,結果先爲全0
; - ② 檢測乘數的最低位,如果是
1
,說明要將結果加上被乘數;然後乘數右移(爲了檢測最低位),被乘數左移; - ③ 檢測乘數的最低位,如果是
0
,則直接:乘數右移,被乘數左移; - ④ 重複 ② 或 ③ 的操作,直到乘數爲
0
。
示例如下:
7 * 7 = 49
被乘數7 = 0000 0111
乘數 7 = 0000 0111
①
0000 0111
* 0111
--------------
0000 0111
②
0000 1110
* 011
--------------
0000 0111
+ 0000 1110
③
0001 1100
* 001
--------------
0000 0111
+ 0000 1110
+ 0001 1100
--------------
= 0011 0001 = 49
其他的乘法在乘法器中也是如此操作的。
上述乘七的過程用到了 3
次移位,3
次加法。而下面的做法可以減少這些操作:
- 將原數左移
3
位,得到8 * n
; - 然後減去
n
,即8n - n = 7n
。
這種思路更加簡單,也可以擴展到乘任何整數。
(3) 模板代碼
#include <bits/stdc++.h>
using namespace std;
long long multiplyBySeven(long long n) {
return ((n << 3) - n);
}
int main() {
long long n = 4;
cout << multiplyBySeven(n) << endl;
return 0;
}
3. 乘3.5 Multiply a given integer with 3.5
(1) 題意
給出一個整數 x
,寫一個函數將 x
乘以 3.5
,然後返回結果的整數部分。不允許使用 %,/,*
。
Examples :
Input: 2
Output: 7
Input: 5
Output: 17 (Ignore the digits after decimal point)
(2) 思路和方法1
從題目看,可以使用 +
,因此,我們可以將乘以 3.5
分解爲 3.5x = 2x + x + 0.5x
。然後,使用移位操作替換乘法,寫成 (x << 1) + x + (x >> 1)
。
#include <bits/stdc++.h>
//multiply a number with 3.5
int multiplyWith3Point5(int x) {
return (x<<1) + x + (x>>1);
}
int main() {
cout << multiplyWith3Point5(2) << endl << multiplyWith3Point5(5) << endl;
return 0;
}
(3) 思路和方法2
還可以將 3.5x
寫成 (8 * x - x) / 2
。
int multiplyWith3Point5(int x)
return ((x << 3) - x) >> 1;
}
4. 判斷整數溢出 Check for integer overflow
(1) 題意
寫一個函數 addOvf(int &result, int a, int b)
,判斷 a+b
是否會溢出,如果溢出,則返回 false
;沒有溢出則返回 true
。轉換爲長整型並且相加來判斷是否溢出,是不允許的。
(2) 思路和代碼
溢出僅僅發生在兩個數同號的情況下,此時結果如果和兩個數的符號不同,則溢出。
所以,步驟如下:
① 計算和;
② 如果都是正數,結果是負數,則返回 false
;
③ 如果都是負數,結果是正數,則返回 false
;
④ 返回 true
。
#include <bits/stdc++.h>
using namespace std;
bool addOvf(int &result, int a, int b) {
result = a + b;
if (a > 0 && b > 0 && result < 0) return false;
if (a < 0 && b < 0 && result > 0) return false;
//上面兩句可以替換爲
//if (a > INT_MAX - b || a < INT_MIN - b) return false;
return true;
}
int main() {
int res, x = 2147483640, y = 10;
cout << addOvf(res, x, y) << endl;
cout << res;
return 0;
}
5. 判斷奇偶 Check whether a given number is even or odd
(1) 題意
判斷一個給定的數是奇數還是偶數。
Examples :
Input: 2
Output: even
Input: 5
Output: odd
(2) 思路和代碼
很簡單的問題,一般不用取餘。如果是奇數,則二進制形式的位 0
是 1
;否則是 0
。
#include <iostream>
using namespace std;
//returns true if n is even, else odd
bool isEven(int n) {
//n & 1 is 1, then odd, else even
return (!(n & 1));
}
int main() {
int n = 101;
isEven(n)? cout << "Even" : cout << "Odd";
return 0;
}