Bit Hacking Toàn Tập - Phần 1: Từ "Ma Thuật" Số Học Đến Nghệ Thuật Bitmask
Trong kỷ nguyên của các ngôn ngữ bậc cao, máy ảo và phần cứng mạnh mẽ, việc lập trình viên thao tác trực tiếp trên từng bit dữ liệu (Bit Manipulation) đôi khi bị coi là một "bí thuật" cổ xưa.
Tuy nhiên, tại những "chiến trường" yêu cầu hiệu năng cực hạn như Game Engine, Real-time Systems, hay Competitive Programming, Bit Hacking vẫn luôn là một loại vũ khí thượng đẳng.
Bài viết này sẽ tổng hợp và đi sâu vào các thủ thuật thao tác bit: từ những mẹo số học tinh tế, kỹ thuật lập trình không rẽ nhánh (Branchless Programming), cho đến nghệ thuật xử lý tập hợp bằng Bitmask.
1. Nền tảng: Sức mạnh của các phép toán Bitwise
CPU hiện đại thực thi các phép toán bitwise (&, |, ^, ~, <<, >>) trong vòng vỏn vẹn 1 chu kỳ máy (clock cycle).
Tốc độ này vượt trội hoàn toàn so với các phép toán số học như nhân, chia, hay lấy dư.
XOR – "viên ngọc quý"
- x ⊕ 0 = x
- x ⊕ x = 0
- x ⊕ y = y ⊕ x (Tính giao hoán)
2. Kỹ thuật Branchless (Không rẽ nhánh)
Các câu lệnh if-else có thể gây ra Branch Misprediction, làm CPU tốn hàng chục chu kỳ.
2.1. Tìm giá trị tuyệt đối không dùng abs()
int mask = x >> 31; // 0 nếu x dương, -1 nếu x âm
int abs_x = (x + mask) ^ mask;
2.2. Tìm Min/Max siêu tốc
// Min
int min = y ^ ((x ^ y) & -(x < y));
// Max
int max = x ^ ((x ^ y) & -(x < y));
2.3. Kiểm tra hai số khác dấu
bool isOppositeSign = (x ^ y) < 0;
3. Thao tác trên các bit đặc biệt
3.1. Cô lập bit 1 cuối cùng (LSB)
int lsb = x & -x;
👉 Rất quan trọng trong Fenwick Tree (BIT)
3.2. Kiểm tra lũy thừa của 2
bool isPowerOfTwo = n > 0 && (n & (n - 1)) == 0;
4. Thuật toán đếm bit (Population Count)
4.1. Brian Kernighan
int countSetBits(int n) {
int count = 0;
while (n) {
n &= (n - 1);
count++;
}
return count;
}
4.2. Built-in Compiler
int count = __builtin_popcount(n);
int countLL = __builtin_popcountll(n);
5. Bitmask - Nghệ thuật quản lý tập hợp
5.1. Thao tác cơ bản
// Thêm
mask |= (1 << i);
// Xóa
mask &= ~(1 << i);
// Kiểm tra
bool has_i = mask & (1 << i);
// Toggle
mask ^= (1 << i);
5.2. Submask Enumeration
for (int submask = mask; submask > 0; submask = (submask - 1) & mask) {
// xử lý
}
👉 Độ phức tạp giảm từ: O(4^N) → O(3^N)
6. ASCII Hack - Xử lý chuỗi bằng bit
Khoảng cách giữa chữ hoa và thường = 32 (2⁵)
// to lower
c | ' '
// to upper
c & '_'
// toggle
c ^ ' '
// vị trí alphabet
c & 31
Ví dụ:
char a = 'a';
char upper = a & '_'; // 'A'
int pos = a & 31; // 1
7. Đảo ngược bit (Bit Reversal)
uint32_t reverseBits(uint32_t n) {
n = ((n >> 1) & 0x55555555) | ((n << 1) & 0xAAAAAAAA);
n = ((n >> 2) & 0x33333333) | ((n << 2) & 0xCCCCCCCC);
n = ((n >> 4) & 0x0F0F0F0F) | ((n << 4) & 0xF0F0F0F0);
n = ((n >> 8) & 0x00FF00FF) | ((n << 8) & 0xFF00FF00);
n = (n >> 16) | (n << 16);
return n;
}
👉 Ứng dụng trong FFT
Lời kết
Bit Hacking không đơn thuần là mẹo vặt — nó là biểu hiện của sự hiểu sâu cách máy tính vận hành.
Khi dữ liệu ngày càng lớn:
- Tối ưu từng cycle
- Tiết kiệm từng byte
… vẫn là lợi thế cạnh tranh cực lớn.
All rights reserved