You are on page 1of 16

ĐỆ QUY (RECURSION)

Giới thiệu đệ quy (recursion)


• Một hàm đệ quy (recursive function) là hàm chứa lời gọi đến chính nó
• Ví dụ: Factorial (tính giai thừa)
• Factorial(4) = 4 * Factorial(3)
• Factorial(3) = 3 * Factorial(2)
• Factorial(2) = 2 * Factorial(1)
• Factorial(1) = 1 * Factorial(0)
• Factorial(0) = 1
• Lời giải cho bài toán với kích cỡ n được tính từ lời giải cho bài toán
con có kích cỡ nhỏ hơn cùng loại.
• Quá trình này được lặp lại cho đến khi bài toán con trở nên rất đơn
giản để có thể xử lý trực tiếp.
2
Giới thiệu đệ quy
• Một lời giải đệ quy có 2 phần chính
• Phần cơ sở (base case)
• Có thể giải (tính) trực tiếp mà không cần gọi đệ quy
• Phần đệ quy (recursive case)
• Cần lời gọi đệ quy để giải (tính)
• Giải pháp đệ quy cần đảm bảo có thể đi đến được phần cơ
sở
• Nếu không sẽ bị gọi đệ quy vô hạn

3
Ví dụ: factorial

factorial(n) Full code factorial(n)


• Base Case long factorial(int k) {
• trả về 1 khi n = 0 if (k == 0)
• Code: if (k == 0) return 1;
return 1;
• Recursive Case
• trả về n * (n-1)!
else
• Code: return k * factorial(k-1); return k * factorial(k-1);
}

4
Hàm đệ quy
• Một lời giải đệ quy luôn bao gồm 2 giai đoạn
• Giai đoạn đệ quy: khi trường hợp cơ sở chưa thỏa mãn
• Giai đoạn này sẽ được thực hiện cho đến khi đi đến trường hợp cơ sở
• Giai đoạn cơ sở: Các hàm được gọi đệ quy sẽ trả về giá trị của
chúng cho các lời gọi đệ quy trước đó
• Hàm cuối cùng trả về giá trị cho hàm cuối thứ 2, hàm cuối thứ 2 trả về giá
trị cho hàm cuối thứ 3, …
• Cuối cùng, nó đạt đến hàm đầu tiên và tính được giá trị cuối cùng

5
Ví dụ: factorial

Factorial(4) n ≠ 0, return 4 * Factorial(3)


return 4 * 6
n=4
= 24
Factorial(3) n ≠ 0, return 3 * Factorial(2)
return 3 * 2 n=3
=6
Factorial(2) n ≠ 0, return 2 * Factorial(1)
return 2 * 1 n=2
=2
long factorial(int k) {
Factorial(1) n ≠ 0, return 1 * Factorial(0)
if (k == 0) return 1 * 1
n=1
return 1; =1
else Factorial(0)
return k * factorial(k-1); n = 0, return 1
n=0
}
6
Đệ quy và lặp
• Nhiều hàm đệ quy đơn giản thực hiện một vòng lặp
• Lời giải dùng đệ quy thường gọn và đẹp hơn lời giải dùng
vòng lặp
• Nó tự nhiên và đơn giản hơn về mặt khái niệm
• Nó thường dễ hiện thực hơn
• Lời giải dùng vòng lặp thường nhanh hơn lời giải dùng đệ
quy
• Nếu có thể chuyển lời giải dùng đệ quy thành lời giải dùng vòng
lặp thì ta nên làm như vậy
7
Lời giải dùng đệ quy và vòng lặp

Lời giải dùng đệ quy cho hàm Factorial Lời giải dùng vòng lặp cho hàm Factorial
long factorial(int k) { long factorial(int k) {
if (k == 0) long t = 1;
return 1; for (int j = 2; j <= k; j++)
else t *= j;
return k * factorial(k-1); return t;
} }

8
Ví dụ: Tính số Fibonacci
• Fibonacci
• Fibo(n) = C(n-1) + C(n-2)
• Fibo(1) = Fibo(2) = 1
• Lời giải đệ quy
int fibo(int n) {
if (n <= 2)
return n;
else
return fibo(n-1) + fibo(n-2);
}

9
Ví dụ: Tính fibo(5), ta sẽ viết f(5) cho gọn
f(5)
return f(4) + f(3)

f(4) f(3)
return f(3) + f(2) return f(2) + f(1)

f(3) f(2) f(2) f(1)


return f(2) + f(1) return 1 return 1 return 1

f(2) f(1)
return 1 return 1
10
Ví dụ: Tính tổ hợp (Combinatorial)
• Tính số cách chọn k phần tử từ n phần tử
• C(k, n) = C(k-1, n-1) + C(k, n-1) Chọn phần tử đầu tiên Chọn k-1 từ n-1 phần tử
• C(0, n) = C(n, n) = 1
Chọn k từ n phần tử
• Lời giải đệ quy
int comb(int k, int n) { Không chọn phần tử đầu tiên Chọn k từ n-1 phần tử

if (k > n) return 0;
if (k == n || k == 0) return 1;
return comb(k-1, n-1) + comb(k , n-1);
}
11
Ví dụ: Tính C(3, 4)
C(3, 4)
return C(2, 3) + C(3, 3)

C(2, 3) C(3, 3)
return C(1, 2) + C(2, 2) return 1

C(1, 2) C(2, 2)
return C(0, 1) + C(1, 1) return 1

C(0, 1) C(1, 1)
return 1 return 1
12
Ví dụ: Tháp Hà Nội (Tower of Hanoi)
• Ta muốn di chuyển tất cả đĩa từ cột 1 (nguồn) sang cột 3 (đích), dùng
cột 2 như cột tạm
• Mỗi lần chỉ di chuyển một đĩa
• Mỗi đĩa không được nằm trên đĩa nhỏ hơn

13
Tower of Hanoi – Lời giải đệ quy

14
Tower of Hanoi – Lời giải đệ quy
void tower(int n, char s, char d, char t) {
if (n == 1)
move(s, d);
else {
tower(n-1, s, t, d);
move(s, d);
tower(n-1, t, d, s);
}
}
void move(char s, char d) {
cout << "move from " << s << " to " << d << endl;
}

15
Tóm tắt
• Đệ quy là một cách tiếp cận để mạnh mẽ để thiết kế lời giải và giải
quyết vấn đề
• Một hàm đệ quy có phần cơ sở và phần đệ quy
• Lời giải đệ quy thường gọn gàng và tinh tế nhưng có thể không hiệu
quả do có thể có nhiều lời gọi lặp lại
• Lời giải dùng vòng lặp thường hiệu quả hơn lời giải đệ quy nhưng có
thể khó hiện thực và khó hiểu hơn

16

You might also like