Kỹ thuật đệ quy

21 528 1
Kỹ thuật đệ quy

Đang tải... (xem toàn văn)

Tài liệu hạn chế xem trước, để xem đầy đủ mời bạn chọn Tải xuống

Thông tin tài liệu

Chương 1 Kỹ thuật đệ quy  1.1 Kỹ thuật đệ quy Đệ quy là một thuật toán dùng để đơn giản hóa những bài toán phức tạp bằng cách phân nhỏ phép toán đó thành nhiều phần đồng dạng. Qua việc giải những bài toán được phân nhỏ này, những lời giải sẽ được kết hợp lại để giải quyết bài toán lớn hơn. Một số các ví dụ đệ quy • Định nghĩa số tự nhiên o 0 là số tự nhiên o N là số tự nhiên n-1 là số tự nhiên • Định nghĩa giai thừa của n o 0! là 1 o Nếu n>0, n! = n *(n-1)! Hàm đệ quy : Hàm đệ quy là một hàm trong đó có dùng lời gọi hàm đến chính bản thân nó. Ví dụ ta có hàm đệ quy như sau: int Sum(int n) { if (n==0) return 0; else return (n+Sum(n-1)); // gọi đệ quy đến chính bản thân hàm sum } Khi một hàm đệ quy gọi đến chính nó thì mỗi lần gọi máy sẽ tạo ra tập các biến cục bộ mới hoàn toàn độc lập với biến cục bộ đã tạo ra trong lần gọi trước. Bao nhiêu lần gọi hàm đệ quy thì tương ứng với bấy nhiêu lần thoát ra khỏi hàm, mỗi lần ra khỏi hàm thì tập biến cục bộ bị xóa. Có một sự tương ứng giữa các lời gọi hàm và lần thoát khỏi hàm theo thứ tự ngược lại: lần ra khỏi hàm đầu tiên tương ứng với lần gọi hàm cuối cùng. Ví dụ minh họa hàm đệ quy: tính giai thừa của n (tích của các số từ 1 đến n). Ta có định nghĩa của giai thừa n như sau: n! = 1.2.3 .(n-1).n hoặc định nghĩa: n! =    ≥− = 1)!.1( 01 nnn n Phương pháp thứ nhất là dùng vòng lặp: long GT(int n) { long result = 1; for(int i=1; i <= n; i++) result *= i; return result; } Phương pháp thứ hai là dùng hàm đệ quy: long Giaithua(int n) { if (n == 0) return 1; else return (n*Giaithua(n-1)); } Phân tích chương trình thực hiện đệ quy: Giả sử chương trình có lời gọi hàm như sau long l = Giaithua(5); n = 5return 5* Giaithua(4) n = 4return 4* Giaithua(3) n = 3return 3* Giaithua(2) n = 2return 2* Giaithua(1) n = 1return 1* Giaithua(0) long l = Giaithua(5) 1 2 6 24 120 Giaithua(5) Giaithua(4) Giaithua(3) Giaithua(2) Giaithua(1) n = 0return 1 Giaithua(0) 1 Hình 2.1: Gọi đệ quy của hàm giai thừa. Lưu ý: Hàm đệ quy dùng nhiều vùng nhớ trên ngăn xếp do đó có thể dẫn đến tràn ngăn xếp. Do đó nếu một bài toán có thể dùng phương pháp lặp (không đệ quy) để giải quyết thì nên sử dụng cách này. Phân loại hàm đệ quy:  Đệ quy trực tiếp : trong một hàm có lời gọi hàm đến chính bản thân hàm đó. - Đệ quy tuyến tính : thân hàm gọi một lần đến chính nó: U n a, n =1 r + U n-1 , n>1 double U(int n, double a, double r) { if (n == 1) return a ; return r + U(n-1, a, r) ; } - Đệ quy nhị phân : thân hàm có hai lần gọi chính nó U n 1, n =1, 2 U n-2 + U n-1 , n>2 long Fibo(int n) { if (n<2 ) return 1 ; return Fibo(n-1) + Fibo(n-1) ; } - Đệ quy phi tuyến : thân hàm gọi nhiều lần đến nó U n n, n < 6 U n-5 + U n-4 U n-3 + U n-2 + U n-1 , n>=6 long U( int n) { if (n<6) return n; long S= 0; for (int i = 5; i>0; i--) S+= U(n-i); return S; } - Đệ quy hỗ tương: hai hàm đệ quy gọi nhau U n n, n <5 U n-1 + G n-2 , n>=5 G n n-3, n <8 U n-1 + G n-2 , n>=8 long G(int n); long U( int n) { if (n<5) return n; return U(n-1) + G(n-2); } long G(int n) { if (n<8) return n-3; return U(n-1) + G(n-2); }  Đệ quy gián tiếp : trong một hàm có lời gọi hàm đến một hàm khác và bên trong hàm này lại có lời gọi hàm đến hàm ban đầu. Ví dụ như hàm F 1 gọi hàm F 2 và bên trong hàm F 2 lại có lời gọi hàm đến F 1 . Đây được gọi là sự đệ quy gián tiếp. Thông thường những dạng chương trình đệ quy gián tiếp thì khó theo dõi và gỡ rối, nên khi xây dựng chương trình loại này phải hết sức cẩn thận. 1.2 Xây dựng một chương trình đệ quy Phương pháp đệ quy thường được áp dụng cho những bài toán phụ thuộc tham số và có các đặc điểm sau: 1. Bài toán dễ dàng giải quyết trong một số trường hợp riêng ứng với các giá trị đặc biệt nào đó của tham số. Trường hợp này gọi là suy biến. Ví dụ như khi tính giai thừa thì giai thừa của 0 là 1. 2. Trong trường hợp tổng quát, bài toán quy về cùng một dạng nhưng giá trị tham số được thay đổi. Sau một số lần hữu hạn các bước biến đổi đệ quy thì bài toán trở về trường hợp suy biến. Ví dụ như n! = (n-1)!. n, khi đó n giảm về 0 thì xảy ra trường hợp suy biến. Các hàm đệ quy thường có dạng tổng quát như sau: if (Trường hợp đặc biệt, suy biến) { // giải theo cách suy biến, trường hợp này đã có lời giải } else // trường hợp tổng quát. { // gọi đệ quy với giá trị tham số khác (thay đổi tham số) } Ví dụ 1: Tính tổng các số nguyên từ 1 đến N. ∑∑∑ − − − = +−+=+= 2 1 1 1 )1( N i N i N i iNNiNi 1 1 1 12 2 2 23 3 3 3 Ta phân tích như sau: + Trường hợp đặc biệt N=1 thì kết quả là 1 + Trường hợp khác ta thực hiện đệ quy: N + Tong(N-1). Ví dụ 2: tìm USCLN của hai số nguyên dương a, b. + Trường hợp đặc biệt khi a = b khi đó USCLN(a, b) = a + Trường hợp chung a và b khác nhau ta có thể thực hiện đệ quy như sau: - USCLN(a, b) = USCLN(a-b, b) nếu a>b - USCLN(a, b) = USCLN(a, b-a) nếu a<b. Hàm tìm USCLN đệ quy được viết như sau: int USCLN(int a, int b) { if (a==b) return a; else if (a>b) return USCLN(a-b, b); else return USCLN(a, b-a); } Ví dụ 3: Tính a n . + Trường hợp đặc biệt n = 0, kết quả là 1 + Trường hợp khác, kết quả là a * a (n-1) . 1.3 Các ví dụ đệ quy Trong phần này chúng ta sẽ tìm hiểu một số chương trình đệ quy như sau:  Tháp Hanoi (Tower of Hanoi) : Cho 3 cột tháp được đặt tên là C 1 , C 2 , và C 3 . Có N đĩa có đường kính giảm dần và được sắp như hình vẽ. Hãy dịch chuyển N đĩa đó sang cột C 2 , theo nguyên tắc sau: mỗi lần chỉ dịch được một đĩa, không được để một đĩa có đường kính lớn nằm trên đĩa có đường kính nhỏ. Ta phân tích cách thực hiện như sau: Với N = 2: ta có cách làm như sau: chuyển đĩa bé nhất sang C 3 , chuyển đĩa lớn sang C 2 , chuyển đĩa nhỏ từ C 3 sang C 2 . C1 C2 C3 1, 2 qua cọc 3 1, 2 qua cọc 2 3 qua cọc 2 B1 B2 B3 C1 C2 C3 C1 C2 C3 C1 C2 C3 C1 C2 C3 C1 C2 C3 C1 C2 C3 C1 C2 C3 C1 C2 C3 B1.1 B1.2 B1.3 B3.1 B3.2 B3.3 Hình 2.2: Minh họa tháp Hanoi với n =2. Với N = 3: ta thực hiện với giả thiết đã biết cách làm với N-1 đĩa (2 đĩa trong ví dụ N=3): chuyển đĩa 1 và 2 sang cọc 3, chuyển đĩa 3 sang cọc 2, chuyển hai đĩa 1, 2 từ cọc 3 sang cọc 2. Hình 2.3: Minh họa trường hợp N = 3. C 2 C 3 C 1 C1 C2 C3 Trong trường hợp N = 3 như hình 2.3, thực hiện ba bước để đưa 3 đĩa về cọc 2: gồm B1, B2 và B3. Với B2 thì đơn giản do chuyển 1 đĩa, còn bước B1 và B3 phải di chuyển nhiều hơn 1 đĩa nên chúng sẽ bao gồm nhiều bước nhỏ trong đó. B1 gồm {B1.1, B1.2, B1.3} và B2 gồm {B2.1, B2.2, B2.3}. Cuối cùng cách thực hiện theo các bước: B1.1 ⇒ B1.2 ⇒ B1.3 ⇒ B2 ⇒ B3.1 ⇒ B3.1⇒ B3.3. Hình 2.4: Tháp Hanoi với n = 4. Chúng ta định nghĩa hàm DichChuyen chuyển N đĩa từ cọc nguồn, sang cọc đích thông qua một cọc trung gian (cọc thứ 3 còn lại). Hàm này định nghĩa như sau: DichChuyen(N, Nguon, Dich, Trung gian); Với N = 2 ta diễn tả lại như sau: DichChuyen(1, C1, C3, C2) DichChuyen(1, C1, C2, C3) DichChuyen(1,C3, C2, C1) Với N = 3 ta diễn tả như sau: thông qua dịch chuyển 2 đĩa DichChuyen(2, C1, C3, C2) DichChuyen(1, C1, C2, C3) DichChuyen(2,C3, C2, C1) Với N tổng quát ta có DichChuyen(N-1, C1, C3, C2) DichChuyen(1, C1, C2, C3) DichChuyen(N-1,C3, C2, C1) Trường hợp N =1 ta chỉ cần dịch từ cọc nguồn tới cọc đích không cần cọc trung gian. Đoạn chương trình C/C++ minh họa như sau: #include <stdio.h> void DichChuyen(int N, int C1, int C2, int C3); int main() { int N; printf(“Nhap so dia: “); scanf(“%d”, &N); DichChuyen(N, 1, 2, 3); return 0; } void DichChuyen(int N, int C1, int C2, int C3) { if (N == 1) printf(“%d - > %d”, C1, C2); else { DichChuyen(N-1, C1, C3, C2); DichChuyen(1, C1, C2, C3); DichChuyen(N-1, C3, C2, C1); } }  Tìm phần tử lớn nhất trong mảng dùng đệ quy : cho mảng a[n], n > 1, hãy tìm phần tử lớn nhất trong mảng a[n]. Ta thử phân tích như sau: ý tưởng là đi từ phần đuôi và so sánh với phần tử cuối cùng của mảng với biến tạm m, chọn ra phần tử lớn nhất ⇒ lưu lại vào m. Bước tiếp theo thực hiện tương tự nhưng lúc này mảng rút ngắn lại còn n-1 phần tử. an m = Max(m, an) Hình 2.5 : Tìm phần tử lớn trong mảng dùng đệ quy Hàm đệ quy tìm phần tử lớn nhất mô tả như sau: giả sử chỉ số mảng tính từ 1. DeQuyMax(int a[N], int n, int &max)// Gỉa sử n > 0  if ( n ==1) {max = a[1] ; return;}  if (max < a[n]) max = a[n];  DeQuyMax(a, n-1, max);  Tính tổng các phần tử trong mảng dùng đệ quy: cho dãy a[1:n], gọi hàm Sum là hàm đệ quy tính tổng, khi đó tổng của dãy a[1:n] là Sum(a[1:n]) Sum(a[1:n]) = Sum(a[1:n-1]) + a[n] [...]... bài toán khó giải quy t theo hướng không đệ quy thì người ta thực hiện quá trình như sau: o Dùng quan niệm đệ quy để tìm giải thuật cho bài toán o Mã hoá giải thuật đệ quy o Khử đệ quy để có một chương trình không đệ quy Quá trình trên gọi là khử đệ quy, đôi khi việc khử đệ quy cũng không dễ dàng gì, nên nhiều khi cũng phải chấp nhận chương trình đệ quy! 1.4.2 Các trường hợp khử đệ quy đơn giản o Hàm... đệ quy Thông thường đệ quy là phương pháp giúp chúng ta tìm giải thuật cho những bài toán khó Kết quả của giải thuật đệ quy thường rất gọn gàng, dễ hiểu và dễ chuyển thành các chương trình trên các ngôn ngữ lập trình Tuy nhiên, việc xử lý giải thuật đệ quy cũng gây khó khăn cho máy về không gian lưu trữ và thời gian xử lý Vì vậy việc thay thế một chương trình đệ quy bằng một chương trình không đệ quy. .. Tổng các phần tử trong mảng Hàm đệ quy mô tả bằng mã giả như sau: Sum(int a[], int n) - if ( n == 1) Sum = a[1]; - else  Sum = Sum(a, n-1) + a[n]; 1.4 Khử đệ quy 1.4.1 Tìm hiểu cơ chế thực hiện hàm đệ quy Tại mỗi thời điểm của hàm đệ quy được đặc trưng bởi: nội dung các biến và các lệnh cần thực hiện tiếp theo Do đó tại mỗi thời điểm trong tiến trình xử lý của hàm đệ quy cần phải lưu trữ cả các trạng... (một hay nhiều biến) P(X): là hàm đệ quy phụ thuộc X A(X) và D(X): là các nhóm lệnh không đệ quy f(X): là hàm biến đổi x trong lần gọi thứ Pi nếu B(fi(X)) không đúng thì thực hiện lệnh X và gọi Pi+1, ngược lại B(fi(X)) đúng thì thực hiện D(X) và kết thúc quá trình gọi (Pi ko gọi thêm hàm đệ quy khác) Ví dụ: Tìm USCLN của hai số dựa vào thuật toán Euclide Giải thuật đệ quy USCLN(m ,n) bằng Euclide như... tính giá trị của dãy dữ liệu mô tả bằng hồi quy: Ví dụ 1: hàm tính giai thừa không đệ quy long int GiaiThua( int n) { long int F =1; for (int k = 1; k 0) { temp = m % 2; Push(S, temp); m = m / 2; } while (! Empty(S)) { Pop(S, temp); printf(“%d”, temp); } } Lệnh gọi đệ quy với hai lần gọi trực tiếp: Thủ tục đệ quy có dạng như sau: P(X) { if C(X) D(X) else { A(X); P(f(X)); B(X); P(g(X)); } } Quá trình thực hiện thủ tục đệ quy P(X) như sau: Nếu... + Fibo(0) Fibo(1) = 1 Fibo(3) = Fibo(2) + Fibo(1) Fibo(0) = 1 Fibo(2) = Fibo(1) + Fibo(0) Fibo(1) = 1 Fibo(1) = 1 Fibo(0) = 1 Hình 2.8: Hàm đệ quy tính dãy Fibonacci Do đặc điểm của quá trình xử lý một hàm đệ quy: việc thực thi lời gọi đệ quy sinh ra lời gọi đệ quy mới cho đến khi gặp trường hợp suy biến, do đó cần phải có cơ chế lưu trữ thông tin thoả yêu cầu: o Ở mỗi lần gọi phải lưu trữ thông tin... theo X A(X), B(X) và D(X) : nhóm lệnh không đệ quy f(X) : là hàm của X Quá trình thực hiện thủ tục P(X) như sau: Nếu C(X) đúng thì thực hiện D(X) Ngược lại thực hiện A(X), gọi P(f(X)), thực hiện B(X) sau khi hoàn thành P(f(X)) Mỗi lần P(Y) được gọi thì thông tin của B(Y) lại được sinh ra nhưng chưa thực hiện Giả sử quá trình đệ quy kết thúc sau k lần gọi đệ quy thì chương trình phải thực hiện dãy k thao... chức các chương trình đệ quy Thực hiện một chương trình con đệ quy theo cách mặc định thường tốn bộ nhớ Do cách tổ chức stack mặc định thích hợp cho mọi trường hợp nên sẽ không tối ưu trong từng trường hợp cụ thể Do đó sẽ tốt khi người lập trình chủ động tạo cấu trúc dữ liệu stack đặc dụng cho từng chương trình đệ quy cụ thể Giả sử thủ tục đệ quy trực tiếp có cấu trúc như sau : P(X) { if C(X) D(X) ; . niệm đệ quy để tìm giải thuật cho bài toán o Mã hoá giải thuật đệ quy o Khử đệ quy để có một chương trình không đệ quy. Quá trình trên gọi là khử đệ quy, . Chương 1 Kỹ thuật đệ quy  1.1 Kỹ thuật đệ quy Đệ quy là một thuật toán dùng để đơn giản hóa những bài toán phức

Ngày đăng: 05/10/2013, 17:20

Hình ảnh liên quan

Hình 2.1: Gọi đệ quy của hàm giai thừa. - Kỹ thuật đệ quy

Hình 2.1.

Gọi đệ quy của hàm giai thừa Xem tại trang 3 của tài liệu.
Hình 2.2: Minh họa tháp Hanoi vớ in =2. - Kỹ thuật đệ quy

Hình 2.2.

Minh họa tháp Hanoi vớ in =2 Xem tại trang 7 của tài liệu.
Hình 2.6: Tổng các phần tử trong mảng. Hàm đệ quy mô tả bằng mã giả như sau: - Kỹ thuật đệ quy

Hình 2.6.

Tổng các phần tử trong mảng. Hàm đệ quy mô tả bằng mã giả như sau: Xem tại trang 11 của tài liệu.
Hình 2.7: Gọi đệ quy hàm GT. - Kỹ thuật đệ quy

Hình 2.7.

Gọi đệ quy hàm GT Xem tại trang 12 của tài liệu.
Hình 2.8: Hàm đệ quy tính dãy Fibonacci. - Kỹ thuật đệ quy

Hình 2.8.

Hàm đệ quy tính dãy Fibonacci Xem tại trang 13 của tài liệu.

Từ khóa liên quan

Tài liệu cùng người dùng

Tài liệu liên quan