Luỹ thừa của 2 gần nhất¶
Đề bài¶
Yêu cầu: tìm số là luỹ thừa của 2 nhỏ nhất mà lớn hơn hoặc bằng n.
Input số nguyên dương n.
Output: số x sao cho x là lũy thừa của 2 và lớn hơn hoặc bằng n.
Ví dụ:
| Input | Output |
|---|---|
| n = 16 | 16 |
| n = 20 | 32 |
| n = 21 | 32 |
Bài giải đề xuất¶
Ý tưởng chính¶
Một số nguyên dương x là luỹ thừa của 2 có dạng nhị phân là: ...1000...0, tức bit tại vị trí p là 1, các bit còn lại bên phải đều là 0.
Số nguyên liền trước x có dạng nhị phân là: ...0111..1, tức bit tại vị trí p là 0, các bit còn lại bên phải đều là 1.
Để tìm được số nguyên liền trước x, ta sẽ sử dụng kỹ thuật (tạm gọi) là lấp đầy bit hoặc lan truyền bit, có thể được viết minh hoạ là: n |= n >> 1 hoặc n = n | (n >> 1).
Cách làm cụ thể như sau:
Bước 1: trừ 1 khỏi n
Bằng cách trừ n đi 1, ta đưa bài toán về tìm số luỹ thừa của 2 nhỏ nhất mà lớn hơn hẳn n - 1.
Ví dụ:
Nếu n = 8, ta trừ 1 để n thành 7. Mục tiêu là tìm số luỹ thừa của 2 nhỏ nhất mà lớn hơn 7, tức tìm 8.
Nếu n = 7, ta trừ 1 để n thành 6. Mục tiêu là tìm số luỹ thừa của 2 nhỏ nhất mà lớn hơn 6, tức tìm 8.
Bước 2: dịch phải k bit và phép toán OR
Thao tác này nhằm làm cho các bit từ vị trí p xuống đến vị trí 0 đều trở thành bit 1.
Ví dụ:
Với n = 7, sau khi trừ 1, n = 6.
n |= n >> 1 => 00000110 | 00000011 = 00000111 (bằng 7)
n |= n >> 2 => 00000111 | 00000001 = 00000111 (bằng 7)
n |= n >> 4 => 00000111 | 00000000 = 00000111 (bằng 7)
n |= n >> 8: ...
Kết quả cuối cùng là 00000111, tức bằng 7.
Bước 3: cộng 1 vào n
Sau khi n có dạng 0...00111...1, ta cộng thêm 1 để làm cho các bit 1 trở thành bit 0 và bit ngay bên trái của chúng đang từ 0 trở thành 1. Đây chính là số luỹ thừa của 2 cần tìm.
Ví dụ:
00000111 (bằng 7) + 1 = 00001000 (bằng 8).
Viết chương trình¶
Mã nguồn¶
Code đầy đủ được đặt tại GitHub.