C. 位元運算

APCS 官方公告的檢測範圍裡,初級實作題那一欄單獨列了「位元運算」。正文的題目用不到它,所以留到這裡講。

電腦裡的整數是一串 0 與 1(上冊 2.2 的位元)。位元運算就是把兩個數字的位元對齊,一格一格分開算——1210 對齊起來長這樣:

12  →  00001100
10  →  00001010

六個運算子

  • a & b(AND):兩邊同一格都是 1 才是 1
  • a | b(OR):兩邊同一格有一邊是 1 就是 1
  • a ^ b(XOR,互斥或):兩邊同一格不一樣才是 1
  • ~a(NOT):每一格 0 換成 1、1 換成 0
  • a << k(左移):整排往左推 k 格,右邊補 0
  • a >> k(右移):整排往右推 k 格,推出界的丟掉
#include <iostream>
using namespace std;

// 印出 x 最低的 8 個位元(由高位印到低位),後面接十進位值
void print_bits(int x) {
    for (int i = 7; i >= 0; i--) {
        cout << ((x >> i) & 1);
    }
    cout << ' ' << x << '\n';
}

int main() {
    int a = 12, b = 10;
    print_bits(a);          // a
    print_bits(b);          // b
    print_bits(a & b);      // 兩邊都是 1 才是 1
    print_bits(a | b);      // 有一邊是 1 就是 1
    print_bits(a ^ b);      // 兩邊不一樣才是 1
    print_bits(a << 1);     // 整排往左推一格
    print_bits(a >> 1);     // 整排往右推一格
    cout << (~a) << '\n';   // 每一格 0 換成 1、1 換成 0
    return 0;
}

執行結果:

00001100 12
00001010 10
00001000 8
00001110 14
00000110 6
00011000 24
00000110 6
-13

前五行對照著看就清楚了:0000110000001010 逐格比對,& 得到 00001000| 得到 00001110^ 得到 00000110。左移一格數字變兩倍、右移一格變一半,也在後兩行看得到。

最後一行的 ~a 印出 -13int32 個位元,最左邊那一格代表正負號,~ 連它一起翻面,正數就變成負數;翻面後的值固定是 -x - 1

最常看到的三種用法

  1. x & 1 判奇偶:最低那一格就是除以 2 的餘數,是 1 代表奇數。
  2. 1 << k 就是 2^k1 只有最低一格是 1,往左推 k 格剛好是 2^k
  3. x >> 1 相當於除以 2(限非負整數,除不盡的部分直接捨去);同理 x >> k 就是除以 2^k

XOR 的兩個性質

a ^ a 一定是 0(每一格都跟自己一樣),a ^ 0 則是 a 本身。XOR 還可以交換、可以任意結合——一整串 XOR 起來,順序怎麼調換都不影響結果。

兩者合起來就有一個經典用法:一堆數字裡只有一個出現奇數次、其餘都成對,把全部 XOR 起來,成對的兩兩抵消,剩下的就是答案。

#include <iostream>
using namespace std;

int main() {
    int n = 25;
    cout << (n & 1) << ' ' << (24 & 1) << '\n';                       // 判斷奇偶
    cout << (1 << 0) << ' ' << (1 << 3) << ' ' << (1 << 10) << '\n';  // 2 的次方
    cout << (n >> 1) << ' ' << (n >> 2) << '\n';                      // 除以 2、除以 4

    int arr[7] = {4, 7, 4, 9, 7, 3, 3};
    int ans = 0;
    for (int i = 0; i < 7; i++) {
        ans = ans ^ arr[i];   // 成對的兩個會互相抵消
    }
    cout << ans << '\n';      // 只出現一次的那個數
    return 0;
}

執行結果:

1 0
1 8 1024
12 6
9