Tải bản đầy đủ (.doc) (43 trang)

Báo cáo đề tài toán rời rạc pptx

Bạn đang xem bản rút gọn của tài liệu. Xem và tải ngay bản đầy đủ của tài liệu tại đây (279.14 KB, 43 trang )


Báo cáo đề tài toán
rời rạc
Mục luc
LỜI MỞ ĐẦU 4
Phần I: GIỚI THIỆU 5
Phần II: ĐỆ QUY VÀ CÁCH KHỬ ĐỆ QUY 6
Chương I: NHỮNG HIỂU BIẾT VỀ ĐỆ QUY 6
1.1 -Khái niệm đệ quy 6
1.1.1 Khái niệm: 6
1.1.2 Phân loại đệ quy: 7
1.2 -Thuật toán đệ quy: 8
1.2.1 Khái niệm : 8
1.2.2 Ví dụ: 10
1.3 Ưu nhược điểm của đệ quy: 10
1.3.1 Ưu điểm: 10
1.3.2 Nhược điểm: 11
Chương II : CHƯƠNG TRÌNH CON ĐỆ QUY 11
2.1 Các phương pháp xây dựng chương trình con đệ quy: 11
2.1.1 Định nghĩa chương trình con đệ quy: 11
2.1.2 Cấu trúc của một chương trình con đệ quy: 11
2.2 Phương pháp xây dựng chương trình con đệ quy: 13
2.3 Cách thực hiện của đệ quy: 16
2.3.2 Hiện thực đơn xử lý: vấn đề vùng nhớ 17
Thuật giải đệ quy 18
3.1-Vấn đề khử đệ quy: 19
3.2 Một số phương pháp khử đệ quy: 20
3.2.1 Khử đệ quy bằng vòng lặp: 20
CHƯƠNG 4: CÀI ĐẶT THUẬT TOÁN SELECTION SORT 31
4.1: Thuật toán selection sort bàng phương pháp đệ quy 31
4.2: Khử đệ quy thuật toán selection sort 31


KẾT LUẬN: 42
TÀI LIỆU THAM KHẢO 42
YÊU CẦU CỦA ĐỀ TÀI:
Thuật toán đệ quy và cách khử đệ quy:
1. Trình bày hiểu biết về thuật toán đệ quy, ưu nhược điểm của đệ
quy.
2. Các phương pháp xây dựng chương trình con đệ quy, cách thực
hiện chương trình đệ quy, cơ chế cấp phát và quản lý bộ nhớ cho
chương trình đệ quy.
3. Vấn đề khử đệ quy và một số phương pháp khử đệ quy, có ví dụ
minh họa.
4. Cài đặt thuật toán Selectionsort thực hiện việc sắp xếp một dãy n
số nguyên theo chiều tăng dần bằng phương pháp đệ quy sau đó
khử đệ quy.


LỜI MỞ ĐẦU
Toán rời rạc là một lĩnh vực của toán học nghiên cứu các đối tượng rời
rạc.Chúng ta sẽ sử dụng công cụ rời rạc khi phải đếm các đối tượng, khi nghiên
cứu quan hệ giữa các tập rời rạc, khi phân tích các quá trình hữu hạn.Đồng thời
tầm quan trọng của toán rời rạc được nâng cao là nhờ việc cất giữ và xử lý thông
tin trên máy tính có bản chất là các quá trình rời rạc.Các quá trình rời rạc ấy được
xử lý và biểu diễn thông qua các chương trình, các thuật toán cụ thể.Khi nói đến
các thuật toán trong toán rời rạc ta không thể không nói đến đệ quy và giải thuật
của đệ quy. Đệ quy không những giúp người lập trình giải quyết tốt các bài toán
mà còn giúp nâng cao tư duy toán, rèn luyện kỹ thuật lập trình.Tuy rằng bên cạnh
giải thuật đệ quy vẫn có những giải thuật lặp khá đơn giản và hữu hiệu,chẳng hạn
như giải thuật lặp tính n!. nhưng đệ quy vẫn có vai trò xứng đáng của nó, có
những bài toán việc nghĩ ra lời giải đệ quy thuận lợi hơn nhiều so với lời giải lặp
và có những giải thuật đệ quy thật sự cũng có hiệu lực cao nữa. Một điều cần nói

