Đệ quy và cách khử

13 1.4K 7
Đệ quy và cách khử

Đ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

Đệ quy và cách khử

Giải thuật khử đệ quyNguyễn Văn TrườngCác khái niệm về đệ quy (ĐQ), giảithuật đệ quy (GTĐQ) được gặp nhiều trong tin học. GTĐQ được dùng khá phổ biếnđể giải các bài toán tính các công thức hồi quy, các bài toán khoa học kỹ thuậtvà đặc biệt được dùng nhiều trong cấu trúc dữ liệu cây. Hiện nay một số ngônngữ lập trình phổ biến như: Pascal, PL1 . cho phép gọi các thủ tục ĐQ (phươngtiện để thể hiện GTĐQ), nên việc cài đặt các GTĐQ đã trở lên khá dễ dàng, cóhiệu quả phản ánh rất sát nội dung của định nghĩa ĐQ. Tuy nhiên, điều đókhông có nghĩa là đảm bảo rằng GTĐQ là cách tốt nhất để giải bài toán,bởi vì quá trình dịch GTĐQ trong các ngôn ngữ cho gọi thủ tục ĐQ tốn nhiềuthời gian bộ nhớ hơn. Do đó, chúng ta sẽ nghiên cứu vấn đề chuyểncác GTĐQ bằng các giải thuật không tự gọi chúng (không ĐQ) hay còn gọi là giảithuật khử ĐQ (GTKĐQ).Sau đây là mộtví dụ đơn giản sử dụng giải thuật lặp để tính số hạng thứ n của dãy sốFibonacithay cho GTĐQ tương ứng: Function F(n:Byte): Longint; { GTĐQ }BeginIf n < 3 then F: = 1Else F: = F(n-1) + F(n - 2);End;Function F(n: Byte): Longint; { GTKĐQ }Var F1,F2 : longint;i: Byte;BeginIf n < 3 then F: = 1Else Begin F1: = 1; F2 =1; i: = 2;Whilei < n doBegini: = i + 1; F2: = F1 + F2; F1: = F2 - F1; End; F: = F2;End; End;Để thể hiện GTKĐQ trong kỹ thuật lập trình ta có thểsử dụng giải thuật lặp (như ví dụ trên), nhưng phổ biến là dùng cấu trúc dữliệu kho đẩy (stack) để ghi nhớ được các bước xử lý, nhờ đó cóthể phục hồi các bước xử lý trước. Việc sử dụng stack để thể hiện GTKĐQ cũng yhệt như công việc mà một trình biên dịch phải làm khi dịch một chương trình ĐQthành ngôn ngữ máy.Dưới đây là kỹ thuật chuyển một sốGTĐQ về GTKĐQ tương ứng. Dạng 1: Đượctóm tắt bởi công thức: P Ξ ED [AP]Hay tương đương:P(x) Ξ If E(x) thenD(x)else beginA (x) ; P(f(x) );End;Trong đó: D(x) A (x) là các lệnh không gọi ĐQ, E(x)là điều kiện kết thúc ĐQ, còn x là tập hợp các biến nào đó; f là một hàm của x.Dạng khử ĐQ (sử dụng giải thuật lặp): P(x) Ξ While not E(x)doBeginA (x); x: = f(x); End;D(x);Ví dụ sau đây minh hoạ cho phươngpháp ở trên: Hãy viết chương trình đổi các ký tự của xâu st hành ký tự in hoatương ứng. Dạng ĐQ: DOI(i,st) Ξ Ifi > length(st) then Exitelse begin St[i]: = upcase (st[i]);DOI(i+1,st);End;Dạngkhử ĐQ: DOI(i,st) Ξ While<= length (st)doBeginSt[i]: = upcase(st[i]); i: = i +1;End;Exit;Dạng2: Được tóm tắt bởi công thức P ED [APB] Haytương đương: P(x) Ξ IfE(x) then D(x)Else begin Ăx); P(f(x)); B(x);End;Dạng này khác dạng 1 ở chỗ sau khi thực hiện lệnh gọiĐQ P(f(x) ) mới thực hiện lệnh khônggọi ĐQ B(x). Để chuyển về dạng khử ĐQ, ta dùngmột stack để ghi lại các giá trị x, f(x) , f(f(x) ) . cho đến khithực hiện xong các lệnh gọi ĐQ P(f(x)) (tức là E(x) có giá trịđúng). Khi đó, để tính B(x) thì lấy lại các giá trị của x trong stack theo thứtự: . f(f(x) ) , f(x) , x. Dạng khử ĐQ:P(x) Ξ create(s) ; {khởitạo stack s}While not E(x)doBegin A (x) ;Push (x, s) ; { đẩy x vào s }X: = f(x) ; End;D(x) ;While notEmpty(s) doBeginPop (s,x); { lấy x từ đỉnh của s }B(x) ;End;Sử dụng sáng tạo dạng này có thể giải một số bài toánnhư trong các kỹ thuật duyệt cây: Preorder, Inorder Postorder.Sau đây là thủ tục khử ĐQ thể hiện kỹ thuật duyệt câytheo thứ tự Preorder: Duyet (R) Ξ Create(s) ; {R:gốc của cây, S: stack}X: = R ; While x <> Nil doBegin Visit(x) ; {thăm đỉnh x}Push(x,s); {xếp x vào đỉnh stack S}X: = EldestChild(x) ; {thay x bởi con trưởng}End;While notEmpty(S) doBeginPop(s,x); {lấy x trên đỉnh stack S}X: = Next(x); {thay x bởi em liền kề}While x<> Nil doBegin Visit(x) ;Push(x,s) ; X: = Eldestchild(x) ; {thay x bởi con trưởng}End ;End ;Dạng 3: Đượctóm tắt bởi công thức: P Ξ ED [APBP]Hay tương đương:P(x) Ξ If E(x) then D(x) Else BeginA (x) ; P(f(x)); (1) B(x) ; P(g(x)); (2)End ;Dạng này phức tạp hơn dạng 2 ở chỗtrong lời gọi lệnh (2) có thực hiện lệnh (1). Cũng tương tự dạng 2, sau khilệnh (1) được thực hiện thì thực hiện B(x). Do đó phải sử dụng phương pháp đánhdấu để nhận biết là đang thực hiện với lệnh (1) hay lệnh (2). Dạng không ĐQ: P(x) Ξ Create(S) ; {khởitạo stack S để chứa các bản ghi, trường thứ 2 để đánh dấu. Số 1 để đánh dấu đáystack S }Repeat While not E(x) doBegin A (x) ; Push ((x,2) , S) ; x: = f(x) ;End ;D(x) ; Pop ((x,m),S) ;If m = 2 then { chưa phải đáy stack } Begin B(x) ; x: = g(x) ; End ;Until m = 1 ;Minh hoạ cho dạng này qua bài toán quen thuộc: Bàitoán tháp Hà NộiDạng ĐQ:Thap _ HN (a,b,c) Ξ If n = 1 then writeln (a,-> ,b)else begin Thap _ HN (n - 1, a, c, b);Thap _ HN (1, a, b, c);Thap _ HN (n - 1, c, b, a);End;Để tiện thể hiện GTKĐQ, ta viết thủ tục ĐQ trên dướidạng: Thap _ HN (n, a, b) Ξ If n = 1 thenwrite (a,-> , b){D(x) } Else BeginC: = 6 - a - b; {A (x) } Thap _ HN (n -1,a ,c); { P(f(x)) }Writeln (a,-> , b); { B(x) }Thap _ HN (n -1, c,b); { P(g(x)) }End;Dạng khử ĐQ được thể hiện như sau:Thap _ HN (n, a,b,) Ξ Create(S);Push ((n, a, b, 1), S); {1 để đánh dấu đáy stack }RepeatWhile n > 1 doBeginC: = 6 - a -b;Push ((n - 1, a, c, 2), S);N: = n -1; b: = c;End;writeln (a,-> , b); Pop ((n, a, b, m), S);If m = 2 then {chưa phải là đáy stack }Begin b: = 6 - a - b; {trả lại trạng thái trước đó của b}Writeln (a,->, b);a: = 6 - a - b;End;Until m = 1;Dưới đây là chương trình thể hiện GTKĐQ giải bài toánTháp Hà Nội :program Thap_HN;{Khu dequy}var stack: array[1 500]of Byte;n,a,b,m,c,t:Byte;BeginWrite(' So tang cua thap:');Readln(n);;Writeln('thu tu chuyen cac tang nhu sau:');a:=1;b:=2;stack[1]:=n;stack[2]:=a;stack[3]:=b;stack[4]:=1;t:=4;RepeatWhile n>1 doBeginc:=6-a-b;stack[T+1]:=n-1;stack[T+2]:=a;stack[T+3]:=c;stack[T+4]:=2;t:=t+4;n:=n-1;b:=c;End; writeln(a,'->',b);M:=stack[T];b:=stack[T-1];a:=stack[T-2];n:=stack[T-3];t:=t-4;If m=2 thenBeginb:=6-a-b;{tra lai trang thai ban dau cua b}writeln(a,'->',b);a:=6-a-b;End;Until m=1;Readln;End.Trong thực tế các bài toán về ĐQ rất phong phú , khôngphải bài toán ĐQ nào cũng đều có thể đưa về dạng khử ĐQ một cách chuẩn mựcnhư ở trên. Nên việc cài đặt các GTKĐQ đòi hỏi phải hiểu rõ được tính chất ĐQcủa bài toán, trên cơ sở đó lựa chọn ra giải thuật phù hợp. Thông thường đó làgiải thuật lặp kết hợp với cấu trúc dữ liệu stack. Dưới đây là hai ví dụ minhhoạ:Ví dụ 1:Khử ĐQ trong thuật toán sắp xếp Quicksort. Trong chương trình dưới ta sử dụng một stack để lưu lại vị trícủa phần dãy chưa sắp xếp tại mỗi bước. Sau khi sắp xếp xong một phần, ta sẽđọc lại stack để thực hiện việc sắp xếp với phần còn lại. Chương trình đượcviết như sau:PROGRAM QUICKSORT;{GTKDQ}Type vec=array[1 300] ofinteger;Var x:vec;n,i:integer;Procedure Quick_sort;{sapxep tang}Vark,i,j,l,r,s,tg,vt:integer;stack:vec;Beginstack[1]:=1;stack[2]:=n; s:=2;Repeatr:=stack[s];l:=stack[s-1];s:=s-2;Repeati:=l;j:=r;vt:=x[(L+r) div 2] ;RepeatWhile x[i]< vt do inc(i);While x[j]> vt do dec(j);If i<=j thenBegintg:=x[i];x[i]:=x[j];x[j]:=tg;inc(i);dec(j);End;Until i>j;If i< r then {ghi nho phan ben phai}Beginstack[s+1]:=i;stack[s+2]:=r;inc(s,2);End;r:=j;{xet phan ben trai}Until l>=r;Until s=0;End;Begin [...]... hậu. 2 - Xây dựng GTKĐQ cho bài toán mãđi tuần theo hai cách: sử dụng giải thuật lặp sử dụng cấutrúc dữ liệu stack. Giải thuật khử đệ quy Nguyễn Văn Trường Các khái niệm về đệ quy (ĐQ), giảithuật đệ quy (GTĐQ) được gặp nhiều trong tin học. GTĐQ được dùng khá phổ biếnđể giải các bài tốn tính các cơng thức hồi quy, các bài toán khoa học kỹ thuậtvà đặc biệt được dùng nhiều trong cấu trúc dữ liệu cây.... dễ dàng, cóhiệu quả phản ánh rất sát nội dung của định nghĩa ĐQ. Tuy nhiên, điều đókhơng có nghĩa là đảm bảo rằng GTĐQ là cách tốt nhất để giải bài tốn,bởi vì q trình dịch GTĐQ trong các ngôn ngữ cho gọi thủ tục ĐQ tốn nhiềuthời gian bộ nhớ hơn. Do đó, chúng ta sẽ nghiên cứu vấn đề chuyểncác GTĐQ bằng các giải thuật khơng tự gọi chúng (khơng ĐQ) hay cịn gọi là giảithuật khử ĐQ (GTKĐQ). Sau... hau:');readln(n); d:=0;xep;readln; END. Bất kỳ một giải thuật ĐQ nào cũngcó thể làm như các ví dụ minh hoạ ở trên để khử bỏ ĐQ. Chúng ta thấy rằng vấn đề khử ĐQ mặc dù phức tạp, không sáng sủa nhưng nóthể hiện sự hiệu quả riêng về bộ nhớ, về tốc độ, về sự không phụ thuộc môitrường cài đặt có cho phép gọi các thủ tục ĐQ hay khơng; đặc biệt nó giúpcho sự hiểu biết sâu sắc hơn về tính chất tự nhiên của các GTĐQ. Bài tập:... Dạng 1: Đượctóm tắt bởi cơng thức: P Ξ ED [AP] Hay tương đương: P(x) Ξ If E(x) thenD(x) else begin A (x) ; P(f(x) ); End; Trong đó: D(x) A (x) là các lệnh không gọi ĐQ, E(x)là điều kiện kết thúc ĐQ, cịn x là tập hợp các biến nào đó; f là một hàm của x. Dạng khử ĐQ (sử dụng giải thuật lặp): P(x) Ξ While not E(x)do Begin A (x); x: = f(x); End; D(x); Ví dụ sau đây minh hoạ cho phươngpháp ở trên:... (n, a, b) Ξ If n = 1 thenwrite (a,-> , b){D(x) } Else Begin C: = 6 - a - b; {A (x) } Thap _ HN (n -1,a ,c); { P(f(x)) } Writeln (a,-> , b); { B(x) } Thap _ HN (n -1, c,b); { P(g(x)) } End; Dạng khử ĐQ được thể hiện như sau: Thap _ HN (n, a,b,) Ξ Create(S); Push ((n, a, b, 1), S); {1 để đánh dấu đáy stack } Repeat While n > 1 do Begin C: = 6 - a -b; Push ((n - 1, a, c, 2), S); N: = n -1; b: . Giải thuật khử đệ quyNguyễn Văn TrườngCác khái niệm về đệ quy (ĐQ), giảithuật đệ quy (GTĐQ) được gặp nhiều trong tin học.. dùng khá phổ biếnđể giải các bài toán tính các công thức hồi quy, các bài toán khoa học kỹ thuậtvà đặc biệt được dùng nhiều trong cấu trúc dữ liệu cây. Hiện

Ngày đăng: 07/09/2012, 11:41

Từ khóa liên quan

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

Tài liệu liên quan