Chương trình con đê quy 24

Một phần của tài liệu giáo trình kỹ thuật lập trình nâng cao - trường đh đà lạt (Trang 25 - 31)

Giải thuật đệ quy là giải thuật cĩ chứa thao tác gọi đến nĩ . Giải thuật đệ quy cho phép mơ tả một dãy lớn các thao tác bằng một chương trình con đê quy . Một cách tổng quát một giải thuật đệ quy được biểu diễn như một bộ P gồm các mệnh đề Si (khơng chứa yếu tố đệ quy ) và P : P ≡ P[ Si , P ] .

Phương tiện để mơ tả giải thuật đệ quy trong các ngơn ngữ lập trình là chương trình con đệ quy : chương trình con cĩ chứa lênh gọi đến nĩ trực tiếp hoặc gián tiếp .

Một giải thuật đệ quy cũng cĩ thể dẫn tới một qúa trình gọi đê quy khơng kết thúc ,vì vậy quan tâm đến điều kiện dừng của một giải thuật đệ quy luơn được đặt ra . Để kiểm sốt qúa trình gọi đệ quy chương trình con P người ta thường gắn thao tác gọi P với việc kiểm tra một điều kiện B , qúa trình gọi P sẻ dừng khi B khơng con thỏa.

Mơ hình tổng quát của một giải thuật đệ quy với sự quan tâm đến sự dừng sẻ là : P ≡ if B then P[ Si , P ] hoặc P ≡ P[ Si , if B then P ] Thơng thường với một gỉai thuật đệ quy P , để chỉ ra rằng P sẻ dừng sau n lần gọi ta sẻ chọn B là ( n >0 ) . Mơ hình gỉai thuật đệ quy khi đĩ sẻ cĩ dạng :

P(n) ≡ If ( n > 0 ) then P[ Si , P(n - 1)] ; hoặc P(n) ≡ P[ Si , if (n >0) then P(n - 1) ] ;

Trong các ứng dụng thực tế số lần gọi đệ quy (độ sâu đệ quy) khơng những phải hữu hạn mà cịn phải đủ nhỏ . Bởi vì mỗi lần gọi đệ quy sẽ cần một vùng nhớ mới trong khi vùng nhớ cũ vẫn phải duy trì .

2 . Các chương trình con đệ qui. a) Các hàm đệ quy.

Định nghĩa hàm số bằng đệ quy thường gặp trong tốn học . Dạng phổ biến là các hàm của đối số nguyên mơ tả các dãy số hồi quy .

Ví dụ :

- Dãy các giai thừa : 1 ,1 , 2 , 6 , 24 , 120 , 720 , 5040 , . . . Ký hiệu FAC(n ) = n ! .

Ta cĩ : + FAC(0 ) = 1 ; ( 0 ! = 1 )

+ FAC(n ) = n * FAC(n - 1 ) ; ( n ! = n * (n - 1 ) ! ) với n >= 1 Giải thuật đệ quy tính FAC(n ) là :

FAC(n ) If (n = 0 ) then return 1 ; ≡

else return (n * FAC(n - 1 )) ;

- Dãy số Fibonaci : 1 ,1 , 2 , 3 , 5 , 8 , 13 , 21 , 34 , 55 , 89 , 144 , 233 , 377 , . . .

+ F(0 ) = F(1 ) = 1 ;

+ F(n ) = F(n - 1 ) + F( n - 2 ) ; với n > = 2 Giải thuật đệ quy tính F( n ) là :

F(n) If ((n = 0 ) or ( n = 1 )) then Return 1 ; ≡

- Dãy các tổ hợp : 1 1 2 1 1 3 3 1 1 4 6 4 1 Cn0 = 1 với n > = 0 Cnm = 0 với m > n > 0 Cnm = Cnm−−11+Cnm− với n > m > 0 1

Giải thuật đệ quy tính Cnm là : If ( m = 0 ) then Return 1 ;

else If (m > n ) then Return 0 ;

else Return (Cnm−−11+Cnm− ) ;

1

Nhận xét : Một định nghĩa hàm đệ quy gồm :

+ Một số các trường hợp suy biến mà gía trị hàm tại đĩ đã được biết hoặc cĩ thể tính một cách đơn giản (khơng đệ quy ) .

Ví dụ : FAC(0) = 1 , F(0) = F(1) = 1 , Cn0 = 1 .

+ Trường hợp tổng quát việc tính hàm sẻ đươc đưa về tính hàm ở mức đơn giản hơn .

Ví dụ : FAC(n ) = n * FAC(n - 1 ) ; F(n) = F(n -1) + F( n - 2 ) .