thêm về đệ quy là: Về mặt định nghĩa công cụ đệ quy đã cho phép xác định một
tập vô hạn các đối tượng bằng một phát biểu hữu hạn. Chúng ta sẽ thấy rõ vai trò
của công cụ này trong định nghĩa văn phạm, định nghĩa cú pháp ngôn ngữ, định
nghĩa một số cấu trúc dữ liệu…Nội dung của bài đề tài được trình bày sau đây sẽ
phần nào giúp các bạn hiểu sâu sắc hơn về đệ quy và các giải thuật liên quan tới
nó.Trong đề tài này chúng tôi trình bày gồm có 4 chương.Mỗi chương chúng tôi
đã cố gắng trình bày ngắn gọn, trực tiếp vào bản chất của vấn đề, đồng thời sử
dụng cài đặt tất cả các chương trình bằng ngôn ngữ lập trình pascal,hy vọng sẽ
mang lại sự gần gũi, dễ hiểu cho các sinh viên,mong rằng nó sẽ thực sự giúp ích
cho các bạn trong quá trình nghiên cứu về đệ quy.
Sinh viên nhóm 5 rất cảm ơn các thầy cô giáo trong bộ môn khoa học
máy tính đã hướng dẫn chúng em trong quá trình thực hiện đề tài này.Mặc dù rất
cố gắng nhưng không tránh khỏi những thiếu sót và hạn chế,rất mong được sự
tham gia, đóng góp ý kiến bổ sung của thầy cô và các bạn cho đề tài này.
Chúng tôi xin chân thành cảm ơn!
Phần I: GIỚI THIỆU
Toán học rời rạc là một trong những môn cơ sở được giảng dạy ở các
khoa công nghệ thông tin hiện nay.Nhằm đáp ứng thêm nhu cầu đa dạng về kiến
thức cho các bạn sinh viên về toán rời rạc,đề tài này chúng tôi nghiên cứu về đệ
quy và cách khử đệ quy,đây là bài toán rất nhỏ được sử dụng trong toán rời rạc
nhưng khi xét về vai trò thì nó là một phần không thể thiếu của toán rời rạc.Nội
dung đề tài chúng tôi trình bày gồm bốn chương, mỗi chương chúng tôi cố gắng
trình bày cô đọng các nội dung,cụ thể như:
• Chương 1: Sơ lược về đệ quy,qua chương này các bạn sẽ nắm được
những khái niệm cơ bản về đệ quy, và phần nào hiểu hơn về nó.
• Chương 2: Chương trình con đệ quy và một số vấn đề liên quan đến
nó,qua chương này các bạn sẽ hiểu thêm về cách xây dựng và sử dụng
các hàm, cũng như các thủ tục của đệ quy.
• Chương 3: Chương này chúng tôi trình bày cách khử đệ quy, khử đệ
quy ở đây được hiểu là một phương pháp làm mất đi tính đệ quy của

một chương trình đệ quy.Giải thuật này cũng mang một ý nghĩa hết sức
quan trọng trong quá trình giải các bài toán liên quan.
• Chương 4: Chương này chúng tôi cài đăt một bài toán ví dụ giúp các
bạn hiểu thêm một cách sâu sắc hơn về những gì chúng tôi đã nêu ở
trên.

Nói một cách tổng quát, qua bài đề này chúng tôi mong giúp cho mọi
người hiểu một cách sâu sắc hơn về đệ quy, và từ đó áp dụng thành thạo nó trong
việc giải quyết các bài toán có liên quan.
**********************************


Phần II: ĐỆ QUY VÀ CÁCH KHỬ ĐỆ QUY

Chương I: NHỮNG HIỂU BIẾT VỀ ĐỆ QUY
1.1 -Khái niệm đệ quy
1.1.1 Khái niệm:
Đệ quy là một khái niệm tồn tại trong cuộc sống, trong toán học cũng như
trong lập trình. Đệ quy cho một phương pháp ngắn gọn và sáng sủa để mô tả các
đối tựơng cũng như một số quá trình. Như vậy đệ quy là một phương pháp xác
định tập hợp các đối tượng thỏa mãn một yêu cầu nào đó.
Một đối tượng được gọi là đệ quy nếu nó bao gồm chính nó như một bộ phận
hoặc đối tượng được định nghĩa dưới dạng của chính nó.
• Ví dụ về đệ quy:
-Ví dụ 1:
Đặt 2 chiếc gương cầu đối diện nhau. Trong chiếc gương thứ nhất chứa
hình chiếc gương thứ hai, trong chiếc gương thứ hai chứa hình chiếc gương thứ
nhất nên tất nhiên nó chứa lại hình ảnh của chính nó trong chiếc gương thứ nhất…
Ở một góc nhìn hợp lý ta có thể thấy một dãy ảnh vô hạn của hai chiếc gương.
-Ví dụ 2:

Trên vô tuyến truyền hình có lúc ta thấy có những hình ảnh đệ quy như sau:
phát thanh viên ngồi bên máy vô tuyến truyền hình, trên màn hình của máy này lại
có chính hình ảnh của phát thanh viên ấy ngồi bên máy vô tuyến và cứ như thế….
-Ví dụ 3:
Hàm n! được định nghĩa là:
• 0! = 1;
• Nếu n > 0 thì n! = n ( n – 1 )!;
Giả sử ta tính 5! Theo định nghĩa trên ta có:
5! = 5 x 4!
= 5 x ( 4 x 3! )
= 5 x ( 4 x ( 3 x 2! )
= 5 x ( 4 x ( 3 x ( 2 x 1! ) ) )
= 5 x ( 4 x ( 3 x ( 2 x ( 1 x 0! ) ) ) )
= 5 x ( 4 x ( 3 x (2 x 1) ) )
=120.
Việc tính toán trên minh họa bản chất của cách mà đệ quy thực hiện. Để có
được câu trả lời cho một bài toán lớn, phương pháp chung là giảm bài toán lớn
thành một hoặc nhiều bài toán con có bản chất tương tự mà kích thước nhỏ hơn.
Sau đó cũng chính phương pháp chung này lại được sử dụng cho những bài toán
con, cứ như thế đệ quy sẽ tiếp tục cho đến khi kích thước của bài toán con đã giảm
đến một kích thước nhỏ nhất nào đó của một vài trường hợp cơ bản,mà lời giải của
chúng có thể có được một cách trực tiếp không cần đến đệ quy nữa. Nói cách
khác:
Mọi quá trình đệ quy gồm có hai phần:
• Một vài trường hợp cơ bản nhỏ nhất có thể được giải quyết mà không
cần đệ quy.
• Một phương pháp chung có thể giảm một trường hợp thành một hoặc
nhiều trường hợp nhỏ hơn,và nhờ đó việc giảm nhỏ vấn đề có thể tiến
triển cho đến kết quả cuối cùng là các trường hợp cơ bản.
1.1.2 Phân loại đệ quy:

Người ta phân đệ quy làm hai loại: + Đệ quy trực tiếp.
+ Đệ quy gián tiếp.
* Đệ quy trực tiếp: là loại đệ quy mà đối tượng được mô tả trực tiếp qua nó: A
mô tả qua A, B, C… Trong đó B, C không chứa A
Ví dụ:
1. Mô tả đệ quy cây gia phả: gia phả của một người bao gồm người đó và
gia phả của cha và gia phả của mẹ.
2. Mô tả đệ quy thủ tục chọn hoa hậu: + Chọn hoa hậu của từng khu vực
+ Chọn hoa hậu của các hoa hậu.
* Đệ quy gián tiếp: Là loại đệ quy mà đối tượng được được mô tả gián tiếp qua
nó: A mô tả qua A
1,
A
2,….,
A
n.
Trong đó có một A
i
được mô tả qua A.
Ví dụ: Mô tả dạng tổng quát một chương trình viết trên ngôn ngữ lập trình
pascal:
Một chương trình pascal gồm:
• Đầu chương trình ( head ) gồm : program tên;
• Thân chương trình ( blok ) gồm:
+ Khai báo unit, định nghĩa hàm,nhãn, kiểu dữ liệu, khai báo biến.
+ Định nghĩa các chương trình con
- Đầu chương trình con: *procedure tên thủ tục ( danh sách thông
số hình thức );
*Function tên hàm ( danh sách tham số
hình thức) : kiểu;

- Thân chương trình con ( blok )
- Dấu ‘ ; ‘
+ Phần lệnh: Là một lệnh ghép dạng:
Begin S
1;
S
2;
…;S
n
End;
• Dấu kết thúc chương trình ‘ . ‘
1.2 -Thuật toán đệ quy:
1.2.1 Khái niệm :
Một thuật toán được gọi là đệ quy nếu nó giải quyết bài toán bằng cách rút
gọn liên tiếp bài toán ban đầu tới bài toán cũng như vậy nhưng có dữ liệu đầu vào
nhỏ hơn .Tức là nếu lời giải của bài toán T được thực hiện bởi lời giải của một bài
toán T’ có dạng như T thì nó là một lời giải đệ quy, khi đó giải thuật chứa lời giải
đệ quy được gọi là giải thuật đệ quy hay thuật toán đệ quy ( T’<T ).
* Định nghĩa một hàm đệ quy hay thủ tục đệ quy gồm hai phần:
- Phần neo ( anchor ): phần này được thực hiện khi mà công việc quá đơn giản
có thể giải trực tiếp chứ không cần phải nhờ đến một bài toán con nào cả
- Phần đệ quy: Trong trường hợp bài toán chưa thể giải được bằng phần neo, ta
xác định những bài toán con và gọi đệ quy giải các bài toán con đó. Khi đã có lời
giải( đáp số ) của những bài toán rồi thì phối hợp chúng lại để giải bài toán đang
quan tâm.
Phần đệ quy thể hiện tính quy nạp của lời giải. Phần neo cũng rất quan trọng bởi
nó quy định tới tính hữu hạn dừng của lời giải.
* Nói 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 mệnh đề S ( không chứa yếu tố đệ quy ) và P: P


P [ S , P ].
Thực thi giải thuật đệ quy có thể dẫn tới một tiến trình gọi đệ quy không kết
thúc khi đó không có khả năng gặp trường hợp neo, 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 soát quá trình gọi đệ
quy của giải thuật đệ quy 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 xác định và biến đổi qua mỗi lần gọi P, quá trình gọi P sẽ dừng
khi P không còn thỏa mãn.
Mô hình tổng quát của một giải thuật đệ quy với sự quan tâm đến điều kiện
dừng sẽ là:
P

if B then P [ S, P ] hoặc P

P [ S, if B then P ]
Thông thường với giải thuật đệ quy P để đảm bảo P sẽ dừng sau n lần gọi
ta chọn B là ( n > 0). Mô hình giải thuật đệ quy khi đó có dạng:
P ( n )

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

P [ S , 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ì.
1.2.2 Ví dụ:
• Xét bài toán tìm một từ trong từ điển :
Ta có thuật toán dưới dạng giả mã như sau:
BEGIN
IF ( từ điển chỉ có một trang ) THEN ( tìm từ trong trang này )

ELSE
BEGIN
+ ( chia đôi từ điển );
+ ( xác định xem nửa nào của từ điển chứa từ cần tìm)
IF ( từ đó nằm ở nửa trước của từ điển )
THEN ( tìm từ đó trong nửa trước )
ELSE ( tìm từ đó trong nửa sau );
END;
END.
Ta thấy sau mỗi lần từ điển được tách đôi thì một nửa thích hợp sẽ lại được
tìm kiếm bằng một cách như đã dùng trước đó. Có một trường hợp đặc biệt, khác
với mọi trường hợp trước, sẽ đạt được sau nhiều lần tách đôi, đó là trường hợp từ
điển chỉ còn duy nhất một trang. Lúc đó việc tách đôi ngừng lại và bài toán đã đủ
nhỏ để ta có thể giải quyết trực tiếp bằng cách tìm từ mong muốn trên trang đó,
chẳng hạn bằng cách tìm tuần tự. Trường hợp đặc biệt này gọi là trường hợp suy
biến.
1.3 Ưu nhược điểm của đệ quy:
1.3.1 Ưu điểm:
• Công cụ mạnh, diễn đạt tư duy rất rõ ràng, chặt chẽ.
• Ngắn gọn, có khả năng định nghĩa một tập hợp rất lớn các đối tượng bằng
một số các câu lệnh hữu hạn.
• Làm chương trình trông đẹp mắt, dễ đọc, dễ hiểu và chương trình được
nêu bật rõ ràng hơn.
1.3.2 Nhược điểm:
• Đệ quy tốn bộ nhớ nhiều hơn khi không đệ quy vì cứ mỗi lần gọi đệ quy
lại cần thêm một vùng nhớ mới trong khi vùng nhớ cũ vẫn được duy trì.
• Tốc độ thực hiện chương trình chậm hơn khi không đệ quy.
Chương II : CHƯƠNG TRÌNH CON ĐỆ QUY
2.1 Các phương pháp xây dựng chương trình con đệ quy:
2.1.1 Định nghĩa chương trình con đệ quy:

• Định nghĩa:
Một chương trình con (hàm hoặc thủ tục) được gọi là đệ quy nếu trong quá
trình thực hiện nó có phần phải gọi đến chính nó.
2.1.2 Cấu trúc của một chương trình con đệ quy:
Cấu trúc chính của một chương trình con đệ quy gồm hai phần: Phần cơ sở
và phần đệ quy.
• Phần cơ sở: Chứa tác động của hàm hoặc thủ tục với một số giá trị cụ
thển ban đầu của tham số.Nó bao gồm các trường hợp dừng mà có thể
được giải quyết ngay (trường hợp duy biến).
• Phần đệ quy: Định nghĩa giá trị cần tác động cho giá trị hiện thời các
tham số bằng các tác động đã được định nghĩa trước đây với các kích
thước tham số nhỏ hơn (Là phần trong thuật toán có yêu cầu gọi đệ
quy,tức là yêu yêu cầu thực hiện lại thuật toán ở cấp độ nhỏ hơn (điều
kiện kiểm soát đệ quy)).
- Ví dụ : Hàm tính giai thừa của số tự nhiên n ( tính n! ).
Ta có chương trình con đệ quy sau:
Function gt ( n: byte ) : longint;
Begin
If n= 0 then gt:= 1 ( phần cơ sở )
Else gt:= n* gt ( n - 1); ( phần đệ quy )
End;
Trong ví dụ trên, qui trình thực hiện như sau:
• Khi có lệnh gọi hàm, chẳng hạn:
x := gt(3);
• thì máy sẽ ghi nhớ là:
gt(3) := 3 * gt(2); và đi tính gt(2)
• kế tiếp máy lại ghi nhớ:
gt(2) := 2 * gt(1); và đi tính gt(1)
• Theo định nghĩa của hàm thì:
gt(1) := 1;

• Máy sẽ quay ngược lại:
gt(2) := 2 * 1; cho kết quả là 2
• Tiếp tục:
gt(3) := 3 * 2; cho kết quả là 6
• Như vậy kết quả cuối cùng trả về là 6. Ta có: 3! = 6.
- Một ví dụ khác như sau: Ta xây dựng chương trình con tính số fibonaci.
Function Fibo ( n: integer) : integer ;
Begin
If ( n = 1) or ( n = 2 ) then Fibo := 1
( phần cơ sở)
Else Fibo := Fibo ( n - 1) + Fibo ( n - 2) ;
( phần đệ quy);
End;
2.2 Phương pháp xây dựng chương trình con đệ quy:
Để xây dựng giải thuật một bài toán có tính đệ quy bằng phương pháp đệ
quy ta cần thực hiện tuần tự 3 nội dung sau:
- Thông số hóa bài toán.
- Tìm các trường hợp neo cùng giải thuật giải tương ứng.
- Tìm giải thuật giải trong trường hợp tổng quát bằng phân rã bài toán
theo kiểu đệ quy.
• Thông số hoá bài toán.
Tổng quát hóa bài toán cụ thể cần giải thành bài toán tổng quat (một họ các bài
toán chúa bài toán cần giải), tìm ra các thông số cho bài toán tổng quat đặc biệt là
nhóm các thông số biểu thị kích thước của bài toán-các thông số điểu khiển (các
thông số mà độ lớn của chúng đặc trưng cho độ phức tạp của bài toán, và giảm đi
qua mỗi lần gọi đệ quy).
Ví dụ: n trong hàm FAC(n); a,b trong hàm USCLN(a,b).
• Phát hiện các trường hợp suy biến (neo) và tìm giải thuật cho các
trường hợp này.
Đây là các trường hợp suy biến của bài toán tổng quat, là các trường hợp tương

ứng với các giá trị biên cuả các biến điều khiển (trường hợp kích thước bài toán
nhỏ nhất), mà giải thuật giải không đệ qui (thường rất đơn giản).
Ví dụ:
FAC(1)=1, USCLN(a,0)=a
• Phân rã bài toán tổng quát theo phương thức đệ quy.
Tìm phương án (giải thuật) giải bài toán trong trường hợp tổng quat bằng cách
phân chia nó thành các thành phần mà hoặc có giải thuật không đệ quy hoặc là bài
toán trên nhưng có kích thước nhỏ hơn.
Ví dụ: FAC(n)=n*FAC(n-1).
Đệ quy là một công cụ cho phép người lập trình tập trung vào bước chính
yếu của giải thuật mà không phải lo lắng tại thới điểm khởi đầu về cách kết nối
bước chính yếu này với các bước khác. Khi cần giải quyết một vấn đề, bước tiếp
cận đầu tiên nên làm thường là xem xét một vài ví dụ đơn giản, và chỉ sau khi đã
hiểu được chúng một cách kỹ lưỡng, chúng ta mới thử cố gắng xây dựng một
phương pháp tổng quát hơn. Một vài điểm quan trọng trong việc thiết kế một giải
thuật đệ quy được liệt kê sau đây:
• Tìm bước chính yếu. Hãy bắt đầu bằng câu hỏi “Bài toán này có thể được
chia nhỏ như thế nào?” hoặc “Bước chính yếu trong giai đọan giữa sẽ được
thực hiện như thế nào?”. Nên đảm bảo rằng câu trả lời của bạn đơn giản
nhưng có tính tổng quat. Không nên đi từ điểm khởi đầu hay điểm kết thúc
của bài toán lớn, hoặc sa vào quá nhiểu trường hợp đặc biệt (do chúng chỉ
phù hờp vói các bài toán nhỏ). Khi đã có được một bước nhỏ và đơn giản
để hướng tới lời giải, hay tự hỏi rằng những khúc mắc còn lại của bài toán
có thể được giải quyết bằng cách tương tự hay không, để sủa lại phương
pháp của bạn cho tổng quát hơn, nếu cần thiết. Ngoại trừ những định nghĩa
toán học thể hiện sự đệ quy quá rõ ràng, một điều thú vị mà chúng ta sẽ lần
lượt gặp trong những chương sau la, khi những bài toán cần được giải quyết
trên những cấy trúc dữ liệu mà định nghĩa mang tính chất đệ quy nhu danh
sách, chuỗi ký tự biểu diễn biểu thức số học, cây, hay đồ thi… thì giải pháp
hướng tới một giải thuật đệ quy là rất dễ nhình thấy.

• Tìm điều kiện dừng. Điều kiện dừng chỉ ra rằng bài toán hoặc một phần
nào đó của bài toán đã được giải quyết. Điều kiện dừng thường là trường
hợp nhỏ, đặc biệt, có thể được giải quyết một cách dễ dàng không cần đệ
quy.
• Phác thảo giải thuật. Kết hợp điểu kiện dừng với bước chính yếu của bài
toán, sử dụng lệnh if để chọn lụa giũa chúng. Đến đây thì chúng ta có thể
viết hàm đệ quy, trong đó mô tá cách mà bước chính yếu được tiến hành
cho đến khi gặp được điểu kiện dùng. Mỗi lần gọi đệ quy hoặc là phải giải
quyết một phần của bài toán khi gặp một trong các điều kiện dừng, hoặc là
phải giảm kích thước bài toán hướng dần đến điều kiện dừng.
• Kiểm tra sự kết thúc. Kế tiếp, và cũng là điều tối quan trọng, là phải chắc
chắn việc gọi đệ quy sẽ không bị lặp vô tận. Bắt đầu từ một trường hợp
chung, qua một số bước hữu hạn, chúng ta cần kiểm tra liệu điều kiện dừng
có khả năng xảy ra để quá trình đệ quy kết thúc hay không. Trong bất kỳ
một giải thuật nào, khi một lần gọi hàm không phải làm gì, nó thường quay
về một cách êm thấm. Đối với giải thuật đệ quy, điều này rất thường xảy ra,
do việc gọi hàm mà không phải làm gì thường là một điều kiện dừng. Do
đó, cần lưu ý rằng việc gọi hàm mà không làm gì thường không phải là một
lỗi trong trường hợp của hàm đệ quy.
• Kiểm tra lại mọi trường hợp đặc biệt. Cuối cùng chúng ta cũng cần bảo
đảm rằng giải thuật của chúng ta luôn đáp ứng mọi trường hợp đặc biệt.
• Vẽ cây đệ quy. Công cụ chính để phân tích các giải thuật đệ quy là cây đệ
quy. Như chúng ta đã thấy trong bài toán Tháp Hà Nội, chiểu cao của cây
đệ quy kiên quan mật thiết đến tổng dung lượng bộ nhớ mà chương trình
cần đến, và kích thước tổng cộng của cây phản ánh số lần thực hiện bước
chính yếu và cũng là tổng thời gian chay chương trình. Thông thường
chúng ta nên vẽ cây đệ quy cho một hoặc hai trường hợp đơn giản của bài
toán của chúng ta vì nó sẽ chỉ dẫn cho chúng ta nhiều điều
2.3 Cách thực hiện của đệ quy:
Khi nghiên cứu về phần này chúng ta phải lưu ý câu hỏi về cách thực hiện

một chương trình đệ quy trong máy tính cần được tách rời khỏi câu hỏi về sử dụng
đệ quy để thiết kế giải thuật.
Trong giai đoạn thiết kế, chúng ta nên sử dụng mọi phương pháp giải quyết
vấn đề mà chúng tỏ ra thích hợp với bài toán, đệ quy là một trong các công cụ hiệu
quả và linh hoạt này.
Trong giai đoạn thực hiện, chúng ta cần tìm xem phương pháp nào trong số
các phương pháp sẽ là tốt nhất so với từng tình huống. Có ít nhất hai cách để hiện
thực đệ quy trong hệ thống máy tính. Quan điểm chính của chúng ta khi xem xét
hai cách hiện thực khác nhau dưới đây là, cho dù có sự hạn chế về không gian và
thời gian chúng cũng nên được tách riêng ra khỏi quá trình thiết kế giải thuật, các
loại thiết bị tính toán khác nhau. Chúng ta sẽ tìm hiểu hai cách hiện thực đa xử lý
và đơn xử lý dưới đây.
2.3.1 Hiện thực đa xử lý:xử lý đồng thời.
Có lẽ rằng cách suy nghĩ tự nhiên về quá trình hiện thực của đệ quy là các hàm
không chiếm những phần riêng trong cùng một máy tính, mà chúng sẽ được thực
hiện trên những máy khác nhau, Bằng cách này, khi một hàm cần gọi một hàm
khác, nó khởi động chiếc tương ứng, và khi máy này kết thúc công việc, nó sẽ trả
về chiếc máy ban đầu kết quả tính được để chiếc máy ban đầu có thể tiếp tục công
việc. Nếu một hàm gọi đệ quy chính nó hai lần, đơn giản nó chỉ cần khởi động hai
chiếc máy khác để thực hiện những dòng lệnh y như những dòng lệnh mà nó đang
thực hiện. Khi hai máy hoàn tất công việc chúng trả kết quả về cho máy gọi
chúng. Nếu chúng cần gọi đệ quy, dĩ nhiên chúng cũng khởi động những chiếc
máy khác nữa.
Thông thường bộ xử lý trung ương là thành phần đắt nhất trong hệ thống
máy tính, nên bất kỳ ý nghĩ nào về một hệ thống có nhiều hơn một bộ xử lý cũng
cần phải xem xét đến xự lãng phí song song sẽ trở nên bình thường.
Với đa xử lý, nhưng người lập trình sẽ không còn xem xét các giải thuật
chỉ như một chuỗi tuyến tính các hành động, thay vào đó, cần phải nhận ra một số
phần của giải thuật có thể thực hiện song song. Cách xử lý này còn được gọi là xử
lý đồng thời (concurrent). Việc nghiên cứu về xử lý đồng thời và các phương pháp

kết nối giữa chúng hiện tại là một đề tài nghiên cứu trong khoa học máy tính, một
điều ,chắc chắn là nó sẽ cải tiến cách mà giải thuật sẽ được mô ta và hiện thực
trong nhiều năm tới.
2.3.2 Hiện thực đơn xử lý: vấn đề vùng nhớ.
Để xem xét làm cách nào mà đệ quy có thể được thực hiện trong một
hệ thống chỉ có một bộ xử lý chúng ta nhớ lại cơ cấu ngăn xếp của các lần gọi
hàm đã được giới thiệu ơ đầu chương. Một hàm khi được gọi phải có một
vùng nhớ riêng để chứa các biến cục bộ và các tham trị của nó, kể cả các trị
trong các thanh ghi và địa chỉ quay về khi nó chuẩn bị gọi một hàm khác . Sau
khi hàm kết thúc , nó sẽ không còn cần đến bất kỳ thứ gì trong vùng nhớ dành
riêng cho nó nữa. Thực sự là không có sự khác nhau giữa việc gọi một hàm đệ
quy và việc gọi một hàm không đệ quy. Khi một hàm chưa kết thúc , vùng nhớ
của nó sẽ bất khả xâm phạm. Một lần gọi hàm đệ quy cũng chưa là một lần gọi
hàm riêng biệt . Chúng ta cần chú ý rằng hai lần gọi đệ quy là hoàn toàn khac
nhau , để chung ta không lẫn vùng nhớ của chúng khi chúng chưa kết thúc.
Đối với hàm đệ quy , những thông tin lưu trữ dành cho lần gọi ngoài cần giữ
được cho đến khi nó kết thúc , như vậy một lần gọi bên trong phải sử dụng
một vùng nhớ khác làm vùng nhớ riêng của nó .
Đối với một hàm không đệ quy , vùng nhớ có thể là vùng nhớ cố định và
được dành cho lâu dài, do chúng không biết rằng một lần gọi hàm sẽ được trả
về trước khi hàm có thể lại được gọi lần nữa,và sau khi gọi trước được trả
về , các thông tin trong vùng nhớ của nó không cần thiết nữa. Vùng nhớ lâu
dài được dành sẵn cho các hàm không đệ quy có thể gây lẵng phí lớn, do
nhưng khi hàm không được yêu cầu thực hiện , vùng nhớ đó không thể được
sử dụng vào mục đích khác. Đó cũng là cách quản lý vùng nhớ dành cho các
hàm của các phiên bản cũ cuả các ngôn ngữ như FORTRAN và COBOL, và
chính điều này cũng là lý do mà các ngôn ngữ này không cho phép đệ quy.
• Sau đây là một số chương trình con đệ quy được minh họa như sau:
BÀI TOÁN THÁP HÀ NỘI
Thuật giải đệ quy

• đặt tên các cọc là A, B, C những tên này có thể chuyển ở các bước khác
nhau (ở đây: A = Cột Nguồn, B = Cột Đích, C = Cột Trung Gian)
• gọi n là tổng số đĩa
• đánh số đĩa từ 1 (nhỏ nhất, trên cùng) đến n (lớn nhất, dưới cùng)
Để chuyển n đĩa từ cọc A sang cọc B thì cần:
1. chuyển n-1 đĩa từ A sang C. Chỉ còn lại đĩa #n trên cọc A
2. chuyển đĩa #n từ A sang B
3. chuyển n-1 đĩa từ C sang B cho chúng nằm trên đĩa #n
Phương pháp trên được gọi là thuật giải đệ quy: để tiến hành bước 1 và 3, áp dụng lại
thuật giải cho n-1. Toàn bộ quá trình là một số hữu hạn các bước, vì đến một lúc nào đó
thuật giải sẽ áp dụng cho n = 1. Bước này chỉ đơn giản là chuyển một đĩa duy nhất từ cọc
A sang cọc C.
Ngôn ngữ Pascal biểu diễn giải thuật trên là:
VAR n: Integer;
Procedure chuyen(sodia: Integer; CotNguon: Char; CotDich: Char; CotTG:
Char);
Begin
If sodia>0 then begin
chuyen(sodia-1, CotNguon, CotTG, CotDich);
Writeln(CotNguon,'->',CotDich); { Dia lon nhat hien tai }
chuyen(sodia-1, CotTG, CotDich, CotNguon)
End;
End;
BEGIN
Write('Hay nhap so dia: '); Readln(n);
chuyen(n,'A','C','B');//Thực hiện chuyển từ cột A sang cột C với
cột B làm trung gian
Readln;
END.
3.1-Vấn đề khử đệ quy:

Như chúng ta đã biết phương pháp đệ quy không phải bao giờ cũng là
giải pháp hữu hiệu nhất
. Cứ mỗi lần gọi đệ quy máy phải dành một số vùng nhớ
để lưu trữ các trị, các thông số và biến cục bộ. Do đó, đệ quy tốn nhiều vùng nhớ,
thời gian truyền đối mục, thiết lập vùng nhớ trung gian và trả về kết quả…
Vậy để khắc phục hạn chế đó người ta nói đến khử đệ quy.
Khử đệ quy ở đây có
nghĩa là biến một thủ tục đệ quy thành một thủ tục chỉ chứa vòng lặp mà không
ảnh hưởng gì đến các yếu tố khác, chứ không phải là thay đổi thuật toán. Ví dụ
như trong các hàm đệ quy tính n! và số Fibonaci F(n) ta có thể thay bằng một vòng
lặp để tính. Trong trường hợp tổng quát, khử đệ quy là một việc làm khá phức tạp
và khó khăn. Có thể bạn sẽ nói rằng, vậy thì cứ sử dụng đệ quy, vừa ngắn gọn, dễ
hiểu, vừa dễ cài đặt. Nhưng có đôi khi, sự hạn hẹp của bộ nhớ dành cho chương
trình con không cho phép chúng ta làm điều đó, hoặc chúng ta biết rằng, ngôn ngữ
máy không có đệ quy, vì vậy các trình biên dịch đều phải có nhiệm vụ khử đệ quy.
Và một lí do nữa là bạn có thể thực sự gặp rắc rối với thủ tục đệ quy của mình khi
trong một môi trường lập trình mà không cung cấp khả năng gọi đệ quy. Khử đệ
quy giúp bạn vẫn giữ được nguyên bản thuật toán đệ quy của mình mà không hề
có lời gọi đệ quy, và như thế chương trình có thể chạy được trong bất kỳ môi
trường lập trình nào.
Khử đệ quy thực chất là chúng ta phải làm công việc của một trình biên
dịch đối với một thủ tục, đó là: Đặt tất cả các giá trị của các biến cục bộ và địa chỉ
của chỉ thị kế tiếp vào ngăn xếp (Stack), quy định các giá trị tham số cho thủ tục
và chuyển tới vị trí bắt đầu thủ tục, thực hiện lần lượt từng câu lệnh. Sau khi thủ
tục hoàn tất thì nó phải lấy ra khỏi ngăn xếp địa chỉ trả về và các giá trị của các
biến cục bộ, khôi phục các biến và chuyển tới địa chỉ trả về.
• Các bước gợi ý trong việc khử đệ quy một cách tổng quát:
Chúng ta có thể tạo một ngăn xếp để chứa các bản ghi, lệnh gọi đệ quy và lệnh trả
về từ hàm đệ quy có thể được thay thế như sau, mỗi lệnh gọi đệ quy có thể được
thay bởi:

1. Đưa vào ngăn xếp một bản ghi chứa các biến cục bộ, các thông số và vị trí
dòng lệnh ngay sau lệnh gọi đệ quy.
2. Gán mọi thông số về các trị mới thích hợp.
3. Trở về thực hiện dòng lệnh đầu tiên trong giải thuật đệ quy.
4. Mỗi lệnh trả về của hàm đệ quy được thay bởi:
5. Lấy từ ngăn xếp để khôi phục mọi biến cục bộ và thông số.
6. Bắt đầu thực hiện dòng lệnh tại vị trí mà trước đó đã được cất trong ngăn
xếp.
3.2 Một số phương pháp khử đệ quy:
Trong thực tế các bài toán về đệ quy rất phong phú , không phải bài toán đệ
quy nào cũng đều có thể đưa về dạng khử đệ quy một cách chuẩn mực. Nên việc
cài đặt các giải thuật khử đệ quy đòi hỏi phải hiểu rõ được tính chất đệ quy của bài
toán, trên cơ sở đó lựa chọn ra giải thuật phù hợp.
Để thực hiện giải thuật khử đệ quy trong kĩ thuật lập trình ta có thể sử dụng
thuật lặp, 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 giải thuật khử đệ quy cũng y hệ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 đệ quy thành ngôn ngữ máy.
3.2.1 Khử đệ quy bằng vòng lặp:
3.2.1.1 Hàm tính giá trị của dãy dữ liệu mô tả bằng hồi quy:
• Ý tưởng:
Xét một vòng lặp trong đó sử dụng một tập hợp biến W = ( V , U ) gồm tập
hợp U các biến bị thay đổi trong vòng lặp và V là các biến còn lại.
Dạng tổng quát của vòng lặp là: W:=W
o
; {W
o
= (U
o
, V

o
) }
While C(U) do U:=g(W) (1)
Gọi U
o
là trạng thái của U ngay trước vòng lặp, U
k
với k>0 là trạng thái của
U sau lần lặp thứ k (giả sử còn lặp đến lần k). Ta có:
U
o
mang các giá trị được gán ban đầu.
U
k
=g(W) =g(U
k-1
,V
o
) =f( U
k-1
) với k=1 n (2)
Với n là lần lặp cuối cùng, tức C(U
k
) đúng với mọi k<n, C(U
n
) sai.
Sau vòng lặp W mang nội dung (U
n
,V
o

).
Ta thấy để tính giá trị dãy được định nghĩa bởi quan hệ hồi quy dạng (2) ta có thể
dùng giải thuật lặp mô tả bởi đoạn lệnh (1).
• Giải thuật tính giá trị của dãy hồi quy thường gặp dạng:
= g(n, f(n-1)) khi n > n
o
Vi dụ:
• Xây dựng hàm FAC(n) = n! không đệ quy:
Function FAC (n: integer) : longint ;
Var k: integer ;
F: longint;
Begin
F:=1; k:=0;
While ( k<n ) do
begin
k :=k+1;
F :=F*k ;
end;
FAC :=F ;
End;

Hoặc:
Function FAC ( n: integer): longint;
Var k: integer;
F:longint;
Begin
F: =1;
For k: =1 to n do
F: =F *k;
FAC: =F;

End;
3.2.1.2 Các thủ tục khử đệ quy dạng đệ quy đuôi:
Xét thủ tục P dạng:
P(X)

if B(X) then D(X)
Else
{ A(X) ;
P(f(X));
}
Trong đó: X là tập biến (một hoặc một bộ nhiều biến).
P(X) là thủ tục đệ quy phụ thuộc X.
A(X),D(X) là các nhóm thao tac ( lệnh ) không đệ quy.
f(X) là hàm biến đổi X.
Xét quá trình thi hành P(X):
Gọi P
o
là lần gọi P thứ 0(đầu tiên) P(X)
P
1
là lần gọi P thư 1( lần hai) P(f(X))
P
i
là lần gọi P thứ i (lần i+1) P(f(f(…(f(X) )))
P(f
i
(X)) hợp i lần hàm f.
Trong lần gọi P
i
nếu B(f

i
(X)) không đúng (false) thì thi hành lệnh A và gọi
P
i+1,
nếu B(f
i
(X)) đúng (true) thì thi hành lệnh D và kết thúc quá trình gọi .
Giả sử P được gọi đúng n+1 lần.khi đó ở trong lần gọi cuối cùng (thứ n) P
n
thì
B(f
n
(X)) đúng,lệnh D được thi hành và chấm dứt thao tac gọi thủ tục P.
Sơ đồ khối quá trình thực hiện lệnh gọi thủ tục P(X) có dạng:
Tương ứng với vòng lặp sau:
While ( not B(X) ) do
Begin
A(X) ;
X := f(X) ;
End;
D(X) ;
Ví dụ: tìm ƯSCLN của hai số nguyên dựa vào thuật toán Euclide.
Ta có : giải thuật đệ quy (dưới dạng thủ tục) tìm ƯSCLN(m,n) bằng thuật toán
Euclide là: ƯSCLN( m,n,var us)

if ( n=0) then us:= m
else ƯSCLN(n,m mod n,us) ;
Trong trường hợp này thì: X là (m , n, us )
P(X) là USCL(m, n, us)
B(X) là n = 0

D(X) là lệnh gán us := m
A(X) là lệnh rỗng
F(X) là f(m, n, us) = (n, m mod n, us)
Đoạn lệnh lặp tương ứng là:
While (n <> 0 ) do begin
Sd := m mod n;
m := n;
n := sd;
end;
us := n;
*Thủ tục không đệ quy tương ứng trong Pascal
Procedure USCL (m, n : integer ; var us : integer );
Var sd : integer ;
Begin
While ( n <> 0 ) do begin
Sd := m mod n ;
m := n ;
n := sd;
end;
us := m ;
end;
3.2.1.3 Các hàm khử đệ quy dạng đệ quy đuôi ( tail-recusive ).
Xét hàm đệ quy dạng :
F(X) = - f(g(X)) khi C (X) đúng
- a(X) khi C(X) sai
Tức là :
F(X)

if (C(X)) then return ( f(g(X)))
Else return (a(X))

Ví dụ : Tìm ƯSCLN của hai số m và n nguyên (m,n>=0):
Với m, n >= 0 ta có hàm đệ quy tính USCLN (m, n) là:
USCLN (m, n)

if (m <> 0 ) then return (USCLN (abs(m – n),(m, n);
Else return n;
Trong trường hợp này :
X là (m, n) ;
C(X) = C(m, n) là m <> 0;
g(X) = g(m, n) = (abs(m – n) , (m, n));
a(X) = a(m, n) = n;
Đoạn chương trình tính USCLN (a,b) là:
m := a; n :=b;
while m <> 0 do begin
t1 := m;
t2 := n;
m := abs(t1 – t2);
n := min (t1, t2);
end;
USCLN := n;
Ta có hàm không đệ quy tương ứng trong Pascal.
Function USCLN ( m, n : integer ) : integer;
Var t1, t2 : integer;
Begin
While (n <> 0 ) do begin t1 := m ; t2 := n ;
m := abs( t1 – t2 );
if ( t1 < t2 ) then n := t1
else n := t2 ;
end;
USCLN := m;

3.2.2 Khử đệ quy một số dạng thủ tục đệ quy thường gặp:
3.2.2.1 Thủ tục đệ quy chỉ có một lệnh gọi là đệ quy trực tiếp.
Mô hình tổng quát của thủ tục đệ quy chỉ có một lệnh gọi đệ quy trực tiếp là:
P(X)

if C(X) then D(X)
Else begin
A(X);
P(f(X)) ;
B(X);

×