Đếm từ ba pha của lời giải
Gọi H(n) là số lần chuyển đĩa tối thiểu, với n≥1. Một đĩa cần H(1)=1. Với n>1, hai pha chuyển chồng n−1 đĩa cần H(n−1) lần mỗi pha; chuyển đĩa lớn nhất thêm một lần:
$$H(n)=2H(n-1)+1.$$
Để thấy vì sao không thể dùng ít hơn, xét đĩa lớn nhất. Trước lần chuyển đầu tiên của nó, n−1 đĩa nhỏ phải được gom trên cọc còn lại, cần ít nhất H(n−1) lần. Sau lần cuối đưa đĩa lớn nhất vào đích, lại phải chuyển chồng n−1 đĩa nhỏ lên nó, cần ít nhất H(n−1) lần. Đĩa lớn nhất phải chuyển ít nhất một lần. Cách giải ba pha đạt đúng tổng đó, nên đạt số lần chuyển tối thiểu.
Từ truy hồi đến công thức trực tiếp
Các giá trị đầu là:
| n | H(n) |
|---|---|
| 1 | 1 |
| 2 | 3 |
| 3 | 7 |
| 4 | 15 |
| 5 | 31 |
Công thức tương ứng là $H(n)=2^n-1$. Có thể kiểm bằng quy nạp: n=1 cho $2^1-1=1$. Giả sử đúng tại n, khi đó
$$H(n+1)=2H(n)+1=2(2^n-1)+1=2^{n+1}-1.$$
Vậy công thức đúng cho các số nguyên n≥1.
Hàm đếm không cần tạo mọi lần chuyển
Có thể viết riêng hàm tính số lần chuyển theo truy hồi:
```python
def H(n):
if n == 1:
return 1
return 2 * H(n - 1) + 1
```
Hàm này nhận n≥1 và chỉ trả một số. Với n=64, kết quả là 18 446 744 073 709 551 615. Tính con số ấy bằng hàm đếm hoặc 2**64−1 không có nghĩa đã thực hiện hay lưu từng ấy lần chuyển. Bản tạo danh sách phải lưu toàn bộ các bộ ba, nên không dùng để thử 64 đĩa.
Chỉ tính số bước cho trường hợp lớn
Dùng tệp Python ở bài trước và chạy:
```text
python3 g11-cs-thap-ha-noi.py 64 --count-only
```
Cần nhận đúng dòng `H(64) = 18446744073709551615`, không có dòng chuyển đĩa. Cờ --count-only chọn hàm đếm và bỏ phần tạo danh sách. Mẫu chế độ này nhận n từ 1 đến 512 để kiểm nhỏ với giới hạn đệ quy Python đang dùng; giới hạn của mẫu không phải giới hạn toán học của công thức.
Với n=4, có thể so sánh số đếm 15 với độ dài danh sách của lời giải nhỏ. Không tăng giới hạn đệ quy hoặc chạy bộ liệt kê với n lớn để kiểm một công thức có thể tính trực tiếp.
Bài luyện
Theo H(1)=1 và H(n)=2H(n−1)+1, H(4) bằng bao nhiêu?
16 là 2^4, còn thiếu phép trừ 1.
H(3)=7 nên H(4)=2×7+1.
7 là H(3), chưa phải H(4).
8 chỉ là 2^3.
Lời giải
Từ H(1)=1 suy ra H(2)=3, H(3)=7, H(4)=2×7+1=15. Cũng có thể tính 2^4−1=15.
Bài luyện
Số 1 trong H(n)=2H(n−1)+1 ứng với việc gì?
Công thức đang đếm lần chuyển đĩa.
Không có thao tác thêm đĩa vào dữ liệu.
Hai hạng H(n−1) đếm hai chồng nhỏ; còn lại là đĩa n.
Quy tắc không cho chuyển cả chồng cùng một lần.
Lời giải
Lời giải gồm chuyển n−1 đĩa nhỏ đi, chuyển đĩa n một lần, rồi chuyển n−1 đĩa nhỏ tới đích. Vì vậy số 1 đếm lần chuyển đĩa lớn nhất ở giữa.
Bài luyện
Chạy mẫu với 64 --count-only và nhận H(64). Kết luận nào đúng?
Một số kết quả không chứng minh đã mô phỏng toàn bộ.
Mã không tạo danh sách khi có cờ --count-only.
Số lần chuyển là 2^64−1, không phải số đĩa.
Chế độ này chỉ gọi hàm đếm và bỏ phần tạo danh sách.
Lời giải
Hàm đếm trả một số nguyên theo truy hồi. Cờ --count-only bỏ nhánh tạo và in danh sách, nên không có bằng chứng đã thực hiện hay lưu mọi lần chuyển của 64 đĩa.
Đang học