Trong tập biến của hàm cĩ một nhĩm mà độ lớn của nĩ quyết định độ phức tạp của việc tính gía trị hàm . Nhĩm biến đĩ gọi là nhĩm biến điều khiển . Gía trị biên của nhĩm biến điều khiển ứng với trường hợp suy biến . Gía trị của nhĩm biến điều khiển sẻ thay đổi qua mỗi lần gọi đệ quy với xu hướng tiến đến gía trị biên ( tương ứng với các trường hợp suy biến của hàm ).

b) Các thủ tục đệ quy .

Thủ tục đệ quy là thủ tục cĩ chứa lệnh gọi đến nĩ .thủ tục đệ quy thường được sử dụng để mơ tả các thao tác trên cấu trúc dữ liệu cĩ tính đệ quy

Ví dụ :

- Thủ tục bầu cử trong một nước theo phương thức đại cử tri : Từng nhĩm cử tri bầu ra đại cử tri , đại cử tri bầu ra tổng thống.

- Thủ tục chọn 1 phần tử lớn nhất trong một danh sách : chia danh sách làm M danh sách , chọn phần tử lớn nhất trong từng phần , rồi chọn trong những cái đã chọn được.

Thủ tục đệ qui đặc biệt thích hợp khi sử lý trên các cấu trúc dữ liệu cĩ tính đệ qui .

- Một dãy n phần tử a[1:n] là sự kết hợp giữa dãy a[1:n-1] và a[n] . Do do ù:

+ Phép tìm 1 phần tử lớn nhất trong 1 dãy a[1:n] ( thủ tục TMax) cĩ thể được xác định 1 cách đệ qui :

TMax(a[1:n] ) max(a[n],TMax(a[1:n-l]) ) ≡

+ Phép tìm tổng các phần tử (TSUM)

TSUM(a[1:n]) Tg(a[n] ,TSUM(a[1:n-1]) ) ≡

với TSUM(a[m:m]) cho a[m] Tg là phép tìm tổng 2 số .

- Một dãy a[m : (m+n)] là sự kết nối giữa hai dãy: dãy a[m:((m+n) div 2)] và dãy a[(((m+n) div 2)+1) : (m+n)], nên phép tính tổng các phần tử (TSUM) là : TSUM(a[m : (m+n)]) Tg(TSUM(a[m:((m+n) div 2)]) , TSUM(a[((m+n) div 2)+1:n)]))

- Thụ tục quét cây nhi nhân theo thứ tự giữa (LNF) là : + Quét cây con trái theo thứ tự giữa (L) ;

+ Thăm nút gốc (N) ;

+ Quét cây con phải theo thứ tự giữa (F) ; Nhận xét :

Trong một thủ tục đệ qui , để cho việc gọi đệ quy dừng lại sau hữu hạn lần gọi nĩ cần chứa điều kiện kiểm tra ( một biểu thức boolean B trên một nhĩm biến ) , để khi điều kiện này khơng cịn thỏa thì việc gọi đệ qui kết thúc .

Dạng thường gặp của thủ tục đệ qui là : a) S1 ; {khơng chứa yếu tố đệ qui}

if B then S2 {phần lệnh trực tiếp , khơng cĩ lệnh gọi đệ qui} else Sdq ; {phần lệnh cĩ lệnh gọi đệ qui}

S3 ; {khơng cĩ gọi đệ qui} b) S1 ; { Khơng đệ quy }

While B do Sdq ; {phần lệnh cĩ lệnh gọi đệ qui} Sø2 ; {phần khơng cĩ lệnh gọi đệ qui}

3. Hai dạng chương trình con đệ qui :

- Đệ qui trực tiếp : Chương trình con P được gọi là đệ quy trực tiếp nếu trong P cĩ lệnh gọi dến P một cách tường minh .

Ví dụ : Các hàm và thủ tục nêu ở các ví dụ trên .

- Đệ qui gián trực tiếp : Chương trình con P được gọi là đệ quy gián tiếp nếu trong P cĩ lệnh gọi dến P một cách khơng tường minh . Như P gọi Q và Q gọi P. Ví dụ : Procedure A(x : . . . ) ; . . . Begin . . . B(f(x),. . .); . . . end ; Procedure B(y : . . . ); . . . Begin . . .

A(g(y), . . . ); . . .

end ;

Khi thực hiện A cĩ lệnh gọi B khi thực hiện B lại cĩ lệnh gọi A . Cả A và B đều là thủ tục đệ quy gián tiếp .

Trong trường hợp chương trình con P là đệ quy gián tiếp , sự xuất hiện lệnh gọi P cĩ thể xảy ra sau khi đi qua 1 dây chuyền nhiều bước trung gian .

