Tính nửa số mũ rồi bình phương
Xét $a\ne0$ và số nguyên n≥0. Cơ sở là $a^0=1$. Nếu n=2k thì $a^n=(a^k)^2$; nếu n=2k+1 thì $a^n=a(a^k)^2$.
Vì vậy chỉ cần tính một lần $b=a^{\lfloor n/2\rfloor}$, rồi dùng b ở cả hai thừa số. Việc lưu b giúp tránh tính lại cùng một bài toán nhỏ.
Một lời gọi nhỏ ở mỗi mức
```python
def power_fast(a, n):
if n == 0:
return 1
b = power_fast(a, n // 2)
if n % 2 == 0:
return b * b
return a * b * b
```
Với a=2,n=13, các lời gọi đi qua số mũ 13,6,3,1,0. Khi trả về:
| n | b đã có | Kết quả |
|---|---|---|
| 0 | Cơ sở | 1 |
| 1 | 1 | 2×1×1=2 |
| 3 | 2 | 2×2×2=8 |
| 6 | 8 | 8×8=64 |
| 13 | 64 | 2×64×64=8192 |
Nhánh chẵn có một phép nhân; nhánh lẻ có hai phép nhân trong biểu thức a*b*b. Với đoạn mã đang hiển thị, ví dụ này có 7 phép nhân, kể cả các phép nhân với 1 ở n=1.
Đếm đúng cách cài đặt đang xét
Bản nhân lần lượt khởi đầu p=1 rồi nhân p với a đúng n lần, nên có n phép nhân; với n=13 là 13 lần. Khi tính tay có thể bắt đầu ngay từ a và bỏ một phép nhân với 1, nên số đếm khác với mã khởi đầu p=1.
Theo mô hình của sách coi mỗi phép tính cơ bản có chi phí hằng số, bản nhanh chỉ có một bài toán nhỏ với số mũ n//2 và không quá hai phép nhân để kết hợp. Số mức tăng theo $\log n$, cho bậc $O(\log n)$ thay vì $O(n)$ của bản nhân lần lượt. Số đếm ấy không tự là số mili giây thực chạy.
Nếu viết hai lời gọi power_fast(a,n//2) riêng để nhân với nhau, cùng kết quả nhỏ bị tính lại; đó không còn là cách lưu b đang phân tích.
Bài luyện
Với hàm power_fast trong bài, power_fast(2,13) trả về bao nhiêu?
64 mới là kết quả của số mũ 6.
Bài toán nhỏ cho b=2^6=64, rồi nhánh lẻ trả 2×64×64.
4096 là 2^12, còn thiếu thừa số 2.
26 là 2×13, khác lũy thừa.
Lời giải
13 lẻ nên b=power_fast(2,6)=64. Kết quả là 2×64×64=8192.
Bài luyện
Vì sao cần lưu b=power_fast(a,n//2) rồi dùng b*b?
Vẫn phải bình phương và có thể nhân thêm a.
Đọc biến b không tạo một lời gọi đệ quy mới.
Hai thừa số b dùng chung giá trị đã tính.
n//2 đang làm số mũ nhỏ hơn khi n>0.
Lời giải
Bài toán nhỏ có cùng cơ số và số mũ n//2. Sau một lời gọi, b giữ giá trị ấy; b*b dùng lại b, thay vì gọi hai lần để tính cùng kết quả.
Bài luyện
Với a=−3 và n=0, cơ sở của power_fast trả gì?
0 làm sai cơ sở của phép nhân.
−3 là lũy thừa số mũ 1.
Đã đạt cơ sở nên không gọi tiếp.
Với cơ số khác 0, lũy thừa số mũ 0 bằng 1.
Lời giải
Điều kiện a≠0 được thỏa. Khi n=0, hàm trả 1 ngay theo cơ sở a^0=1, không thực hiện lời gọi nhỏ.
Đang học