Try   HackMD

Thuật toán tính

ab bình thường nếu chỉ chạy
b
lần rồi nhân
a
vào kết quả sẽ có độ phức tạp
O(b)
, không hiệu quả với
b
lớn. Đối với thuật toán lũy thừa nhị phân dùng chia để trị chỉ mất độ phức tạp
O(logb)
, cực kì hiệu quả.

Thuật toán

Xét ví dụ cần tính

520. Ta có thể phân tích
520=(510)2
. Như vậy ta chỉ cần tính
510
sau đó bình phương lên là được kết quả. Tương tự với
510=(55)2
, ta chỉ cần tính
55
rồi bình phương lên để được
510
. Với
55
, ta có thể phân tích thành
545=(52)25
. Và
52=52
. Cuối cùng
5=505
, và ta có base case
50=1

Vậy ta suy ra được công thức sau:

ab={1nếu b=0(ab2)2nếu b>0 và b chẵn(ab2)2anếu b>0 và b lẻ

long long binpow(long long a, long long b) {
    if(b == 0)
        return 1;
    long long res = binpow(a, b / 2);
    res *= res; // res ^ 2
    if(b % 2 == 1)
        res *= a;
    return res;
}

Một cách khác không dùng đệ quy:
Xét ví dụ

513. Trong hệ nhị phân (bits),
13=11012=10002+1002+12=8+4+1513=58+4+1=585451
(
ab+c=abac
).
Vì trong hệ nhị phân, mỗi bit
1
tượng trưng cho một lũy thừa của 2 nên ta sẽ được tích của các lũy thừa với các số mũ là lũy thừa 2.
Xét các bit từ phải qua trái, bit thứ 0 (bit đầu tiên) tượng trưng cho
5(20)
, bit thứ 1 tượng trưng cho
5(21)
, bit thứ 2 tượng trưng cho
5(22)
, , bit thứ i tượng trưng cho
5(2i)
.
Nếu bit thứ 0 được bật (= 1), ta sẽ nhân
5(20)
vào kết quả, bit thứ 1 được bật thì ta sẽ nhân
5(21)
vào kết quả, bit thứ i được bật thì ta sẽ nhân
5(2i)
vào kết quả.

long long binpow(long long a, long long b) {
    long long res = 1;
    while(b > 0) {
        if(b & 1) // nếu bit i được bật
            res *= a;
        a *= a; // bình phương a hiện tại để tới bit tiếp theo
        b >>= 1; // dịch 1 bit sang bên phải (tới bit tiếp theo)
    }
    return res;
}

Thông thường, các bài toán sẽ yêu cầu ta tính

abmodm do lũy thừa là một số có thể rất lớn. Ta cũng có thể áp dụng mod vào lũy thừa nhị phân.