4 . Thể hiện đệ qui trong NNLT PASCAL và C++ :

NN LT Pascal và C++ đều cho phép tổ chức chương trình con đê quy nhờ vào cơ chế tạo vùng nhớ Stak của phần mềm ngơn ngữ .

a) Trong NN LT Pascal :

Pascal cĩ 2 hình thức chương trình con đệ quy là : hàm đê quy và thủ tục đệ quy . Lệnh gọi đệ quy cũng phải tuân theo quy luật tầm vực của ngơn ngữ : một CT PASCAL , chỉ cĩ thể thực hiện lệnh gọi đến một chương trình con đã được khai báo trong khối chứa lệnh gọi .

Ví dụ : Với mơ hình chương trình sau .

Trong phần lệnh của khối A cĩ thể gọi đến :

Program + Các thủ tục , hàm con trực tiếp của nĩ : E gọi được B nhưng khơng gọi được C + Gọi chính A ( gọi đệ quy trực tiếp )

–A + Gọi thủ tục hàm cùng cấp nhưng phải được

–B khai báo trước gọi được E nhưng khơng gọi C được D , muốn gọi D phải dùng forward để

khai báo trước .

-D

Khai báo FORWARD : Để từ thủ tục hàm A cĩ thể gọi đến D là thủ tục hàm cùng cấp nhưng được mơ tả sau A , ta cần cĩ một khai báo trước của D ở phía trước của A .

Khai báo này gồm : tiêu đề của D, với danh sách thơng số của D, tiếp theo là từ khố FORWARD . Sau đĩ , lúc khai báo lại hồn chỉnh D thì chỉ cần khai báo từ khố PROCEDURE ( hoặc FUNCTION ) , tên của D ( khơng cần danh sách thơng số) rồi đến phần thân của D.

Ví dụ :

procedure second (var M,N : integer ; p : char) ; Forward ; procedure first (A,B:integer; var X : real);

var ... begin . . . second(W,Y,Z) ; . . . end ; procedure second ; var ... begin ... end ; b) Trong NNLT C++

NNLT C++ cho phép tổ chức chương trình con đệ quy thuận lợi hơn . Bởi vì mọi hàm con trong C++ đều phải khai báo trước tiêu đề nên khơng cĩ sự phân biệt nào về việc khai báo đối với hàm con thường và hàm con đệ quy.

5. Các ví dụ về các chương trình con đệ qui trong PASCAL và C++ . Ví dụ 1 : Hàm F(n) tính số hạng n của dãy FIBONACCI

+ Dạng hàm trong Pascal:

Function F(n : integer) : integer; begin if( n < 2 ) then F := 1 else F := F(n-1) + F(n-2) end; + Dạng hàm trong C++ : int F(n :int) { if ( n < 2 ) return 1 ; else return (F(n -1) + F(n -2)) ; }

Ví dụ 2 : Chương trình đảo ngược một câu (sử dụng CT con đệ quy ) : Đọc vào một câu kết thúc bằng dấu ‘.’xuất ra màn hình theo thứ tự ngược .

( ví dụ : nhập : good _ bye see You agean . xuất : . naega ees eyb_doog ) + Chương trình trong Pascal :

Program VD1; Uses crt ;

Procedure DAO ; var kt : char ; Begin read(kt) ;

if(kt <> ‘.’) then DAO ; write(kt) ;

end ;

Begin (* main program *) clrscr ;

writeln(‘ go vao mot cau ket thuc bang dau . ‘ ) ; DAO ;

readln ; end . + Chương trình trong C++ : # include <iostream.h> # include <conio.h> void DAO(void) ; int main() { clrscr() ;

cout << “ nhap vao 1 cau ket thuc bang dau . \n “ ; DAO ; getch() ; return 1 ; } void DAO() { char kt ; cin >> kt ; if(kt != ‘.’) DAO ; cout << kt ; }

vi dụ 3 : Chương trình con tính USCLN của 2 số dựa vào thuật tốn Euclide : USCLN(a,b) = USCLN(b,a mod b ) với a <> 0 , b <> 0

USCLN(n,0) = n

+ Dạng hàm trong Pascal :

Function USCLN(a,b : integer ) : integer ; begin

if(b = 0 ) then USCLN := a

else USCLN := USCLN(b , a mod b ) ; end ;

+ Dạng hàm trong C++ : int USCLN(int a , int b ) { if(b = 0 ) return (a) ;

else return ( USCLN(b , a mod b)) ; }

$ 2 . BAØI TỐN ĐỆ QUY

Một phần của tài liệu giáo trình kỹ thuật lập trình nâng cao - trường đh đà lạt (Trang 25 - 31)

Tải bản đầy đủ (PDF)

(110 trang)