Tìm phương án giải thuật giải bài toán trong trường hợp tổng quát 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ướ[r]
(1)TRƯỜNG ĐẠI HỌC ĐAØ LẠT F7G GIAÙO TRÌNH KYÕ THUAÄT LAÄP TRÌNH NAÂNG CAO TRẦN HOAØNG THỌ 2002 Lop11.com (2) Kyõ thuaät laäp trình naâng cao -2- MUÏC LUÏC LỜI NÓI ĐẦU PHAÀN I CHÖÔNG I I MỞ ĐẦU Mô tả đệ quy Các loại đệ quy II MÔ TẢ ĐỆ QUY CÁC CẤU TRÚC DỮ LIỆU III MÔ TẢ ĐỆ QUY GIẢI THUẬT Giải thuật đệ quy Chương trình đệ quy Mã hóa giải thuật đệ qui các ngôn ngữ lập trình 11 Một số dạng giải thuật đệ quy đơn giản thường gặp 13 CHÖÔNG II 16 I CÁC NỘI DUNG CẦN LAØM ĐỂ TÌM GIẢI THUẬT ĐỆ QUY CHO MỘT BAØI TOÁN 16 Thông số hoá bài toán 16 Phát 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.16 Phân rã bài toán tổng quát theo phương thức đệ quy 16 II MỘT SỐ BAØI TOÁN GIẢI BẰNG GIẢI THUẬT ĐỆ QUY ĐIỂN HÌNH 17 Bài toán tháp Hà Nội 17 Bài toán chia thưởng 19 Bài toán tìm tất các hoán vị dãy phần tử 21 Bài toán xếp mảng phương pháp trộn (Sort-Merge) 24 Bài toán tìm nghiệm xấp xỉ phương trình f(x)=0 25 CHÖÔNG III 28 I CƠ CHẾ THỰC HIỆN GIẢI THUẬT ĐỆ QUY 28 II TỔNG QUAN VỀ VẤN ĐỀ KHỬû ĐỆ QUY 32 III CÁC TRƯỜNG HỢP KHỬ ĐỆ QUY ĐƠN GIẢN 33 Các trường hợp khử đệ quy vòng lặp 33 Khử đệ quy hàm đệ quy arsac 41 Khử đệ quy số dạng thủ tục đệ quy thường gặp 45 Phaàn II 52 CHÖÔNG IV 52 I CÁC GIAI ĐOẠN TRONG CUỘC SỐNG CỦA MỘT PHẦN MỀM 52 1) Đặc tả bài toán 52 2) Xây dựng hệ thống 52 3) Sử dụng và bảo trì hệ thống 53 II ÑAËC TAÛ 53 Đặc tả bài toán 53 Ñaëc taû chöông trình (ÑTCT) 54 Đặc tả đoạn chương trình 55 III NGÔN NGỮ LẬP TRÌNH 57 CHÖÔNG V 59 I CÁC KHÁI NIỆM VỀ TÍNH ĐÚNG 59 II HEÄ LUAÄT HOARE (HOARES INFERENCE RULES) 59 Caùc luaät heä quaû (Consequence rules) 60 Trần Hoàng Thọ Khoa Toán - Tin Lop11.com (3) Kyõ thuaät laäp trình naâng cao -3- Tiên đề gán (The Assignement Axiom) 61 Caùc luaät veà caùc caáu truùc ñieàu khieån 61 III KIỂM CHỨNG ĐOẠN CHƯƠNG TRÌNH KHÔNG CÓ VÒNG LẶP 64 IV KIỂM CHỨNG ĐOẠN CHƯƠNG TRÌNH CÓ VÒNG LẶP 68 Baát bieán 68 Lý luận quy nạp và chứng minh quy nạp 70 Kiểm chứng chương trình có vòng lặp while 71 CHÖÔNG VI 76 I CAÙC KHAÙI NIEÄM 76 Đặt vấn đề 76 Ñònh nghóa WP(S,Q) 76 Heä quaû cuûa ñònh nghóa 76 Caùc ví duï 77 II TÍNH CHAÁT CUÛA WP 77 III CÁC PHÉP BIẾN ĐỔI TÂN TỪ 78 Toán tử gán (tiên đề gán) 78 Toán tử 78 Toán tử điều kiện 79 Toán tử lặp 80 IV LƯỢC ĐỒ KIỂM CHỨNG HỢP LÝ VAØ CÁC ĐIỀU KIỆN CẦN KIỂM CHỨNG 84 Lược đồ kiểm chứng 84 Kiểm chứng tính đúng 85 Tập tối tiểu các điều kiện cần kiểm chứng 93 PHU LUÏC 96 I LOGIC TOÁN 96 II LOGIC MỆNH ĐỀ 96 Phaân tích 96 Các liên từ logic 97 Ýnghĩa các liên từ Logic Bảng chân trị 97 Lý luận đúng 98 Töông ñöông (Equivalence) 99 Tính thay thế, tính truyền và tính đối xứng 100 Bài toán suy diễn logic 100 Caùc luaät suy dieãn (rules of inference) 102 III LOGIC TÂN TỪ 103 Khaùi nieäm 103 Các lượng từ logic 105 Tập hợp và tân tưØ 107 Các lượng từ số học 107 Trần Hoàng Thọ Khoa Toán - Tin Lop11.com (4) Kyõ thuaät laäp trình naâng cao -4- LỜI NÓI ĐẦU Giáo trình viết theo nội dung môn học “ Kỹ thuật lập trình nâng cao” với mục ñích laøm taøi lieäu tham khaûo chính cho moân hoïc Giaùo trình goàm phaàn chính vaø moät phuï luïc : Phần I Đệ quy Trình bày chủ đề đệ quy lập trình bao gồm các nội dung sau : - Khái niệm đệ quy và vai trò nó lập trình - Cách xây dựng giải thuật cho bài toán phương pháp đệ quy - Cơ chế thực giải thuật đệ quy - Khử đệ quy Phần II Kiểm chứng chương trình Trình bày chủ đề kiểm chứng tính đúng chương trình bao gồm các nội dung sau: - Vai trò vấn đề kiểm chứng lập trình - Các phương pháp dùng để kiểm chứng tính đúng - Hệ luật Hoare và áp dụng nó vào kiểm chứng tính đúng có điều kiện - Hệ luật Dijkstra và áp dụng nó vào kiểm chứng tính đúng đầy đủ - Dạng tổng quát bài toán kiểm chứng và phương pháp kiểm chứng Các lược đồ kiểm chứng và tập tối thiểu các điều kiện cần kiểm chứng Phụ lục Các kiến thức chung logic Trình bày các kiến thức ban đầu logic mệnh đề và logic tân từ Phụ lục cung cấp một tài liệu cô đọng các kiến thức logic áp dụng trực tiếp phần I và phần II ( nó là phần nôi dung giáo trình nhập môn toán) người học cần dành thời gian thích hợp ôn lại để có thể theo kịp hướng tiếp cận giáo trình Cùng với trình bày lý thuyết tổng quát, tác gỉa đưa vào số thỏa đáng các ví dụ chọn lọc nhằm giúp người học nắm bắt chất các khái niệm, các phương pháp và làm quen với cách sử dụng các kết qủa Khi học trước tìm cách giải các bài tập thầy gíao cung cấp các bạn cố gắng đọc và hiểu hết các ví dụ minh hoïa Vì nhieàu leõ chaéc chaén giaùo trình coøn nhieàu khieám khuyeát Raát mong taát caû moïi người sử dụng chân thành góp ý Tác giả chân thành cảm ơn các đồng nghiệp khoa Toán_Tin đã đóng góp nhieàu yù kieán quyù baùu cho vieäc hình thaønh caáu truùc chi tieát cho noäi dung giaùo trình, chân thành cảm ơn thạc sỹ Võ Tiến đã đóng góp nhiều ý kiến quý báu cấu trúc giaùo trình, giuùp chænh lyù nhieàu khieám khuyeát baûn thaûo ÑaLat ngaøy 01 thaùng 12 naêm 2002 TRẦN HOAØNG THỌ Trần Hoàng Thọ Khoa Toán - Tin Lop11.com (5) Kyõ thuaät laäp trình naâng cao -5- PHAÀN I ĐỆ QUY CHÖÔNG I KHÁI NIỆM ĐỆ QUY I MỞ ĐẦU Mô tả đệ quy Trong nhiều tình việc mô tả các bài toán, các giải thuật, các kiện, các vật các quá trình, các cấu trúc, đơn giản và hiệu ta nhìn nó góc độ mang tính đệ qui Mô tả mang tính đệ qui đối tượng là mô tả theo cách phân tích đối tượng thaønh nhieàu thaønh phaàn maø soá caùc thaønh phaàn coù thaønh phaàn mang tính chaát cuûa chính đối tượng mô tả Tức là mô tả đối tượng qua chính nó Caùc ví duï : - Mô tả đệ quy tập số tự nhiên N : + Số là số tự nhiên ( ∈ N) + Số tự nhiên số tự nhiên cộng ( n ∈ N ⇒ ( n +1 ) ∈ N ) - Mô tả đệ quy cấu trúc xâu (list) kiểu T : + Caáu truùc roãng laø moät xaâu kieåu T + Ghép nối thành phần kiểu T(nút kiểu T ) với xâu kiểu T ta có xaâu kieåu T - Mô tả đệ quy cây gia phả : Gia phả người bao gồm mgười đó và gia phả cuûa cha vaø gia phaû cuûa meï - Moâ taû ñeâ quy thuû tuïc choïn hoa haäu : + Chọn hoa hậu khu vực + Choïn hoa haäu cuûa caùc hoa haäu - Mô tả đệ quy thủ tục tăng dãy a[m:n] ( dãy a[m], a[m+1], , a[n] ) phöông phaùp Sort_Merge (SM) : SM (a[m:n]) ≡ Merge ( SM(a[m : (n+m) div 2]) , SM (a[(n+m) div +1 : n] ) Với : SM (a[x : x]) laø thao taùc roãng (khoâng laøm gì caû ) Merge (a[x : y] , a[(y+1) : z]) laø thuû tuïc troän daõy taêng a [x : y] , a[(y+1) : z] để dãy a[x : z] tăng - Đinh nghĩa đệ quy hàm giai thừa FAC( n) = n ! 0!=1 n!=n*(n-1)! Trần Hoàng Thọ Khoa Toán - Tin Lop11.com (6) Kyõ thuaät laäp trình naâng cao -6- Phương pháp đệ quy mạnh chổ nó cho phép mô tả tập lớn các đối tượng số ít các mệnh đề mô tả giải thuật phức tạp số ít các thao tác (một chương trình đệ quy) Một mô tả đệ quy đầy đủ gồm phần : - Phần neo : mô tả các trường hợp suy biến đối tượng (giải thuật) qua caáu truùc (thao taùc) cuï theå xaùc ñònh ví dụ: là số tự nhiên, cấu trúc rỗng là xâu kiểu T, ! = , SM (a[x:x]) laø thao taùc roãng - Phần quy nạp: mô tả đối tượng (giải thuật) trường hợp phổ biến thông qua chính đối tượng (giải thuật ) đó cách trực tiếp gián tiếp Ví duï : n! = n * (n – 1) ! SM (a[m:n]) ≡ Merge (SM (a[m:( m+n) div 2] , SM (a[(m+n) div +1 : n]) ) Nếu mô tả không có phần neo thì đối tượng mô tả có cấu trúc lớn vô hạn, giải thuật mô tả trở thành cấu trúc lặp vô tận Các loại đệ quy Người ta phân đệ quy thành 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 mô tả trực tiếp qua nó : A mô tả qua A, B, C, đó B, C, không chứa A (các ví dụ trên) - Đệ quy gián tiếp là loại đệ quy mà đối tượng mô tả gián tiếp qua nó : A mô tả qua A1 ,A2 , , An Trong đó có Ai mô tả qua A Ví duï 1: Moâ taû daïng toång quaùt moät chöông trình vieát treân NNLT Pascal : Moät Chöông trình Pascal goàm : a) Đầu chương trình (head) gồm: Program Tên ; b) Thaân chöông trình (blok) goàm : b1) Khai báo unit, định nghĩa hằng, nhãn, kiểu liệu, khái báo biến b2) Ñònh nghóa caùc chöông trình goàm : b2.1) Đầu chương trình : Procedure Tên thủ tục ( danh sách thông số hình thức ) ; Function Tên hàm ( danh sách thông số hình thức ) : Kiểu ; b2.2) Thaân chöông trình ( Blok ) b2.3) Daáu ‘ ; ‘ b3) Phaàn leänh : laø moät leänh gheùp daïng : Begin S1 ; S2 ; ; Sn End ; c) Daáu keát thuùc chöông trình : ‘.’ Ví dụ : Mô tả hai dãy số {Xn},{Yn} theo luật đệ quy hổ tương sau : X0 = ; Xn = Xn-1 + Yn-1 ; Y0 = ; Yn =n2 Xn-1 + Yn-1 ; Trần Hoàng Thọ Khoa Toán - Tin Lop11.com (7) Kyõ thuaät laäp trình naâng cao -7- II MÔ TẢ ĐỆ QUY CÁC CẤU TRÚC DỮ LIỆU Trong toán học , lập trình người ta thường sử dụng đệ quy để mô tả các cấu trúc phức tạp, có tính đệ quy Bởi mô tả đệ quy không là cách mô tả ngắn gọn các cấu trúc phức tạp mà còn tạo khả để xây dựng các thao tác xử lý trên các cấu trúc phức tạp các giải thuật đệ qui Một cấu trúc liệu có tính đệ quy thường gồm số thành phần liệu cùng kiểu ghép nối theo cùng phương thức Ví duï 1: Mô tả đệ quy cây nhi phân : Caây nhi phaân kieåu T : + Hoặc là cấu trúc rỗng (phần neo) + Hoặc là nút kiểu T (nút gốc) và cây nhị phân kiểu T rời (cây nhị phân phải, cây nhị phân trái) kết hợp với Ví duï 2: Mô tả đệ quy mảng nhiều chiều : + Mảng chiều là dãy có thứ tự các thành phần cùng kiểu + Maûng n chieàu laø maûng chieàu maø caùc thaønh phaàn coù kieåu maûng n-1 chieàu III MÔ TẢ ĐỆ QUY GIẢI THUẬT Giải thuật đệ quy 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ả dãy lớn các thao tác số ít các thao tác đó có chứa thao tác gọi lại giải thuật (gọi đệ quy) Một cách tổng quát giải thuật đệ quy biểu diễn 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 tiến trình gọi đê quy không kết thúc nó không có khả gặp trường hợp neo, vì quan tâm đến điều kiện dừng giải thuật đệ quy luôn đặt Để kiểm soát qúa trình gọi đệ quy 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 điều kiện B xác định và biến đổi qua lần gọi P , qúa trình gọi P sẻ dừng B không thoûa Mô hình tổng quát giải thuật đệ quy với quan tâm đến dừng sẻ là : P ≡ if B then P[ S , P ] 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 đó có dạng : P(n) ≡ If ( n > ) then P[ S , P(n - 1)] ; P(n) ≡ P[ S , if (n >0) then P(n - 1) ] ; Trần Hoàng Thọ Khoa Toán - Tin Lop11.com (8) Kyõ thuaät laäp trình naâng cao -8- Trong các ứng dụng thực tế số lần gọi đệ quy (độ sâu đệ quy) không phải hữu hạn mà còn phải đủ nhỏ Bởi vì lần gọi đệ quy cần vùng nhớ vùng nhớ cũ phải trì Chương trình đệ quy a) Các hàm đệ quy Định nghĩa hàm số đệ quy thường gặp toán học, điển hình là các hàm nguyeân moâ taû caùc daõy soá hoài quy Ví duï Dãy các giai thừa : { n! } ≡ ,1 , , , 24 , 120 , 720 , 5040 , Kyù hieäu FAC(n ) = n ! Ta coù : + FAC(0 ) = ; ( ! = ) + FAC(n ) = n * FAC(n - ) ; ( n ! = n * (n - ) ! ) với n >= Giải thuật đệ quy tính FAC(n ) là : FAC(n ) ≡ if (n = ) then return ; else return (n * FAC(n - )) ; Ví duï Daõy soá Fibonaci(FIBO) : { FIBO (n) } ≡ ,1 , , , , , 13 , 21 , 34 , 55 , 89 , 144 , 233 , 377 , + FIBO(0 ) = FIBO (1 ) = ; + FIBO(n ) = FIBO (n - ) + FIBO ( n - ) ; với n > = Giải thuật đệ quy tính FIBO ( n ) là : FIBO(n) ≡ if ((n = ) or ( n = )) then return ; else return ( FIBO (n - 1) + FIBO (n - 2)) ; Ví duï Dãy các tổ hợp : 1 1 3 1 Cn = với n > = Cnm = với m > n > Cnm = Cnm−−11 + Cnm−1 Giải thuật đệ quy tính Cnm với n > m > laø : if ( m = ) then return ; else if (m > n ) then return ; else return ( Cnm−−11 + Cnm−1 ) ; Nhaän xeùt : Một định nghĩa hàm đệ quy gồm : Trần Hoàng Thọ Khoa Toán - Tin Lop11.com (9) Kyõ thuaät laäp trình naâng cao -9- + Một số các trường hợp suy biến mà gía trị hàm đó đã biết trước có thể tính cách đơn giản (không đệ quy ) Nhö : FAC(0 ) = , FIBO(0) = FIBO(1) = , Cn0 = , Cnm = với m > n > + Trường hợp tổng quát việc tính hàm sẻ đươc đưa tính hàm giá trị “ bé hơn” (gần với giá trị neo) đối số Nhö : FAC(n ) = n * FAC(n - ) ; FIBO(n) = FIBO(n -1) + FIBO( n - ) Trong tập biến hàm có nhóm mà độ lớn nó định độ phức tạp 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 nhóm biến điều khiển ứng với trường hợp suy biến Gía trị nhóm biến điều khiển sẻ thay đổi qua 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 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 sử dụng để mô tả các thao tác trên cấu trúc liệu có tính đệ quy Ví duï : Xem dãy n phần tử a[1:n] là kết hợp dãy a[1:n-1] và a[n] Do ño ù: - Thủ tục tìm max dãy a[1:n] ( thủ tục TMax) có thể thực theo luật đệ qui : + Tìm max dãy a[1:n] (gọi đệ quy Tmax(a[1:n-1] ) ) + Tìm max số : Tmax(a[1:n-1]) và a[n] (giải thuật không đệ quy) Tức là : TMax(a[1:n]) = max(TMax(a[1:n-l]) , a[n] ) với TMax(a[m:m] = a[m] ; ( trường hợp neo ) max(x,y) = x > y ? x : y ; ( giaûi thuaät tính max soá : if (x>y) then max(x ,y) = x else max(x ,y) = y ) - Thủ tục tính tổng các phần tử ( thủ tục TSUM ) có thể thực theo luật đệ quy : + Tìm tổng dãy a[1:n] (gọi đệ quy TSUM(a[1:n-1]) ) + Tìm tổng số : TSUM(a[1:n-1]) và a[n] (giải thuật không đệ quy) Tức là : TSUM(a[1:n]) = a[n] + TSUM(a[1:n-1] với TSUM(a[m:m]) = a[m] Ví duï : Xem dãy a[m : n] là kết nối hai dãy: dãy a[m:((m+n) div 2)] và daõy a[(((m+n) div 2)+1) :n] Trần Hoàng Thọ Khoa Toán - Tin Lop11.com (10) Kyõ thuaät laäp trình naâng cao - 10 - Do ño ù: - Thủ tục tìm max dãy a[1:n] ( thủ tục Tmax1) có thể thực theo luật đệ qui : + Tìm max daõy traùi a[m:((m+n) div 2)] (gọi đệ quy Tmax1(a[m:((m+n) div 2)] ) ) + Tìm max daõy phaûi a[(((m+n) div 2)+1) :n] (gọi đệ quy Tmax1(a[(((m+n) div 2)+1) :n] ) + Tìm max cuûa soá : Tmax1(a[m:((m+n) div 2)] ) vaø Tmax1(a[(((m+n) div 2)+1) :n] ) (giải thuật không đệ quy) Tức là :Tmax1(a[m:n]) = max(Tmax1(a[m:((m+n) div 2)] ) ,Tmax1(a[(((m+n) div 2)+1) :n]) ) với Tmax1(a[m:m] = a[m] ; ( trường hợp neo ) max(x,y) = x > y ? x : y ; - Thủ tục tính tổng các phần tử ( TSUM1 ) có thể thực theo luật đệ quy : + Tìm toång daõy traùi a[m:((m+n) div 2)] (gọi đệ quy TSUM1 (a[m:((m+n) div 2)] ) ) + Tìm toång daõy phaûi a[(((m+n) div 2)+1) :n] (gọi đệ quy TSUM1 (a[(((m+n) div 2)+1) :n] ) ) + Tìm toång cuûa soá : TSUM1 (a[m:((m+n) div 2)] ) vaø TSUM1 (a[(((m+n) div 2)+1) :n] ) Tức là : TSUM1 (a[m:n]) = TSUM1 (a[m:((m+n) div 2)]) + TSUM1 (a[(((m+n) div 2)+1) :n] ) với TSUM1 (a[m:m]) = a[m] Ví duï : Caây nhò phaân tìm kieám kieåu T(BST) laø moät caáu truùc goàm : moät nuùt kieåu T keát noái với cây nhi phân tìm kiếm kiểu T nên : - Thụ tục quét cây nhi nhân tìm kiếm theo thứ tự (LNF) là : + Quét cây trái theo thứ tự ; + Thaêm nuùt goác ; + Quét cây phải theo thứ tự ; - Thuû tuïc tìm kieám giaù tri αo treân caây nhò phaân tìm kieám Root laø : Nếu Root ≡ ∅ thì thực thao tác rỗng (không làm gì ) Con khoâng giá trị nút gốc = αo thì thông báo tìm thấy và dừng Coøn khoâng giá trị nút gốc < αo thì tìm cây trái Còn không thì tìm cây phải Nhaän xeùt : Trần Hoàng Thọ Khoa Toán - Tin Lop11.com (11) Kyõ thuaät laäp trình naâng cao - 11 - Trong thủ tục đệ qui, 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 nhóm biến ) , để đ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 thủ tục đệ qui là : 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 ) Mã hóa giải thuật đệ qui các ngôn ngữ lập trình a) Toång quan Không phải ngôn ngữ lập trình có có thể mã hóa giải thuật đệ quy, số ngôn ngữ lập trình có khả tổ chức vùng nhớ kiểu stack có khả mã hóa giải thuật đệ quy Các ngôn ngữ lập trình mã hóa giải thuật đệ quy cách tổ chức các chương trình đệ quy tương ứng b) Thể đệ qui NNLT PASCAL và C++ NN LT Pascal và C++ cho phép mã hóa giải thuật đệ quy cách tổ chức chương trình đê quy nhờ vào chế tạo vùng nhớ Stak phần mềm ngôn ngữ b1) Trong NNLT C++ NNLT C++ cho phép mã hóa giải thuật đệ quy cách thuận lợi nhờ vào kỹ thuật khai báo trước tiêu đề nên không có phân biệt hình thức nào việc khai báo hàm đệ quy và hàm không đệ quy b2) Trong NN LT Pascal Đối với chương trình đệ quy trực tiếp thì hình thức khai báo giống chương trình không đệ quy Đối với chương trình đệ quy gián tiếp thì hình thức khai báo có thay đổi ít nhiều nhằm thỏa quy tắc tầm vực ngôn ngữ ( phần lệnh chương trình gọi chương trình cùng cấp đã khai báo trước ) Ví duï : Với mô hình chương trình sau : Trong phần lệnh khối A có thể gọi đến : Trần Hoàng Thọ Khoa Toán - Tin Lop11.com (12) Kyõ thuaät laäp trình naâng cao Program - 12 - + Gọi các chương trình trực tiếp nó gọi B không gọi C + Gọi chính nó ( gọi đệ quy ) + Goïi chöông trình cuøng caáp nhömg phải khai báo trước gọi E không gọi D , Muốn gọi D phải khai báo trước ( khai báo FORWARD) E A B C D Khai báo trước FORWARD Để từ thủ tục hàm A có thể gọi đến D là thủ tục hàm cùng cấp mô tả sau A, ta cần có khai báo trước D phía trước A Khai báo này gồm : tiêu đề D, với danh sách thông số D, là từ khoá FORWARD Sau đó lúc mô tả lại D thì cần khai báo từ khoá PROCEDURE ( FUNCTION ) , tên D ( khoâng coù danh saùch thoâng soá ) , phaàn thaân cuûa D Ví dụ : Với thủ tục gọi đệ quy hỗ tương FIRST,SECOND khai báo nhö sau : procedure SECOND (i : integer ) ; Forward ; procedure FIRST (n : integer ; var X : real); var j, k : interger ; begin for j := to n begin writeln(‘ j = ‘, j ) ; k := n – 2* j ; SECOND( k ); end ; end ; procedure second ; begin if ( i > ) then begin writeln(‘ i= ‘, i ); FIRST( i – ) ; end ; end ; Trần Hoàng Thọ Khoa Toán - Tin Lop11.com (13) Kyõ thuaät laäp trình naâng cao - 13 - Một số dạng giải thuật đệ quy đơn giản thường gặp a) Đệ quy tuyến tính Chương trình đệ quy tuyến tính là chương trình đệ quy trực tiếp đơn giaûn nhaát coù daïng : P ≡ { NẾU thỏa điều kiện dừng thì thực S ; Còn không begin { thực S* ; gọi P } } Với S , S* là các thao tác không đệ quy Ví duï : Haøm FAC(n) tính soá haïng n cuûa daõy n! + Dạng hàm ngôn ngữ mã giả : { Nếu n = thì FAC = ; /* trường hợp neo */ Coøn khoâng FAC = n*FAC(n-1) } + Dạng hàm ngôn ngữ Pascal : Function FAC(n : integer) : integer; begin if( n = ) then FAC := else FAC := n*FAC(n-1) ; end; + Daïng haøm C++ : int FAC( int n ) { if ( n == ) return ; else return ( n * FAC(n-1 )) ; } Ví duï : Chương trình tính USCLN số dựa vào thuật toán Euclide : + Dạng hàm trên ngôn ngữ toán học : USCLN(m , n ) = USCLN(n , m mod n ) n ≠ USCLN(m , 0) = m + Dạng hàm ngôn ngữ mã giả : Neáu n = thì USCLN = m Coøn khoâng USCLN = USCLN( n , m mod n ) ; + Daïng haøm Pascal : Function USCLN(m , n : integer ) : integer ; begin if (n = ) then USCLN := m else USCLN := USCLN( n , m mod n ) ; end ; +Daïng haøm C++ : int USCLN( int m , int n ) Trần Hoàng Thọ Khoa Toán - Tin Lop11.com (14) Kyõ thuaät laäp trình naâng cao - 14 - { if(n == ) return (m) ; else return ( USCLN( n , m mod n)) ; } b) Đệ quy nhị phân Chương trình đệ quy nhị phân là chương trình đệ quy trực tiếp có dạng : P ≡ { NẾU thỏa điều kiện dừng thì thực S ; Coøn khoâng begin { thực S* ; gọi P ; gọi P } } Với S , S* là các thao tác không đệ quy Ví duï : Haøm FIBO(n) tính soá haïng n cuûa daõy FIBONACCI + Daïng haøm Pascal: Function F(n : integer) : integer; begin if( n < ) then F := else F := F(n-1) + F(n-2) end; + Daïng haøm C++ : int F(int n) { if ( n < ) return ; else return (F(n -1) + F(n -2)) ; } c) Đệ quy phi tuyến Chương trình đệ quy phi tuyến là chương trình đệ quy trực tiếp mà lời gọi đệ quy thực bên vòng lặp Dạng tổng quát chương trình đệ quy phi tuyến là : P ≡ { for giá tri đầu to giá trị cuối begin thực S ; if ( thỏa điều kiện dừng ) then thực S* else goïi P end ; } Với S , S* là các thao tác không đệ quy Ví duï : Cho dãy { Xn } xác định theo công thức truy hồi : X0 = ; Xn = n2 XO +(n-1)2 X1 + + 2 Xn-2 + Xn-1 + Dạng hàm đệ quy tính Xn trên ngôn ngữ mã giả là : Xn ≡ if ( n= ) then return ; Trần Hoàng Thọ Khoa Toán - Tin Lop11.com (15) Kyõ thuaät laäp trình naâng cao - 15 - else { tg = ; for i = to n-1 return tg ; } tg = tg + (n-i)2 Xi ; + Dạng hàm đệ quy tính Xn trên ngôn ngữ Pascal là : function X( n :integer) : integer ; var i , tg : integer ; begin if ( n= ) then X := else begin tg = ; for i: = to n-1 tg : = tg + sqr(n-i) *X(i) ; X := tg ; end ; end ; + Dạng hàm đệ quy tính Xn trên ngôn ngữ C++ là : int X( int n ) ; { if ( n == ) return ; else { int tg = ; for (int i = ; i<n ; i++ ) tg = tg + sqr(n-i) *X(i); return ( tg ) ; } Trần Hoàng Thọ Khoa Toán - Tin Lop11.com (16) Kyõ thuaät laäp trình naâng cao - 16 - CHÖÔNG II BAØI TOÁN ĐỆ QUY I CÁC NỘI DUNG CẦN LAØM ĐỂ TÌM GIẢI THUẬT ĐỆ QUY CHO MỘT BAØI TOÁN Để xây dựng giải thuật giải bài toán có tính đệ quy phương pháp đệ quy ta cần thực 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 trường hợp tổng quát 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 quát (một họ các bài toán chứa bài toán cần giải ),tìm các thông số cho bài toán tổng quát đặc biệt là nhóm các thông số biểu thị kích thước bài toán – các thông số điều khiển ( các thông số mà độ lớn chúng đặc trưng cho độ phức tạp bài toán , và giảm qua lần gọi đệ qui ) Ví duï : n haøm FAC(n) ; a , b haøm USCLN(a,b) Phát 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 bài toán tổng quát , là các trương hợp tương ứng với các gía trị biên 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 đơn giản) Ví duï : FAC(1) =1 , USCLN(a,0) = a , SM(a[x:x] ≡∅ ,TSUM(a[m:m]) = a[m] 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 trường hợp tổng quát cách phân chia nó thành các thành phần mà có giải thuật không đệ quy là bài toán trên có kích thước nhỏ Ví duï : FAC(n) = n * FAC(n -1) Tmax(a[1:n]) = max(Tmax(a[1:(n-1)]) , a[n] ) Trần Hoàng Thọ Khoa Toán - Tin Lop11.com (17) Kyõ thuaät laäp trình naâng cao - 17 - II MỘT SỐ BAØI TOÁN GIẢI BẰNG GIẢI THUẬT ĐỆ QUY ĐIỂN HÌNH Bài toán tháp Hà Nội Truyền thuyết kể : Một nhà toán học Pháp sang thăm Đông Dương đến ngôi chùa cổ Hà Nội thấy các vị sư chuyển chồng đĩa qúy gồm 64 đĩa với kích thước khác từ cột A sang cột C theo cách : - Moãi laàn chæ chuyeån ñóa - Khi chuyeån coù theå duøng coät trung gian B - Trong suốt qúa trình chuyển các chồng đĩa các cột luôn xếp đúng (đĩa có kích thước bé đặt trên đĩa có kích thước lớn ) Khi hỏi các vị sư cho biết chuyển xong chồng đĩa thì đến ngày tận ! Như sau này với chồng gồm n đĩa cần n - lần chuyển (chuyển ñóa ) Giả sử thời gian để chuyển đỉa là t giây thì thời gian để chuyển xong chồng 64 đĩa seõ laø : * 1019 * t S T = ( 64 − ) * t S = 18 Với t = 1/100 s thì T = 5.8*109 năm = 5.8 tỷ năm Ta có thể tìm thấy giải thuật (dãy các thao tác ) cho bài toán cách dễ dàng ứng với trường hợp chồng đĩa gồm 0, 1, 2, đĩa Với chồng đĩa giải thuật bài toán đã trở nên phức tạp Tuy nhiên giải thuật bài toán lại tìm thấy dễ dàng nhanh chóng ta khái quát số đĩa là n và nhìn bài toán quan niệm đệ quy a) Thông số hóa bài toán Xét bài toán mức tổng quát : chuyển n (n>=0) đĩa từ cột X sang cột Z laáy coät Y laøm trung gian Ta gọi giải thuật giải bài toán mức tổng quát là thủ tục THN(n ,X ,Y,Z) chứa thông số n,X,Y,Z ; n thuộc tập số tự nhiên N (kiểu nguyên không dấu ); X ,Y,Z thuộc tập các ký tự (kiểu ký tự ) Bài toán cổ trên sẻ thực lời gọi THN(64,A,B,C) Dễ thấy : thông số bài toán thì thông số n là thông số định độ phức tạp bài toán ( n càng lớn thì số thao tác chuyển đỉa càng nhiều và thứ tự thực hieän chuùng caøng khoù hình dung ) , n laø thoâng soá ñieàu khieån b) Trường hợp suy biến và cách giải Với n =1 bài toán tổng quát suy biến thành bài toán đơn giản THN (1,X,Y,Z) : tìm dãy thao tác để chuyển chồng đĩa từ cột X sang cột Z lấy cột Y làm trung gian Giải thuật giải bài toán THN (1,X,Y,Z) là thực thao tác : Chuyển đĩa từ X sang Z ( kyù hieäu laø Move (X , Z) ) Trần Hoàng Thọ Khoa Toán - Tin Lop11.com (18) Kyõ thuaät laäp trình naâng cao - 18 - THN(1,X,Y,Z) ≡ { Move( X, Z ) } Chú ý : Hoàn toàn tương tự ta có thể quan niện trường hợp suy biến là trường hợp n= tương ứng với bài toán THN(0,X,Y,Z) : chuyển đĩa từ X sang Z lấy Y làm trung gian mà giải thuật tương ứng là không làm gì ( thực thao tác rỗng ) THN(0,X,Y,Z) ≡ { φ } c) Phân rã bài toán : Ta có thể phần rã bài toán TH N (k,X,Y,Z) : chuyển k đĩa từ cột X sang cột Z lấy cột Y làm trung gian thành dãy công việc sau : + Chuyển (k -1) đĩa từ cột X sang cột Y lấy cột Z làm trung gian : THN (k -1,X,Z,Y) (bài toán THN với n = k-1,X= X , Y = Z , Z = Y ) + Chuyển đĩa từ cột X sang cột Z : Move ( X, Z ) (thao tác ) + Chuyển (k - ) đĩa từ cột Y sang cột Z lấy cột X làm trung gian : THN( k -1,Y,X,Z) ( bài toán THN với n = k-1 , X = Y , Y = X , Z = Z ) Vậy giải thuật trường hợp tổng quát (n > 1) là : THN(n,X,Y,Z) { THN (n -1,X,Z,Y) ; Move ( X, Z ) ; THN (n -1,Y,X,Z) ; } Với n đĩa thì cần bao nhiêu bước chuyển đĩa? Thực chất thủ tục THN các lệnh gọi đệ qui nhằm sếp trình tự các thao tác chuyển đĩa Số lần chuyển đĩa thực là đặc trưng cho độ phức tạp giải thuật Với n đĩa , gọi f(n) là số các thao tác chuyển _một_đĩa Ta coù : f(0) = f(1) =1 f(n) = 2f(n -1) + với n > Do ño ù : f(n) = 1+ + 2 + + n-1 = n - Để chuyển 64 đĩa cần 64 - bước hay xấp xỉ 10 20 bước Cần khoảng 10 triệu năm với máy tính nhanh để làm việc này ≡ d) Chöông trình maõ hoùa giaûi thuaät THN NNLT Pascal : procedure THN (n : integer ; X,Y,Z : char) begin if n > then begin THN (n-1 ,X,Z,Y) ; Move( X, Z); THN (n-1 ,Y,X,Z); end ; end ; ( Lấy trường hợp chuyển n = làm trường hợp neo ) Trần Hoàng Thọ Khoa Toán - Tin Lop11.com (19) Kyõ thuaät laäp trình naâng cao - 19 - Hoặc : procedure THN (n : integer ; X,Y,Z : char) begin if (n = 1) then Move(X, Z) else begin THN (n-1 ,X,Z,Y ) ; Move(X, Z ); THN (n -1 ,Y,X,Z ); end ; end; ( Lấy trường hợp chuyển n = làm trường hợp neo ) Với thủ tục Move(X, Y) mô tả thao tác chuyển đĩa từ cột X sang cột Y viết tuyø theo caùch theå hieän thao taùc chuyeån e) Chöông trình maõ hoùa giaûi thuaät THN NNLT C++ : Trong C++ hàm thực giải thuật THN có dạng : void THN( int n , char X,Y,Z) { if(n > 0) { THN(n -1,X,Z,Y ) ; Move ( X , Z ) ; THN(n - 1,Y,X,Z ) ; } return ; } : void THN( int n , char X,Y,Z) { if(n = = 1) Move ( X , Z ) ; else { THN(n -1,X,Z,Y ) ; Move ( X, Z ) ; THN(n - 1,Y,X,Z ) ; } return ; } Bài toán chia thưởng Có 100 phần thưởng đem chia cho 12 học sinh giỏi đã xếp hạng Có bao nhiêu cách khác để thực cách chia? Ta thấy việc tìm lời giải cho bài toàn sẻ không dễ dàng ta không tìm cách thích hợp để tiếp cận với nó Ta tìm giải thuật giải bài toàn phương pháp đệ quy Trần Hoàng Thọ Khoa Toán - Tin Lop11.com (20) Kyõ thuaät laäp trình naâng cao - 20 - a) Thoâng soá hoùa Ta giải bài toán mức độ tổng quát : Tìm số cách chia m vật (phần thưởng ) cho n đối tượng (học sinh ) có thứ tự Gọi PART là số cách chia đó PART là hàm biến nguyên m , n ( PART(m ,n )) Ta mã hoá n đối tượng theo thứ tự xếp hạng 1, , , n ; Si là số phần thưởng mà học sinh i nhận Khi đó các điều kiện ràng buộc lên cách chia là : Si >= S1 >= S2 >= >= Sn S1 + S2 + + Sn = m Ví duï : Với m = , n = ta có cách chia sau : 0 3 1 2 Tức là PART(5,3 ) = b) Các trường hợp suy biến : + m = thì sẻ có cách chia : học sinh nhận phần thưởng Vậy : PART(0 , n ) = với n + n = , m <> thì không có cách nào để thực việc chia Vậy : PART(m , ) = với m <> ( ta có thể thay trường hợp neo PART(m ,0) = trường hợp neo PART(m , 1) =1) c ) Phân rã bài toán trường hợp tổng quát : + m < n soá phaàn thöông m nhoû hôn soá hoïc sinh n thì n - m hoïc sinh xeáp cuối luôn không nhận gì cách chia Vaäy : n > m thì PART(m , n ) = PART(m , m ) + Trong trường hợp m >= n : số vật chia (phần thưởng ) lớn số học sinh (đối tượng ) ta phân các cách chia làm nhóm : * Nhóm thứ không dành cho học sinh xếp cuối cùng phần thưởng nào caû ( Sn = ) Soá caùch chia naøy seõ baèng soá caùch chia m phaàn thöông cho n -1 hoïc sinh Tức là : Số cách chia nhóm thứ = PART(m , n -1 ) Trần Hoàng Thọ Khoa Toán - Tin Lop11.com (21)