Khám phá thao tác bit trong Java và các ứng dụng giải thuật đơn giản

Như chúng ta đã biết, nền tảng của máy tính là hệ nhị phân. Là một ngôn ngữ lập trình máy tính, Java cung cấp hỗ trợ đầy đủ cho các thao tác bit dựa trên hệ nhị phân. Cần lưu ý rằng mọi thao tác bit trong Java đều chỉ áp dụng được cho các kiểu số nguyên như int, long, short, charbyte.

Trong Java, kiểu int có 32 bit, cho phép thực hiện các thao tác bit trên 32 bit tin. Để dễ dàng biểu diễn, chúng ta thường sử dụng hệ thập lục phân (hexadecimal) để gán giá trị. Ví dụ: 0011 trong nhị phân tương đương với 0x3 trong hex, còn 1101110x37.

Vậy thao tác bit là gì? Đó là các thao tác xử lý trực tiếp trên từng bit của dữ liệu, bao gồm hai loại chính: phép dịch chuyển (shift) và phép toán logic (logical).

Phép dịch chuyển bit

  • Dịch trái (Left Shift - <<): Di chuyển các bit sang trái, các bit thấp bên phải được bổ sung bằng 0, còn các bit cao bên trái bị loại bỏ. Về mặt số học, dịch trái 1 bit tương đương với nhân giá trị với 2.
  • Dịch phải không dấu (Unsigned Right Shift - >>>): Di chuyển các bit sang phải, các bit bên phải bị loại bỏ, và bit trái cùng được lấp đầy bằng 0.
  • Dịch phải có dấu (Signed Right Shift - >>): Di chuyển các bit sang phải, các bit bên phải bị loại bỏ và bit trái cùng được lấp đầy bằng giá trị của bit cao nhất (bit dấu). Nếu bit dấu là 1, các bit mới sẽ là 1; nếu là 0, chúng sẽ là 0. Dịch phải 1 bit tương đương với chia số nguyên cho 2.

Khi chuyển đổi số thập phân sang nhị phân, chúng ta thường biểu diễn dưới dạng 8, 16, 32 hoặc 64 bit, do đó các bit cao nhất được lấp đầy bằng 0. Trong máy tính, số âm được biểu diễn bằng mã bù hai (two's complement). Các bước cơ bản là: lấy mã gốc (source code) của số dương, đảo bit để có mã bù một (one's complement), sau đó cộng thêm 1 để có mã bù hai. Bit cao nhất (bit dấu) của số nhị phân quy định: 0 cho số dương, 1 cho số âm.

Ví dụ:

int a = 4; // 100 trong nhị phân
a = a >> 2; // trở thành 001, tức 1
System.out.println("Kết quả của 4>>2: " + a); // 1

a = 4;
a = a << 3; // trở thành 100000, tức 32
System.out.println("Kết quả của 4<<3: " + a); // 32

System.out.println("Kết quả của 16>>2: " + (16 >> 2)); // 4
System.out.println("Kết quả của -16>>2: " + ((-16) >> 2)); // -4
System.out.println("Kết quả của 16>>>2: " + (16 >>> 2)); // 4
System.out.println("Kết quả của -16>>>2: " + ((-16) >>> 2)); // Số nguyên lớn, tùy theo số bit

Kết quả cho thấy: Với số dương, >>>>> hoạt động giống nhau. Sự khác biệt chỉ xuất hiện khi xử lý số âm.

Phép toán logic

  • AND bit (&): Kết quả là 1 chỉ khi cả hai bit đều là 1.
  • OR bit (|): Kết quả là 1 nếu có ít nhất một bit là 1.
  • NOT bit (~): Đảo ngược tất cả các bit: 1 thành 0, 0 thành 1.
  • XOR bit (^): Kết quả là 1 nếu hai bit khác nhau, là 0 nếu chúng giống nhau.

Ví dụ:

int x = ...;
int lowBit = x & 0x1; // Trả về 0 hoặc 1, chính là bit cuối cùng của x.
x = x | 0x1;          // Luôn đặt bit cuối cùng thành 1, bất kể giá trị ban đầu của nó.

Ứng dụng đơn giản

Ứng dụng 1: Kiểm tra số chẵn lẻ

Phân tích: Số lẻ không phải là bội số của 2, do đó bit cuối cùng (bit 0) trong biểu diễn nhị phân của chúng luôn là 1. Ngược lại, số chẵn có bit cuối cùng là 0. Chúng ta có thể tận dụng điều này để kiểm tra tính chẵn lẻ của một số nguyên thông qua thao tác bit.

Mã nguồn:

int mask = 1; // 00000000000000000000000000000001
int m = 5;    // 00000000000000000000000000000101
int n = 6;    // 00000000000000000000000000000110

if ((m & mask) == 1) {
    System.out.println("Bit cuối cùng của m là 1, do đó m là số lẻ");
}
if ((n & mask) == 0) {
    System.out.println("Bit cuối cùng của n là 0, do đó n là số chẵn");
}

Ứng dụng 2: Xác định xem một số nguyên dương có phải là lũy thừa của 2 hay không

Phân tích: Hãy xem xét các lũy thừa của 2 như: 2, 4, 8, 16. Biểu diễn nhị phân tương ứng của chúng là: 10, 100, 1000, 10000. Dễ thấy, chỉ có một bit duy nhất bằng 1 và các bit còn lại đều là 0. Điều thú vị là: Nếu chúng ta trừ đi 1 từ một số như vậy, kết quả sẽ có tất cả các bit thấp đều là 1. Ví dụ: 8 - 1 = 7 (111 trong nhị phân), và 8 & 7 = 0. Công thức tổng quát là:

(n & (n - 1)) == 0

Một số nguyên dương n thỏa mãn điều kiện này chính là lũy thừa của 2.

Thẻ: Java thao tác bit dịch chuyển bit phép toán logic số chẵn lẻ

Đăng vào ngày 27 tháng 5 lúc 20:49