Kỹ thuật lập trình Bài 3 – Giải thuật đệ quy
Ngô Hữu Dũng
61 Kỹ thuật lập trình | DHTH11C | HK1 | 2016-2017 Ngô Hữu Dũng
Quy nạp toán học
Chứng minh hàm F(n) đúng với mọi số tự nhiên n ≥ N0. Phương pháp quy nạp:
Bước cơ sở: Chỉ ra trường hợp ban đầu F(N0) đúng Bước quy nạp: Chứng minh rằng giả sử F(k) đúng thì F(k+1) đúng.
Ví dụ: Chứng minh S(n): 1 + 3 + 5 + 7 + …+ (2n-1) = n2 (*), với n ≥ 1
Bước cơ sở: Trường hợp n = 1, ta có (2n-1) = n2 = 1 là đúng hiển nhiên. Bước quy nạp: Giả sử S(k) đúng, ta có 1 + 3 + 5 + ... + (2k – 1) = k2 Khi đó: S(k+1): 1 + 3 + 5 + ... + (2k – 1) + [2(k+1) – 1] = k2 + (2k + 1)
= (k + 1)2
Vậy S(k + 1) = (k + 1)2 là đúng. Suy ra S(n) đúng với mọi n ≥ 1.
62 Kỹ thuật lập trình | DHTH11C | HK1 | 2016-2017 Ngô Hữu Dũng
Lập trình đệ quy
Lập trình tính S(n) = 1 + 3 + 5 + … + (2n – 1) = n2 với n ≥ 1. Từ bài toán quy nạp ta có:
Bước cơ sở: S(1) = 1 Bước quy nạp: S(k+1) = S(k) + [2(k + 1) – 1] hay S(k) = S(k – 1) + (2k – 1)
Phương pháp lập trình đệ quy:
Phần cơ sở: S(1) = 1 Phần đệ quy: S(n) = S(n – 1) + (2n – 1)
Áp dụng vào lập trình:
1. int S(int n) 2. { 3.
if (n==1) return 1; else return S(n-1) + (2*n – 1);
4. 5. }
63 Kỹ thuật lập trình | DHTH11C | HK1 | 2016-2017 Ngô Hữu Dũng
1. int S(int n) 2. { 3.
if (n==1) return 1; else return S(n-1) + (2*n–1);
Cách hoạt động Giả sử n = 5, hàm đệ quy chạy như sau: S(5) = S(4) + 9 // Gọi hàm S(4)
4. 5. }
S(4) = S(3) + 7 // Gọi hàm S(3)
// Gọi hàm S(2)
S(3) = S(2) + 5
// Gọi hàm S(1)
// Return S(1)
S(2) = S(1) + 3 S(1) = 1
S(2) = 1 + 3 // Return S(2) // Return S(3)
S(3) = 1 + 3 + 5
S(4) = 1 + 3 + 5 + 7
// Return S(4) S(5) = 1 + 3 + 5 + 7 + 9 = 25 = 52 // Return S(5)
64 Kỹ thuật lập trình | DHTH11C | HK1 | 2016-2017 Ngô Hữu Dũng
Khái niệm đệ quy
Khái niệm
Một hàm gọi là đệ quy khi có phép gọi lại chính nó trong thân hàm
Thành phần
Phần cơ sở: Điều kiện thoát Phần đệ quy: Có phép gọi lại chính nó
Ưu điểm
Thuận lợi trong việc giải những bài toán có tính chất quy nạp Làm gọn chương trình
Nhược điểm
Không tối ưu, tốn bộ nhớ
65 Kỹ thuật lập trình | DHTH11C | HK1 | 2016-2017 Ngô Hữu Dũng
Một số loại đệ quy Đệ quy tuyến tính (Linear Recursion) Hàm đệ quy chỉ gọi lại chính nó một lần
Đệ quy nhị phân, đa đệ quy (Binary Recursion, Multiple Recursion)
Hàm đệ quy gọi lại chính nó hai lần hoặc nhiều hơn hai lần
Đệ quy đuôi (Tail Recursion)
Lời gọi đệ quy được thực hiện ở cuối hàm đệ quy
Đệ quy lồng (Nested Recursion)
Tham số của một lời gọi đệ quy là một lời gọi đệ quy
Đệ quy hỗ tương (Mutual Recursion)
Hai hàm đệ quy gọi lẫn nhau Đệ quy quay lui (Backtracking) Là hàm đệ quy có phép thử và sai
66 Kỹ thuật lập trình | DHTH11C | HK1 | 2016-2017 Ngô Hữu Dũng
Đệ quy tuyến tính
1. int Fact(int n) 2. { 3.
if (n==1)
4.
return 1;
Chỉ có một lời gọi đệ quy Được dùng thông dụng Ví dụ
5.
else
Tính giai thừa Fact(n) với n > 0
return n * Fact(n-1);
6. 7. }
Fact(n) = 1 * 2 * 3 *… * n Phần cơ sở: Fact(1) = 1 Phần đệ quy: Fact(n)=n*Fact(n-1)
Tính T(n) = 1 + 2 + 3 + … + n
1. int T(int n) 2. { 3.
if (n==1)
4.
return 1;
Phần cơ sở: T(1) = 1 Phần đệ quy: T(n) = T(n-1) + n
5.
else
return T(n-1) + n;
6. 7. }
67 Kỹ thuật lập trình | DHTH11C | HK1 | 2016-2017 Ngô Hữu Dũng
Đệ quy nhị phân
Hàm đệ quy có hai lời gọi đệ quy tại một thời điểm Thường dùng trong các bài toán kiểu cấu trúc cây Ví dụ: Tính số Fibonacci thứ n, với n > 0
Fib(1) = 1, Fib(2) = 1 Fib(n) = Fib(n-1) + Fib(n-2)
1. int Fib(int n) 2. { 3.
if (n==1 || n==2)
4.
return 1;
5.
else
return Fib(n-1)+Fib(n-2);
6. 7. }
Fib(1)
Fib(2)
Fib(3)
Fib(4)
Fib(5)
Tính Fib(5) = ? = Fib(4)+Fib(3) = Fib(3)+Fib(2) + Fib(2)+Fib(1) = Fib(2)+Fib(1)+1+1+1 = 5
1
1
2
3
5
68 Kỹ thuật lập trình | DHTH11C | HK1 | 2016-2017 Ngô Hữu Dũng
Đệ quy nhiều lần
Lời gọi đệ quy được thực hiện
nhiều lần trong hàm
1. int mystery(int a, int b) 2. {
3.
if (b == 0)
Ví dụ: Tính hàm mystery Hàm có hai lời gọi đệ quy Hàm trả về kết quả ?
4.
return 0;
5.
if (b % 2 == 0)
Tính mystery(2,4) = ?
6.
return mystery(a+a, b/2);
7.
else
8.
return mystery(a+a, b/2)+a;
9. }
= mystery(4, 2) = mystery(8, 1) = mystery(16, 0) + 8 = 8 Vậy mystery(2,4) = ?
69 Kỹ thuật lập trình | DHTH11C | HK1 | 2016-2017 Ngô Hữu Dũng
Đệ quy nhiều lần (tt)
Thay dòng số 4 thành return 1;
và thay dấu + bằng dấu *
Hàm trả về kết quả?
1. int mystery(int a, int b) 2. {
3.
if (b == 0)
4.
return 1;
Tính mystery(2,4) = ?
5.
if (b % 2 == 0)
6.
return mystery(a*a, b/2);
7.
else
8.
return mystery(a*a, b/2)*a;
= mystery(4,2) = mystery(16,1) = mystery(256,0)*16 = 16
9. }
Vậy mystery(2,4) = ?
70 Kỹ thuật lập trình | DHTH11C | HK1 | 2016-2017 Ngô Hữu Dũng
Đệ quy đuôi
Hàm đệ quy có lời gọi đệ quy ở cuối hàm Ví dụ
Tính số Fibonacci thứ n, với n > 0
Tính Fib(5,1,1) = ?
1. int Fib(int n, int x, int y) 2. { 3.
if (n==1)
4.
return x;
5.
else
return Fib(n-1, y, x+y);
6. 7. }
Fib(1)
Fib(2)
Fib(3)
Fib(4)
Fib(5)
= Fib(5,1,1) = Fib(4,1,2) = Fib(3,2,3) = Fib(2,3,5) = Fib(1,5,8) = 5
1
1
2
3
5
71 Kỹ thuật lập trình | DHTH11C | HK1 | 2016-2017 Ngô Hữu Dũng
Đệ quy lồng
Lời gọi đệ quy là tham số của một lời gọi đệ quy Ví dụ