Đề tài Thuật toán đệ quy và cách khử đệ quy

Tài liệu Đề tài Thuật toán đệ quy và cách khử đệ quy: YÊU CẦU CỦA ĐỀ TÀI: Thuật toán đệ quy và cách khử đệ quy: Trình bày hiểu biết về thuật toán đệ quy, ưu nhược điểm của đệ quy. 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. Vấn đề khử đệ quy và một số phương pháp khử đệ quy, có ví dụ minh họa. 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 tron...

doc41 trang | Chia sẻ: hunglv | Lượt xem: 3154 | Lượt tải: 2download
Bạn đang xem trước 20 trang mẫu tài liệu Đề tài Thuật toán đệ quy và cách khử đệ quy, để tải tài liệu gốc về máy bạn click vào nút DOWNLOAD ở trên
YÊU CẦU CỦA ĐỀ TÀI: Thuật toán đệ quy và cách khử đệ quy: Trình bày hiểu biết về thuật toán đệ quy, ưu nhược điểm của đệ quy. 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. Vấn đề khử đệ quy và một số phương pháp khử đệ quy, có ví dụ minh họa. 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 A1, A2,….,An. Trong đó có một Ai đượ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 S1; S2; …;Sn 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: chuyển n-1 đĩa từ A sang C. Chỉ còn lại đĩa #n trên cọc A chuyển đĩa #n từ A sang B 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: Đư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. Gán mọi thông số về các trị mới thích hợp. Trở về thực hiện dòng lệnh đầu tiên trong giải thuật đệ quy. Mỗi lệnh trả về của hàm đệ quy được thay bởi: Lấy từ ngăn xếp để khôi phục mọi biến cục bộ và thông số. 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:=Wo ; {Wo = (Uo, Vo) } While C(U) do U:=g(W) (1) Gọi Uo là trạng thái của U ngay trước vòng lặp, Uk 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ó: Uo mang các giá trị được gán ban đầu. Uk =g(W) =g(Uk-1 ,Vo) =f( Uk-1 ) với k=1..n (2) Với n là lần lặp cuối cùng, tức C(Uk) đúng với mọi k<n, C(Un) sai. Sau vòng lặp W mang nội dung (Un,Vo). 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 > no 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 Po là lần gọi P thứ 0(đầu tiên) P(X) P1 là lần gọi P thư 1( lần hai) P(f(X)) Pi là lần gọi P thứ i (lần i+1) P(f(f(…(f(X)..))) P(fi(X)) hợp i lần hàm f. Trong lần gọi Pi nếu B(fi(X)) không đúng (false) thì thi hành lệnh A và gọi Pi+1,nếu B(fi(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) Pn thì B(fn(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; Khử đệ quy một số dạng thủ tục đệ quy thường gặp: 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); End; Với: + X là một biến đơn hoặc là một biến véc tơ. + C(X) là một biểu thức boolean của X. + A(X), P(f(X), B(X) là các nhóm lệnh không đệ quy (không chứa lệnh gọi đến P). + f(X) là hàm của X. * Tiến trình thực hiện của P(X) sẽ là: - Nếu C(X) đúng thì thực hiện D(X). -Nếu ( C(X) sai) thì thực hiện A(X); gọi P(f(X)) thực hiện B(X).(B(X) chỉ được thực hiện khi P(f(X)) thực hiện xong). Mỗi lần thành phần đệ quy P(Y) được gọi thì thông tin giải thuật 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ì ta phải thực hiện một dãy các thao tác B theo thứ tự : B(f(X)), B(f(X))…. B(f(f(X))),B(f(X)), B(X). Để thực hiện dãy thao tác { B(f(X))} theo thứ tự ngược với thứ tự phát sinh ta cần dãy dữ liệu {f(X)} truy xuất theo nguyên tắc LIFO (last in first out). Ta sẽ dùng một stack để lưu trữ dãy {f(X)} { X, f(f(X)), … f(X) ….f(X) } Trình tự thực hiện P(X) được diễn tả bằng mô hình sau : P(X) C(X) = false A(X); Push(S,X); U:=f(X);P(U); POP(S,U); B(U) ( U = X ) C(U) = False A(U); Push(S,U); U := f(U); P(U) ; POP(S,U);B(U) (U = X) C(U) = False A(U); Push(S,U); U:=f(U)); P(U); POP(S,U);B(U) --------------------------------------------------------------------------------------------------- C(U) = False A(U) ; Push(S,U); U:= f(U)); P(U); POP(S,U);B(U) (U = f(X)) C(U) = True D(U) Giải thuật thực hiện P(X) với việc sử dụng Stack có dạng : P(X) { Creat_Stack (S) ; ( tạo stack S ) While(not(C(X)) do begin A(X); Push (S,X) ; ( cất giá trị X vào stack S) X := f(X) ; End; D(X) ; While (not(EmptyS(S))) do begin Pop(S,X) ; (lấy dữ liệu từ S) B(X); End; } Ví dụ : Thủ tục đệ quy chuyển biểu diễn số từ cơ số thập phân sang nhị phân có dang: Binary(m) if(m>0) then begin Binary ( m div 2 ); write ( m mod 2 ); end; Trong trường hợp này: X là m. P(X) là Binary(m). A(X); D(X) là lệnh rỗng. B(X) là lệnh write(m mod 2 ) ; C(X) là ( m <= 0 ). f(X) = f(m) = m div 2. Giải thuật thực hiện Binary(m) không đệ quy là : Binary(m) { Creat_Stack (S) ; while (m > 0 ) do begin Sdu := m mod 2 ; Push (S,sdu); m := m div 2 ; end; while ( not (emptyS(S)) do begin POP(S,sdu); Write(sdu); End; } Nhiều lệnh gọi đệ quy trực tiếp: Thủ tục đệ quy với hai lần gọi trực tiếp: P(X) if C(X) then D(X) Else begin A(X); P(f(X)); B(X); P(g(X)); end; Quá trình thực hiện thủ tục P(X) sẽ là : -Nếu C(X) đúng thì thực hiện D(X). -Nếu C(X) sai thì tực hiện A(X), gọi P(f(X)) ;thực hiện B(X); gọi P(g(X)),khi gọi P(g(X)) thì lại phát sinh lệnh A(g(X)),như vậy ngoài việc phải lưu vào stack các giá trị f i(X) ta còn phải lưu vào các giá trị g i(X) tương ứng. khi ta lấy dữ liệu từ stack để thực hiện lệnh B(U) mà chưa gặp điều kiện kết thúc thì ta thực hiện P(g(U)) và lại phải lưu giá trị g(U) vào stack …Điều kiện dừng là khi truy xuất tới phần tử lưu đầu tiên trong stack. Như vậy,ngoài dữ liệu X còn phải lưu vào stack thêm thứ tự lần gọi (cụm gọi). *Thuật toán khử đệ quy tương ứng với thủ tục đệ quy P(X) là: Begin Creat _stack (S); Push (S,(X,1)) ; Repeat While ( not C(X)) do Begin A(X) ; Push (S,(X,2)); X:=f(X); End; D(X); Pop (S,(X,k)) ; If (k1) then begin B(X); X:=g(X); End; Until (k=1); End; Ví dụ : Khử đệ quy thủ tục tháp Hà Nội (THN): +Dạng đệ quy là: THN(n, X,Y, Z) if( n>0) then begin THN(n-1,X,Z,Y); Move(X,Z); THN(n-1,Y, X,Z); End; Với n là số đĩa ,X là cột đầu ,Z là cột cuối ,Y là cột giữa ,move(X,Z) là thao tác chuyển một đĩa từ cột X sang cột Z. Trong trường hợp này : -Biến X là bộ (n,X,Y,Z). -C(X) là biểu thức Boolean (n<=0). -D(X),A(X) là thao tác rỗng . -B(X)= B(n,X,Y,Z) là thao tác move (X,Z); -f(X)= f(n,X,Y,Z) =(n-1,X,Z,Y). -g(X)= g(n, X,Y,Z) =(n-1,Y,X,Z) . +Giải thuật không đệ quy tương đương là: Begin Creat _stack(S); Push(S, (n,X,Y,Z,1)); Repeat While( n>0) do begin Push (S,(n,X,Y,Z,2)); n:=n-1; swap(Y,Z); end; (* swap(a,b) là thủ tục hoán đổi nội dung 2 biến a,b *) Pop (S,(n,X,Y,Z,k)); If (k1) then begin Move(X,Z); n:=n-1; swap(X,Y); end; until (k=1); end; Trường hợp n lần gọi đệ quy trực tiếp : tương tự với hai lần gọi trực tiếp. (các bạn có thể tham khảo thêm). CHƯƠNG 4: CÀI ĐẶT THUẬT TOÁN SELECTION SORT 4.1: Thuật toán selection sort bàng phương pháp đệ quy. 4.2: Khử đệ quy thuật toán selection sort. Giải thuật: Ý tưởng của thuật toán Selection Sort hay còn gọi là thuật toán sắp xếp chọn trực tiếp như sau: duyệt dãy N phần tử của danh sách, tìm phần tử nhỏ nhất, đưa phần tử này về vị trí đầu. Bỏ qua phần tử đầu tiên (phần tử nhỏ nhất trong dãy đã được đem lên trong lần duyệt đầu tiên), duyệt dãy N - 1 phần tử còn lại, tìm phần tử nhỏ nhất rồi đưa phần tử này về vị trí thứ 2. Lặp lại quá trình này cho đến khi dãy cần duyệt chỉ còn lại 1 phần tử. Cài đặt thuật toán Selection Sort bằng ngôn ngữ pascal PROGRAM VD_TAO_DAY_SO_SAP_XEP; USES GRAPH,CRT; CONST KT=20; R=20; L=30; YORG=200; T=9; TYPE B1=ARRAY[1..KT] OF INTEGER; VAR F1,F: FILE OF B1; A,B:B1; ST:ARRAY[1..KT] OF STRING; H:ARRAY[1..KT] OF INTEGER; GD,GM,N,J,K,I,TG,T1,MIN:INTEGER; PROCEDURE VEKHUNG; VAR N:INTEGER; BEGIN I:=0; ASSIGN(F1,'MANG.DAT'); RESET(F1); WHILE NOT EOF(F1) DO BEGIN I:=I+1; READ(F1,B); STR(B[I],ST[I]); SETFILLSTYLE(1,LIGHTRED); H[1]:=100; BAR(H[I]-L,YORG,H[I]+L,YORG+R); SETCOLOR(YELLOW); OUTTEXTXY(H[I],YORG +(R DIV 2),ST[I]); H[I+1]:=H[I]+75; END; CLOSE(F1); END; PROCEDURE XUONG1(C1:INTEGER;U:STRING); VAR A,X1,Y1,X2,Y2:INTEGER; PROCEDURE VE( X1,Y1,X2,Y2:INTEGER); BEGIN SETCOLOR(YELLOW); OUTTEXTXY(A,Y2-(R DIV 2),U); DELAY(T); SETFILLSTYLE(1,LIGHTRED); BAR(X1,Y1,X2,Y2); END; PROCEDURE XOA( X1,Y1,X2,Y2:INTEGER); VAR DY1,DY2:INTEGER; BEGIN DY2:=Y1;DY1:=Y1-R; SETFILLSTYLE(1,BLUE); BAR(X1,DY1,X2,DY2); OUTTEXTXY(A,Y2-(R DIV 2),U); END; BEGIN A:=C1; X1:=A-L;X2:=A+L;Y1:=YORG; Y2:=YORG+R; WHILE Y1<=300 DO BEGIN VE(X1,Y1,X2,Y2); XOA(X1,Y1,X2,Y2); Y1:=Y1+1;Y2:=Y2+1; END; END; PROCEDURE XUONG2(C1:INTEGER;U:STRING); VAR A,X1,Y1,X2,Y2:INTEGER; PROCEDURE VE( X1,Y1,X2,Y2:INTEGER); BEGIN SETCOLOR(YELLOW); OUTTEXTXY(A,Y2-(R DIV 2),U); DELAY(T); SETFILLSTYLE(1,LIGHTRED); BAR(X1,Y1,X2,Y2); END; PROCEDURE XOA( X1,Y1,X2,Y2:INTEGER); VAR DY1,DY2:INTEGER; BEGIN DY2:=Y1;DY1:=Y1-R; SETFILLSTYLE(1,BLUE); BAR(X1,DY1,X2,DY2); OUTTEXTXY(A,Y2-(R DIV 2),U); END; BEGIN A:=C1; X1:=A-L;X2:=A+L;Y1:=100; Y2:=100+R; WHILE Y1<=YORG DO BEGIN VE(X1,Y1,X2,Y2); XOA(X1,Y1,X2,Y2); Y1:=Y1+1;Y2:=Y2+1; END; END; PROCEDURE LEN1(C1:INTEGER;U:STRING); VAR A,X1,Y1,X2,Y2:INTEGER; PROCEDURE VE( X1,Y1,X2,Y2:INTEGER); BEGIN SETCOLOR(YELLOW); OUTTEXTXY(A,Y2-(R DIV 2),U); DELAY(T); SETFILLSTYLE(1,LIGHTRED); BAR(X1,Y1,X2,Y2); END; PROCEDURE XOA( X1,Y1,X2,Y2:INTEGER); VAR DY1,DY2:INTEGER; BEGIN DY1:=Y2;DY2:=Y2+R; SETFILLSTYLE(1,BLUE); BAR(X1,DY1,X2,DY2); OUTTEXTXY(A,Y2-(R DIV 2),U); END; BEGIN A:=C1; X1:=A-L;X2:=A+L;Y1:=YORG; Y2:=YORG+R; WHILE Y1>=100 DO BEGIN VE(X1,Y1,X2,Y2); XOA(X1,Y1,X2,Y2); Y1:=Y1-1;Y2:=Y2-1; END; END; PROCEDURE LEN2(C1:INTEGER;U:STRING); VAR A,X1,Y1,X2,Y2:INTEGER; PROCEDURE VE( X1,Y1,X2,Y2:INTEGER); BEGIN SETCOLOR(YELLOW); OUTTEXTXY(A,Y2-(R DIV 2),U); DELAY(T); SETFILLSTYLE(1,LIGHTRED); BAR(X1,Y1,X2,Y2); END; PROCEDURE XOA( X1,Y1,X2,Y2:INTEGER); VAR DY1,DY2:INTEGER; BEGIN DY1:=Y2;DY2:=Y2+R; SETFILLSTYLE(1,BLUE); BAR(X1,DY1,X2,DY2); OUTTEXTXY(A,Y2-(R DIV 2),U); END; BEGIN A:=C1; X1:=A-L;X2:=A+L;Y1:=300; Y2:=300+R; WHILE Y1>=YORG DO BEGIN VE(X1,Y1,X2,Y2); XOA(X1,Y1,X2,Y2); Y1:=Y1-1;Y2:=Y2-1; END; END; PROCEDURE PHAI(C1,C2:INTEGER;U:STRING); VAR A,B,X1,X2,Y1,Y2:INTEGER; PROCEDURE VE( X1,Y1,X2,Y2:INTEGER); BEGIN SETCOLOR(YELLOW); OUTTEXTXY(X1+L,Y1+(R DIV 2),U); DELAY(T); SETFILLSTYLE(1,LIGHTRED); BAR(X1,Y1,X2,Y2); END; PROCEDURE XOA( X1,Y1,X2,Y2:INTEGER); VAR DX1,DX2:INTEGER; BEGIN DX2:=X1; DX1:=X1-L; SETFILLSTYLE(1,BLUE); BAR(DX1,Y1,DX2,Y2); OUTTEXTXY(X1+L,Y1+(R DIV 2),U); END; BEGIN A:=C1;B:=C2; X1:=A-L;X2:=A+L;Y1:=100; Y2:=100+R; WHILE X1+L <= B DO BEGIN VE(X1,Y1,X2,Y2); XOA(X1,Y1,X2,Y2); X1:=X1+1; X2:=X2+1; END; END; PROCEDURE TRAI(C1,C2:INTEGER;U:STRING); VAR A,B,X1,X2,Y1,Y2:INTEGER; PROCEDURE VE( X1,Y1,X2,Y2:INTEGER); BEGIN SETCOLOR(YELLOW); OUTTEXTXY(X1+L,Y1+(R DIV 2),U); DELAY(T); SETFILLSTYLE(1,LIGHTRED); BAR(X1,Y1,X2,Y2); END; PROCEDURE XOA( X1,Y1,X2,Y2:INTEGER); VAR DX1,DX2:INTEGER; BEGIN DX1:=X2; DX2:=X2+L; SETFILLSTYLE(1,BLUE); BAR(DX1,Y1,DX2,Y2); OUTTEXTXY(X1+L,Y1+(R DIV 2),U); END; BEGIN A:=C1;B:=C2; X1:=A-L;X2:=A+L;Y1:=300; Y2:=300+R; WHILE X1+L >= B DO BEGIN VE(X1,Y1,X2,Y2); XOA(X1,Y1,X2,Y2); X1:=X1-1; X2:=X2-1; END; END; PROCEDURE CHUYEN; BEGIN I:=0; ASSIGN(F1,'MANG.DAT'); RESET(F1); H[1]:=100; WHILE NOT EOF(F1) DO BEGIN I:=I+1; READ(F1,B); H[I+1]:=H[I]+75; END; FOR I:=1 TO N-1 DO BEGIN MIN:=B[I]; T1:=I; FOR J:=I+1 TO N DO IF B[J]<MIN THEN BEGIN MIN:=B[J]; T1:=J; END; IF T1 I THEN BEGIN STR(B[I],ST[I]); STR(B[T1],ST[T1]); DELAY(9);DELAY(9); LEN1(H[I],ST[I]); XUONG1(H[T1],ST[T1]); PHAI(H[I],H[T1],ST[I]); TRAI(H[T1],H[I],ST[T1]); XUONG2(H[T1],ST[I]); LEN2(H[I],ST[T1]); DELAY(9); TG:=B[I]; B[I]:=B[T1]; B[T1]:=TG; END; END; CLOSE(F1); END; BEGIN CLRSCR; WRITE(' BAN HAY VAO SO PHAN TU MANG N :'); READLN(N); WRITELN(' BAN HAY VAO GIA TRI PHAN TU MANG '); FOR I:=1 TO N DO BEGIN WRITE('A[',I,'] :'); READLN(A[I]); END; ASSIGN(F,'MANG.DAT'); REWRITE(F); WRITELN;WRITELN('IN LAI MANG');WRITELN; FOR I:=1 TO N DO BEGIN WRITE(F,A); WRITE(A[I]:3); END; CLOSE(F); FOR I:=1 TO N-1 DO BEGIN MIN:=A[I];K:=I; FOR J:=I+1 TO N DO IF A[J]<MIN THEN BEGIN MIN:=A[J]; K:=J END; TG:=A[I]; A[I]:=A[K]; A[K]:=TG; END; WRITELN;WRITELN;WRITELN('SEP LAI MANG');WRITELN; FOR I:=1 TO N DO WRITE(A[I]:3); WRITELN; WRITELN;WRITELN('AN ENTER DE XEM MINH HOA'); READLN; GD:=DETECT; INITGRAPH(GD,GM,''); IF GRAPHRESULT GROK THEN HALT(1); SETBKCOLOR(BLUE); SETCOLOR(YELLOW); OUTTEXTXY(200,20,'BAI TOAN SAP XEP 2'); OUTTEXTXY(190,40,'THEO THU TU TANG DAN'); OUTTEXTXY(190,60,'PHUONG SELECTION SORT '); SETFILLSTYLE(8,MAGENTA); BAR(0,350,700,400); SETCOLOR(YELLOW); OUTTEXTXY(140,370,'BAN HAY AN MOT NUT DE CHAY CHUONG TRINH '); REPEAT VEKHUNG; UNTIL KEYPRESSED; CHUYEN; SETCOLOR(YELLOW); OUTTEXTXY(180,300,'BAN THAY CHUONG TRINH THE NAO ?'); OUTTEXTXY(260,420,'THE END !'); READLN; CLOSEGRAPH; END. KẾT LUẬN: Trên đây là toàn bộ nội dung chúng tôi muốn gửi tới các bạn,những vấn đề cơ bản về đệ quy,chúng tôi mong rằng đây sẽ là tài liệu hữu ích cho các bạn khi tìm hiểu về đệ quy. Chúng tôi đã đưa ra một cách khái quát và khá chi tiết những hiểu biết về đệ qui,cũng như đưa ra khái niệm, thuật toán đệ quy, bên cạnh đó để các bạn có thể hiểu hơn về đệ quy, đồng thời đề tài cũng đưa ra những ví dụ cụ thể để các bạn có thể hiểu một cách sâu sắc về những khái niệm mang tính chất trừu tượng như đệ quy.Chúng tôi cũng đã trình bày khá chi tiết về khử đệ quy,đây là một giải thuật làm mất tính đệ quy của một chương trình đệ quy. Mặc dù đã rất cẩn thận trong quá trình thực hiện đề tài song vẫn không tránh khỏi những thiếu sót rất mong những ý kiến đóng góp của các thầy cô và các bạn. TÀI LIỆU THAM KHẢO 1. Giaó trình toán rời rạc - Bộ môn khoa học máy tính. 2. Giáo trình kĩ thuật lập trình nâng cao - Trần Hoàng Thọ (Trường Đại học Đà Lạt 2002). 3. Kỹ thuật lập trình C cơ sở và nâng cao – Phạm Văn Ất. 4. Cấu trúc dữ liệu và giải thuật – Đỗ Xuân Lôi,NXB Khoa học kỹ thuật ( 1998).

Các file đính kèm theo tài liệu này:

  • docde_tai_toan_roi_rac_5425.doc
Tài liệu liên quan