演算法小記·不用四則運算做加法

只燈片箋發表於2018-11-05

寫一個函式,求兩個整數之和,要求在函式體內不得使用 +、-、*、/ 四則運算子號。

輸入:

輸入可能包含多個測試樣例。
對於每個測試案例,輸入為兩個整數 x 和 y (1 <= m, n <= 1000000)。

輸出:

對應每個測試案例,輸出 x + y 的值。

樣例輸入:

1 2
11 18

樣例輸出:

3
29

首先,思考的是,不能用四則運算子,只能從別的角度來考慮。
那麼,用過C語言的,自然就得考慮到C語言的優勢之一 按位運算

吾來打個樣:

#include <stdio.h>
// 主要的方法是用與來求進位,用異或來求不進位的加法
unsigned int Add(unsigned int a, unsigned int b) 
{
    unsigned int sum, carry;
    
    do {
        // 按位異或運算
        sum = a ^ b;
        // 按位與運算後,再進行按位左移運算
        // << 1 相當於 *2
        carry = (a & b) << 1;
        
        a = sum;
        b = carry;
    } while (b != 0); // 對carry的值做判斷,不滿足條件的,則為正確的sum值
    
    return a;
}

int main(int argc, const char * argv[]) {
    unsigned int x, y;
    while (scanf("%u%u", &x, &y) != EOF) {
        printf("%u
", Add(x, y));
    }
    return 0;
}

關於按位運算的些許補充(不瞭解的或遺忘了的,可以看看):

C 語言 和其它高階語言不同的是,它完全支援 按位運算子。這與 組合語言位操作 有些相似。
因為 位運算 得到了更多的底層優化,因此同樣的功能它的 效率更高

所謂 位運算,就是對一個 位元(Bit)位 進行操作。位元(Bit) 是一個電子元器件,8 個位元 構成 一個位元組(Byte),它已經是 粒度最小可操作單元 了。

// C語言中提供的六種按位運算子
& 位邏輯與
| 位邏輯或
^ 位邏輯異或
~ 位邏輯反
>> 右移
<< 左移
標準的 C 語言其實並不支援二進位制數字,只不過有些編譯器自己進行了擴充套件,才支援二進位制數字。換句話講就是,並非所有的編譯器都支援二進位制數字,只有一部分編譯器支援,並且跟編譯器的版本還有關係。

C 語言中不能直接使用 二進位制 數字,按位運算子 兩邊的運算元可以是十進位制、八進位制、十六進位制,因為它們在記憶體中最終皆是以 二進位制 的形式儲存,按位運算子就是對這些 記憶體中二進位制位 進行運算。其他的位運算子也是相同的道理。

提醒按位運算子 是根據記憶體中的二進位制位進行運算的,而不是資料的二進位制形式;其他位運算子也一樣。

按位與運算 (&)

    /*
     * 按位與運算 (&)
     * 一個位元(Bit)位只有 0 和 1 兩個取值,只有參與 & 運算的兩個位都為 1 時,結果才為 1,否則為 0。
     * 例如:1 & 1為 1,0 & 0為 0,1 & 0也為 0,這和邏輯運算子 && 非常類似。
     * 
     *   0000 0000 -- 0000 0000 -- 0000 0000 -- 0000 0010  //表示十進位制的 2 (在記憶體中的的儲存)
     * & 0000 0000 -- 0000 0000 -- 0000 0000 -- 0000 0001  //表示十進位制的 1 (在記憶體中的的儲存)
     * ---------------------------------------------------
     *   0000 0000 -- 0000 0000 -- 0000 0000 -- 0000 0000  //表示十進位制的 0 (在記憶體中的的儲存)
     *
     */
    int m = 2;
    int n = 1;
    printf("%d 
", (m & n));  //輸出為 0

按位或運算 (|)

    /*
     * 按位或運算 (|)
     * 一個位元(Bit)位只有 0 和 1 兩個取值,只要參與 | 運算的兩個二進位制位有一個為 1 時,結果就為 1,兩個都為 0 時結果才為 0。
     * 例如:1 | 1為 1,0 | 0為 0,1 | 0為 1,這和邏輯運算中的||非常類似。
     *
     *   0000 0000 -- 0000 0000 -- 0000 0000 -- 0000 0010  //表示十進位制的 2 (在記憶體中的的儲存)
     * | 0000 0000 -- 0000 0000 -- 0000 0000 -- 0000 0001  //表示十進位制的 1 (在記憶體中的的儲存)
     * ---------------------------------------------------
     *   0000 0000 -- 0000 0000 -- 0000 0000 -- 0000 0011  //表示十進位制的 3 (在記憶體中的的儲存)
     *
     */
    int m = 2;
    int n = 1;
    printf("%d
", (m | n));  //輸出為 3

按位異或運算 (^)

    /*
     * 按位異或運算(^)
     * 一個位元(Bit)位只有 0 和 1 兩個取值,只要當參與 ^ 運算兩個二進位制位不同時,結果就為 1,相同時結果則為 0。
     * 例如:0 ^ 1為 1,0 ^ 0為 0,1 ^ 1為 0。
     * 
     *   0000 0000 -- 0000 0000 -- 0000 0000 -- 0000 0010  //表示十進位制的 2 (在記憶體中的的儲存)
     * ^ 0000 0000 -- 0000 0000 -- 0000 0000 -- 0000 0001  //表示十進位制的 1 (在記憶體中的的儲存)
     * ---------------------------------------------------
     *   0000 0000 -- 0000 0000 -- 0000 0000 -- 0000 0011  //表示十進位制的 3 (在記憶體中的的儲存)
     *    
     */
    int m = 2;
    int n = 1;
    printf("%d
", (m ^ n));  //輸出為 3

取反運算 (~)

    /*
     * 取反運算(~)
     * 一個位元(Bit)位只有 0 和 1 兩個取值,取反運算子~為單目運算子,右結合性,作用是對參與運算的二進位制位取反。
     * 例如:~1為 0,~0為 1,這和邏輯運算中的!非常類似。
     * ~ 0000 0000 -- 0000 0000 -- 0000 0000 -- 0000 0001  //表示十進位制的 1 (在記憶體中的的儲存)
     * ---------------------------------------------------
     *   1111 1111 -- 1111 1111 -- 1111 1111 -- 1111 1110  //表示十進位制的 -2 (在記憶體中的的儲存)
     * 在計算機中,負數以原碼的補碼形式表達。
     * 正數的補碼與原碼相同,負數的補碼為對該數的原碼(除符號位外)逐位取反,然後在最後一位加 1。
     * 二進位制中,如果有符號, 最高位表示符號, 0 為正, 1 為負,
     * -2 的二進位制原碼是:1000 0000 -- 0000 0000 -- 0000 0000 - 0000 0010
     * -2 的二進位制補碼是:1111 1111 -- 1111 1111 -- 1111 1111 - 1111 1101
     * -2 的o二進位制補碼加1 是:1111 1111 -- 1111 1111 -- 1111 1111 -- 1111 1110
     */
    int n = 1;
    printf("%d
", (~ n));  //輸出為 -2

左移運算 (<<)

    /*
     * 左移運算 (<<)
     * 一個位元(Bit)位只有 0 和 1 兩個取值,左移運算子<< 就是把運算元的各個二進位制位全部左移若干位,高位丟棄,低位補 0。
     *
     * << 0000 0000 -- 0000 0000 -- 0000 0000 -- 0000 0001  //表示十進位制的 1 (在記憶體中的的儲存)
     * ---------------------------------------------------
     *    0000 0000 -- 0000 0000 -- 0000 0000 -- 0000 1000  //表示十進位制的 8 (在記憶體中的的儲存)
     *
     */
    int n = 1;
    printf("%d
", (n << 3));  //輸出為 8

右移運算(>>)

    /*
     * 右移運算 (>>)
     * 一個位元(Bit)位只有 0 和 1 兩個取值,右移運算子>> 就是把運算元的各個二進位制位全部右移若干位,低位丟棄,高位補 0 或 1。
     * 如果資料的最高位是 0,那麼就補 0;如果最高位是 1,那麼就補 1。
     *
     * >> 0000 0000 -- 0000 0000 -- 0000 0000 -- 0000 0001  //表示十進位制的 1 (在記憶體中的的儲存)
     * ---------------------------------------------------
     *    0000 0000 -- 0000 0000 -- 0000 0000 -- 0000 0000  //表示十進位制的 0 (在記憶體中的的儲存)
     *
     */
    int n = 1;
    printf("%d
", (n >> 3));  //輸出為 0


相關文章