Sáng tạo trong thuật toán và lập trình với ngôn ngữ Pascal và C# - Tập 1 - Chương 8: Suy ngẫm
Chương này giới thiệu một số bài toán thuộc các lớp thuật giải khác nhau để bạn
đọc tự luyện tập.
Thông thường, nếu chỉ biết một phương pháp giải mà gặp bài toán "trúng tủ", nghĩa là
bài toán vận dụng chính phương pháp đã biết thì ta gần như không phải suy nghĩ gì. Tuy
nhiên, khi đã có trong tay một số phương pháp thì việc chọn thuật giải cho mỗi bài toán cụ
thể sẽ không dễ dàng chút nào.
Luyện tập và suy ngẫm để tìm kiếm đường đi trong các tình huống như trên sẽ
cung cấp cho chúng ta nhiều kinh nghiệm quý.
= (a mod 3)+1); 'n': ke := (a = (b mod 3)+1); 't': Ke := (abs(a-b) = 1); 'h': Ke := True; end {case}; end; Tương tự, khi hai cọc a và b không kề nhau ta cũng thực hiện các thao tác như nhau. Biết hàm Ke, ta dựa vào các thuật toán riêng cho mỗi trường hợp để viết phương án cho bài toán Hà Nội sắc màu như sau: (*---------------------------------------- Ha Noi sac mau x: xanh - xuoi chieu kim dong ho n: nau - nguoc chieu kim dong ho t: trang - chuyen thang h: hong - chuyen tu do ---------------------------------------*) procedure Hnm(n: byte; a,b: byte); begin if n = 0 then exit; if Ke(n,a,b) then begin Hnm(n-1,a,6-a-b); inc(d); writeln(f,d,'. ',a,' -> ',b); Hnm(n-1,6-a-b,b); end else begin Sáng tạo trong Thuật toán và Lập trình Tập I 277 Hnm(n-1,a,b); inc(d); writeln(f,d,'. ',a,' -> ',6-a-b); Hnm(n-1,b,a); inc(d); writeln(f,d,'. ',6-a-b,' -> ',b); Hnm(n-1,a,b); end; end; (* Pascal *) (************************************** HNM.PAS – Thỏp Hà Nội màu x – xanh: Xuụi chiều kim đồng hồ n – nõu: Ngược chiều kim đồng hồ t – Trắng: thẳng theo hàng ngang h – Hà Nội cổ: kinh điển ****************************************) uses crt; var d: longint; {dem so buoc chuyen} f: text; {output file} s: string; {cau hinh thap} {---------------------------------------- Kiem tra tinh chat ke giua 2 coc a va b -----------------------------------------} function Ke(n,a,b: byte): Boolean; begin case s[n] of 'x': ke := (b = (a mod 3)+1); 'n': ke := (a = (b mod 3)+1); 't': Ke := (abs(a-b) = 1); 'h': Ke := True; end {case}; end; (*---------------------------------------- Ha Noi sac mau x: xanh - xuoi chieu kim dong ho n: nau - nguoc chieu kim dong ho t: trang - chuyen thang h: hong - chuyen tu do ---------------------------------------*) procedure Hnm(n: byte; a,b: byte); tự viết {------------------------------- To chuc goi thu tuc Hnm ------------------------------} procedure runHnm(Thap:string); begin s := Thap; d := 0; assign(f,'hnm.out'); rewrite(f); writeln('-----------------'); Sáng tạo trong Thuật toán và Lập trình Tập I 278 Hnm(length(s),1,2); writeln(f,'Total: ',d,' step(s)'); close(f); readln; end; BEGIN runHnm('txhxn'); END. // C# using System; namespace SangTao1 { /*------------------------------------ * Thap Ha Noi * -----------------------------------*/ class ThapHaNoi { static int d = 0; static char[] s = new char[64]; static void Main() { Console.WriteLine("\n Ha Noi Sac Mau"); RunHaNoiSacMau("xnxht", 1, 2); Console.WriteLine("\n Total: " + d + " steps"); Console.ReadLine(); } // Main static bool Ke(char KieuThap, int a, int b) { switch (KieuThap) { case 'x': return (b == (a % 3) + 1); case 'n': return (a == (b % 3) + 1); case 't': return (Math.Abs(a - b) == 1); } return true; } /*------------------------------------- Ha Noi sac mau x: xanh - xuoi chieu kim dong ho n: nau - nguoc chieu kim dong ho t: trang - chuyen thang h: hong - chuyen tu do ---------------------------------------*/ void HaNoiSacMau(int n, int a, int b) { if (n == 0) return; if (Ke(s[n], a, b)) { HaNoiSacMau(n - 1, a, 6 - a - b); Sáng tạo trong Thuật toán và Lập trình Tập I 279 Console.WriteLine((++d)+ ". ("+s[n]+") "+a+" -> "+b); HaNoiSacMau(n - 1, 6 - a - b, b); } else { HaNoiSacMau(n - 1, a, b); Console.WriteLine((++d)+ ". ("+s[n]+") " +a+" -> "+(6-a-b)); HaNoiSacMau(n - 1, b, a); Console.WriteLine((++d)+ ". ("+s[n]+")" +(6-a-b) +" -> "+b); HaNoiSacMau(n - 1, a, b); } } static void RunHaNoiSacMau(string w, int a, int b) { d = 0; w.CopyTo(0, s, 1, w.Length); HaNoiSacMau(w.Length, a, b); } } // ThapHaNoi } // SangTao1 Hƣớng dẫn kiểm thử Ta sẽ dùng kĩ thuật đối sánh để kiểm thử các trường hợp. Ta chọn và cố định các giá trị n, a và b. Chẳng hạn, n = 4, a = 1, b = 2. Khi đó ta có: RunHn(n) và RunHnm('hhhh') cho cùng kết quả. RunHnx(n) và RunHnm('xxxx') cho cùng kết quả. RunHnn(n) và RunHnm('nnnn') cho cùng kết quả. RunHnt(n) và RunHnm('tttt') cho cùng kết quả. Nếu ghi các kết quả này vào các tệp tương ứng, sau đó gọi thủ tục đối sánh từng cặp hai tệp tương ứng thì có thể kiểm định được một số trường hợp. Để xây dựng các tình huống kiểm thử khác nhau còn lại bạn để ý đến các tính chất thuận nghịch của các thủ tục khác nhau. Thí dụ, như đã phân tích ở phần trên, các lời gọi Hnx(3,1,2) và Hnn(3,3,1) phát sinh cùng một số lần chuyển. Bạn hãy thử phát hiện thêm sự tương đồng giữa các lời gọi Hnm với các tham trị khác nhau. Sáng tạo trong Thuật toán và Lập trình Tập I 280 Đọc thêm Lƣơc sử Một số bài toán về tháp Hà Nội đã được đưa vào các kì thi Olympic Tin học tại một số quốc gia. Chúng ta thử tìm hiểu cội nguồn của các bài toán thuộc loại này. Năm 1883, trên một tờ báo ở Paris có đăng bài mô tả một trò chơi toán học của giáo sư Claus với tên là Tháp Hà Nội. Nội dung trò chơi được mọi người say mê làm thử chính là bài toán Tháp Hà Nội cổ. Thời đó ở thủ đô Paris dân chúng đổ xô nhau mua đồ chơi này và suốt ngày ngồi chuyển tháp. Trong lịch sử về các trò chơi thông minh đã từng có những cơn sốt như vậy. Tính trung bình mỗi thế kỉ có một vài cơn sốt trò chơi. Thế kỉ thứ XX có cơn sốt Rubic, thế kỉ XIX là các trò chơi 15 và tháp Hà Nội. Bài toán này nổi tiếng đến mức trở thành kinh điển trong các giáo trình về thuật giải đệ quy và được trình bày trong các thông báo chính thức của các phiên bản chuẩn của các ngữ trình như ALGOL-60, ALGOL-68, Pascal, Delphy, C, C++, Ada,... khi muốn nhấn mạnh về khả năng đệ quy của các ngôn ngữ đó. Theo nhà nghiên cứu Henri De Parville công bố vào năm 1884 thì tác giả của trò chơi tháp Hà Nội có tên thật là nhà toán học Eduard Lucas, người có nhiều đóng góp trong lĩnh vực số luận. Mỗi khi viết về đề tài giải trí thì ông đổi tên là Claus. Bạn có để ý rằng Claus là một hoán vị các chữ cái của từ Lucas. De Parville còn kể rằng bài toán tháp Hà Nội bắt nguồn từ một tích truyền kì ở Ấn Độ. Một nhóm cao tăng Ấn Độ giáo được giao trọng trách chuyển dần 64 đĩa vàng giữa ba cọc kim cương theo các điều kiện đã nói ở bài toán Tháp Hà Nội cổ. Khi nào hoàn tất công việc, tức là khi chuyển xong toà tháp vàng 64 tầng từ vị trí ban đầu sang vị trí kết thúc thì cũng là thời điểm tận thế. Sự việc này có xảy ra hay không ta sẽ xét ở bài tập 8.10. Lời giải được công bố đầu tiên cho bài toán tháp Hà Nội là của Allardice và Frase, năm 1884. Năm 1994 David G. Poole ở Đại học Trent, Canada đã viết một bài khảo cứu cho tờ Mathematics Magazine số tháng 12 nhan đề "Về các tháp và các tam giác của giáo sư Claus" cùng với một phụ đề "Pascal biết Hà Nội". Poole đã liệt kê 65 công trình khảo cứu bài toán tháp Hà Nội đăng trên các tạp chí toán-tin trong khoảng mười năm. Tác giả cũng chỉ ra sự liên quan giữa các công thức tính số lần chuyển các tầng tháp và một phương pháp quen biết dùng để tính các hệ số của dạng khai triển nhị thức Newton (a + b) n . Phương pháp này được gọi là Tam giác Pascal, mang tên nhà toán học kiêm vật lí học Pháp Blaise Pascal (1623-1662), người đã chế tạo chiếc máy tính quay tay đầu tiên trên thế giới. Một số nhà nghiên cứu trong và ngoài nước có bàn luận về địa danh Hà Nội. Theo tôi vấn đề này vẫn còn ngỏ. Hầu hết các bài viết xoay quanh đề tài chuyển tháp nói trên đều dùng thuật ngữ bài toán tháp Hà Nội. Khi giới thiệu về bài toán Hà Nội nhiều tháp Dudeney đặt tên là bài toán đố của Reve (The Reve's Puzzle). Tuy nhiên, nhiều nhà nghiên cứu cho rằng tốt hơn cả là nên đặt tên và phân loại theo tên nguyên thuỷ của bài toán, nghĩa là Tháp Hà Nội. Ngoài các dạng Tháp Hà Nội đã liệt kê ở phần trên một số tác giả còn đề xuất những dạng khá kì lạ, chẳng hạn như bài toán sau đây. Sáng tạo trong Thuật toán và Lập trình Tập I 281 Hà Nội nhiều tháp Trong trò chơi này người ta làm thêm những cọc, chẳng hạn thay vì ba ta dùng bốn cọc và cũng có thể bố trí tháp tại nhiều cọc. Ý kiến này do H.E. Dudeney, một tác giả hàng đầu về toán học giải trí người Anh đưa ra vào năm 1908. Đã có nhiều bài đăng lời giải cho bài toán này, có những bài mới xuất hiện gần đây vào những năm 1988 và 1989. Dù vậy chưa ai chứng minh được rõ ràng số lần chuyển của bài giải là tối thiểu như đã làm với các dạng tháp Hà Nội khác. Bài tập Bạn hãy thử lập công thức tính số lần chuyển các tầng tối thiểu cho các bài toán sau: Tháp Hà Nội, Tháp Hà Nội Xuôi, Tháp Hà Nội Ngược và Tháp Hà Nội Thẳng. Lời cảm ơn Các tư liệu trên và một số tư liệu khác trong bài được trích dẫn từ các bài viết của giáo sư viện sĩ Nguyễn Xuân Vinh, Khoa Kỹ thuật không gian, Đại học Michigan, cộng tác viên NASA, Hoa Kỳ. Tác giả xin chân thành cám ơn giáo sư đã cho phép trích dẫn và chỉ giáo về các phương pháp truyền thụ tri thức khoa học cho giới trẻ. NXH 8/4/2008 Sửa ngày 4/4/09 Sáng tạo trong Thuật toán và Lập trình Tập I 282 Nguyễn Xu©n Huy s¸ng t¹o trong thuËt to¸n vµ lËp tr×nh víi C#, Pascal tuyÓn c¸c bµi to¸n tin n©ng cao cho häc sinh vµ sinh viªn giái TËp mét Lêi giíi thiÖu S¸ch tr×nh bµy cã hÖ thèng c¸c ph-¬ng ph¸p thiÕt kÕ thuËt to¸n minh häa qua c¸c bµi to¸n thi häc sinh giái vµ Olimpic häc sinh vµ sinh viªn trong n-íc, khu vùc vµ quèc tÕ. C¸c bµi to¸n ®Òu ®-îc ph©n tÝch ®Çy ®ñ kÌm theo thuËt to¸n vµ toµn v¨n ch-¬ng tr×nh viÕt trªn ng«n ng÷ C# vµ Pascal. S¸ch lµ tµi liÖu bæ Ých cho häc sinh trung häc, gi¸o viªn c¸c tr-êng phæ th«ng vµ cao ®¼ng vµ sinh viªn c¸c tr-êng ®¹i häc muèn hoµn thiÖn kiÕn thøc ®Ó tham dù c¸c kú thi Olimpic Tin häc quèc gia vµ quèc tÕ. C¸c ch-¬ng tr×nh song ng÷ Pascal vµ C# gióp cho b¹n ®äc chuyÓn ®æi nhanh chãng sang c¸c m«i tr-êng lËp tr×nh tiªn tiÕn.
File đính kèm:
- Sáng tạo trong thuật toán và lập trình với ngôn ngữ Pascal và C# - Tập 1 - Chương 8_Suy ngẫm.pdf