Cây 2-3-4 – Lý thuyết và mô phỏng

I. Lý do chọn đề tài Trong hai thập kỉ qua, mô phỏng thuật toán đã được các nhà sư phạm của ngành công nghệ thông tin sử dụng như một công cụ hỗ trợ cho việc giảng dạy các thuật toán trên máy tính. Nguyên nhân của việc mô phỏng thuật toán được sử dụng như một công cụ trợ giúp cho việc giảng dạy là do nó có thể cung cấp các mô phỏng động bằng đồ họa của một thuật toán và các thay đổi trong cấu trúc dữ liệu của nó trong suốt quá trình thực thi. Như một phần của quá trình học thuật toán, việc mô phỏng các thuật toán còn góp phần giúp các em học sinh, sinh viên khi mới bắt đầu làm quen với giải thuật có thể vừa dễ dàng theo dõi các bước duyệt ở lý thuyết vừa nhìn thấy các bước chạy ở thực tế như thế nào. Tư đó có thể giúp các em tư duy thuật toán nhanh hơn và ngày càng yêu thích giải thuật. Mô phỏng thuật toán ngày càng trở nên hữu ích và trở thành một giáo cụ trực quan rất quan trọng trong hầu hết các lĩnh vực, nhất là trong môi trường giáo dục. Với các nhà sư phạm của ngành công nghệ thông tin thì mô phỏng thuật toán có tác dụng như một tài liệu hướng dẫn trong việc dạy các thuật toán bằng máy tính. Cây 2-3-4 là một cây nhị phân tìm kiếm giải quyết tốt hơn các trường hợp xấu nhất cho cây nhị phân tìm kiếm bình thường. Và đây còn là một nội dung khá mới mẻ và phức tạp đối với nhiều học sinh, sinh viên. Vì vậy vấn đề “Cây 2-3-4 – Lý thuyết và mô phỏng” được chọn làm đề tài nghiên cứu.

doc36 trang | Chia sẻ: lvcdongnoi | Lượt xem: 2667 | Lượt tải: 0download
Bạn đang xem trước 20 trang tài liệu Cây 2-3-4 – Lý thuyết và mô phỏng, để xem tài liệu hoàn chỉnh bạn click vào nút DOWNLOAD ở trên
MỤC LỤC----------------------------------------------------------------------------------1 LỜI MỞ ĐẦU LÝ DO CHỌN ĐỀ TÀI---------------------------------------------------2 MỤC ĐÍCH NGHIÊN CỨU ĐỀ TÀI-----------------------------------2 NHIỆM VỤ NGHIÊN CỨU ĐỀ TÀI-----------------------------------3 ĐỐI TƯỢNG NGHIÊN CỨU--------------------------------------------3 PHƯƠNG PHÁP NGHIÊN CỨU----------------------------------------3 PHẦN NỘI DUNG-------------------------------------------------------------------------4 CHƯƠNG 1. LÝ THUYẾT CÂY 2-3-4---------------------------------------------4 Giới thiệu về cây 2-3-4.-----------------------------------------------------4 II. Tổ chức cây 2-3-4.----------------------------------------------------------6 III. Tìm kiếm.---------------------------------------------------------------------8 Tách node.-------------------------------------------------------------------8 1. Tách node con.------------------------------------------------------------8 2. Tách node gốc.----------------------------------------------------------11 3. Tách theo hướng đi xuống.--------------------------------------------12 Chèn node.------------------------------------------------------------------14 Tính hiệu quả của Cây 2-3-4---------------------------------------------15 Chuyển từ cây 2-3-4 sang cây đỏ đen.----------------------------------16 CHƯƠNG 2. MÔ PHỎNG THUẬT TOÁN TRÊN CÂY 2-3-4--------------21 Tổng quan về mô phỏng thuật toán.-------------------------------------21 Khái niệm thuật toán và các đặc trưng của thuật toán.----------21 Khái niệm mô phỏng thuật toán.-------------------------------------21 Các yêu cầu mô phỏng thuật toán.--------------------------------------22 Quá trình thiết kế nhiệm vụ mô phỏng thuật toán.--------------------23 Mô phỏng thuật toán trên Cây 2-3-4------------------------------------23 Giới thiệu ngôn ngữ mô phỏng.--------------------------------------23 Phân tích và thiết kế thuật toán mô phỏng.------------------------24 Phân tích.-----------------------------------------------------------24 Thiết kế.-------------------------------------------------------------24 TÀI LIỆU THAM KHẢO---------------------------------------------------------------36 LỜI MỞ ĐẦU I. Lý do chọn đề tài Trong hai thập kỉ qua, mô phỏng thuật toán đã được các nhà sư phạm của ngành công nghệ thông tin sử dụng như một công cụ hỗ trợ cho việc giảng dạy các thuật toán trên máy tính. Nguyên nhân của việc mô phỏng thuật toán được sử dụng như một công cụ trợ giúp cho việc giảng dạy là do nó có thể cung cấp các mô phỏng động bằng đồ họa của một thuật toán và các thay đổi trong cấu trúc dữ liệu của nó trong suốt quá trình thực thi. Như một phần của quá trình học thuật toán, việc mô phỏng các thuật toán còn góp phần giúp các em học sinh, sinh viên khi mới bắt đầu làm quen với giải thuật có thể vừa dễ dàng theo dõi các bước duyệt ở lý thuyết vừa nhìn thấy các bước chạy ở thực tế như thế nào. Tư đó có thể giúp các em tư duy thuật toán nhanh hơn và ngày càng yêu thích giải thuật. Mô phỏng thuật toán ngày càng trở nên hữu ích và trở thành một giáo cụ trực quan rất quan trọng trong hầu hết các lĩnh vực, nhất là trong môi trường giáo dục. Với các nhà sư phạm của ngành công nghệ thông tin thì mô phỏng thuật toán có tác dụng như một tài liệu hướng dẫn trong việc dạy các thuật toán bằng máy tính. Cây 2-3-4 là một cây nhị phân tìm kiếm giải quyết tốt hơn các trường hợp xấu nhất cho cây nhị phân tìm kiếm bình thường. Và đây còn là một nội dung khá mới mẻ và phức tạp đối với nhiều học sinh, sinh viên. Vì vậy vấn đề “Cây 2-3-4 – Lý thuyết và mô phỏng” được chọn làm đề tài nghiên cứu. II. Mục đích nghiên cứu đề tài Mục đích nghiên cứu của khóa luận này nhằm tìm hiểu và đánh giá các thuật toán trên Cây 2-3-4, đồng thời xây dựng một phần mềm mô phỏng các thuật toán này nhằm hỗ trợ cho việc học, nghiên cứu và tiến tới dạy các thuật toán trên Cây 2-3-4. III .Nhiệm vu nghiên cứu đề tài. Nghiên cứu tổng quan về mô phỏng thuật toán, các yêu cầu, phương pháp tiếp cận, phương pháp thiết kế một mô đun mô phỏng thuật toán. Thiết kế minh họa các mô đun minh họa các thuật toán trên Cây2-3-4. IV. Đối tượng nghiên cứu. Đề tài nghiên cứu đi sâu vào nghiên cứu và cài đặt một số thuật toán: - Thuật toán tìm kiếm trên Cây 2-3-4 - Thuật toán chèn một node và chèn một giá trị vào Cây 2-3-4 - Thuật toán tách node trên Cây 2-3-4 - Thuật toán xóa node và xóa một giá trị trên Cây 2-3-4 V. Phưong pháp nghiên cứu. Phương pháp nghiên cứu chủ yếu tham khảo các tài liệu tham khảo liên quan đến Cây nhị phân tìm kiếm, Cây 2-3-4 thông qua các sách, tài liệu tham khảo và đặc biệt là nguồn tài liệu phong phú trên mạng Internet. PHẦN NỘI DUNG Chương I. Lý thuyết về Cây 2-3-4 I. Giới thiệu về cây 2-3-4. Như chúng ta đã biết, các thuật toán về cây nhị phân luôn rất tốt cho nhiều ứng dụng, tuy nhiên chúng lại có những khuyết điểm trong trường hợp xấu nhất. Chẳng hạn như trường hợp Quicksort, trường hợp xấu nhất của nó lại là trường hợp dễ xuất hiện trong thực tế nếu người dùng không chú ý đến nó. Các tập tin đã được xắp xếp thứ tự, các tập tin với thứ tự ngược, các tập các khoá lớn, nhỏ xen lẫn nhau hay các tập tin với sự phân đoạn lớn có cấu trúc đơn giản có thể làm thuật toán tìm trên cây hoạt động rất tồi. Với thuật toán QuickSort, cái mà chúng ta cần để cái tiến tình huống là sắp xếp lại để có trường hợp ngẫu nhiên: bằng cách chọn một phần tử phân hoạch ngẫu nhiên, chúng ta có thể dựa vào quy luật xác xuất để tránh khỏi trường hợp xấu nhất. Với tìm kiếm trên cây nhị phân thì may mắn hơn, bởi vì chúng ta có thể làm tốt hơn nhiều; có một kỹ thuật tổng quát cho phép chúng ta bảo đảm trường hợp xấu nhất sẽ không xuất hiện. Kỹ thuật này gọi là Cân bằng đã được dùng làm cơ sở cho nhiều thuật toán khác nhau về “cây cân bằng”. Chúng ta sẽ xem xét kỹ một thuật toán thuộc loại đó và cùng nhau thảo luận tóm tắt về sự liên quan của nó đối với các phương pháp khác. Để khử trường hợp xấu nhất của cây tìm kiếm nhị phân, chúng ta cần dùng một vài linh động trong cấu trúc sẽ dùng. Để có sự linh động này, chúng ta giả sử rằng các node trong cây của chúng ta có chứa nhiều hơn một khóa. Cụ thể hơn, chúng ta sẽ thừa nhận các 3-node và 4-node mà có thể chứa tương ứng hai và ba khóa. Một 3-node có ba liên kết ra khỏi nó, một liên kết cho tất cả các mẩu tin có khóa nhỏ hơn cả hai khóa của nó, một cho tất cả các mẩu tin có khóa nằm giữa hai khóa của nó, một cho tất cả các mẩu tin có khóa lớn hơn hai khóa của nó. Tương tự với một 4-node có 4 liên kết đi ra khỏi nó. Chúng ta sẽ xem xét các đặc tính của cây 2-3-4 và mối quan hệ khá gần gũi giữa cây 2-3-4 và cây đỏ-đen. Hình 4.1 Trình bày một cây 2-3-4 đơn giản. Mỗi node có thể lưu trữ 1, 2 hoặc 3 mục dữ liệu. Hình 4.1 cây 2-3-4 Các số 2, 3 và 4 trong cụm từ cây 2-3-4 có ý nghĩa là khả năng có bao nhiêu liên kết đến các node con có thể có được trong một node cho trước. Đối với các node không phải là lá, có thể có 3 cách sắp xếp sau: Một node với một mục dữ liệu thì luôn luôn có 2 con. Một node với hai mục dữ liệu thì luôn luôn có 3 con. Một node với ba mục dữ liệu thì luôn luôn có 4 con. Như vậy, một node không phải là lá phải luôn luôn có số node con nhiều hơn 1 so với số mục dữ liệu của nó. Nói cách khác, đối với mọi node với số con là c và số mục dữ liệu là d, thì : c = d + 1. Sau đây là các ví dụ cụ thể: Hình 4.2. Các trường hợp của cây 2-3-4 Với mọi node lá thì không có node con nhưng có thể chứa 1, 2 hoặc 3 mục dữ liệu, không có node rỗng. Một cây 2-3-4 có thể có đến 4 cây con nên được gọi là cây nhiều nhánh bậc 4. Trong cây 2-3-4 mỗi node có ít nhất là 2 liên kết ,trừ lnode lá (node không có liên kết nào). Hình 4.2 trình bày các trường hợp của cây 2-3-4. Một node với 2 liên kết gọi là một 2-node, một node với 3 liên kết gọi là một 3-node, và một node với 4 liên kết gọi là một 4-node, nhưng ở đây không có loại node nào là 1-node. II. Tổ chức cây 2-3-4. Các mục dữ liệu trong mỗi node được sắp xếp theo thứ tự tăng dần từ trái sang phải (sắp xếp từ thấp đến cao). Một đặc tính quan trọng của bất kỳ cấu trúc cây là mối liên hệ giữa các liên kết với giá trị khóa của các mục dữ liệu. Trong cây tìm kiếm nhị phân, tất cả node của cây con bên trái có khoá nhỏ hơn khóa của node đang xét và tất cả node của cây con bên phải có khoá lớn hơn hoặc bằng khóa của node đang xét. Trong cây 2-3-4 thì nguyên tắc cũng giống như trên, nhưng có thêm một số điểm sau: Với node có một mục dữ liệu. Tất cả các node con của cây con có gốc tại node con thứ 1 thì có các giá trị khoá nhỏ hơn giá trị khoá 1 của node cha. Tất cả các node con của cây con có gốc tại node con thứ 2 thì có các giá trị khoá lớn hơn giá trị khoá 1 của node cha. Với node có hai mục dữ liệu. Tất cả các node con của cây con có gốc tại node con thứ 1 thì có các giá trị khoá nhỏ hơn giá trị khoá 1 của node cha. Tất cả các node con của cây con có gốc tại node con thứ 2 thì có các giá trị khoá lớn hơn khoá 1 của node cha và nhỏ hơn giá trị khóa 2 của node cha. Tất cả các node con của cây con có gốc tại node con thứ 3 thì có các giá trị khoá lớn hơn giá trị khoá 2 của node cha. Với node có ba mục dữ liệu. Tất cả các node con của cây con có gốc tại node con thứ 1 thì có các giá trị khoá nhỏ hơn giá trị khoá 1 của node cha. Tất cả các node con của cây con có gốc tại node con thứ 2 thì có các giá trị khoá lớn hơn khoá 1 của node cha và nhỏ hơn giá trị khóa 2 của node cha. Tất cả các node con của cây con có gốc tại node con thứ 3 thì có các giá trị khoá lớn hơn khoá 2 của node cha và nhỏ hơn giá trị khóa 3 của node cha. Tất cả các node con của cây con có gốc tại node con thứ 4 thì có các giá trị khoá lớn hơn giá trị khoá 3 của node cha. Trong tất cả cây 2-3-4, các lá đều nằm trên cùng một mức. Các node ở mức trên thường không đầy đủ, nghĩa là chúng có thể chứa chỉ 1 hoặc 2 mục dữ liệu thay vì 3 mục. Lưu ý rằng cây 2-3-4 là cây cân bằng. Nó vẫn giữ được sự cân bằng khi thêm vào các phần tử có thứ tự (tăng dần hoặc giảm dần). III. Tìm kiếm. Thao tác tìm kiếm trong cây 2-3-4 tương tự như thủ tục tìm kiếm trong cây nhị phân. việc tìm kiếm bắt đầu từ node gốc và chọn liên kết dẫn đến cây con với phạm vi giá trị phù hợp. Ví dụ, để tìm kiếm mục dữ liệu với khoá là 64 trên cây ở hình 4.1, bạn bắt đầu từ gốc. Tại node gốc không tìm thấy mục khoá này. Bởi vì 64 lớn 50, chúng ta đi đến node con 1, (60/70/80)(lưu ý node con 1 nằm bên phải, bởi vì việc đánh số của các node con và các liên kết bắt đầu tại 0 từ bên trái). Tại vị trí này vẫn không tìm thấy mục dữ liệu, vì thế phải đi đến node con tiếp theo. Tại đây bởi vì 64 lớn hơn 60 nhưng nhỏ hơn 70 nên đi tiếp đến node con 1. Tại thời điểm chúng ta tìm được mục dữ liệu đã cho với liên kết là 62/64/66. IV. Tách node 1. Tách node con Việc thêm vào sẽ trở nên phức tạp hơn nếu gặp phải một node đầy (node có số mục dữ liệu đầy đủ) trên nhánh dẫn đến điểm thêm vào. Khi điều này xảy ra, node này cần thiết phải được tách ra. Quá trình tách nhằm giữ cho cây cân bằng. Loại cây 2-3-4 mà chúng ta đề cập ở đây thường được gọi là cây 2-3-4 top-down bởi vì các node được tách ra theo hướng đi xuống điểm chèn. Giả sử ta đặt tên các mục dữ liệu trên node bị phân chia là A, B và C. Sau đây là tiến trình tách (chúng ta giả sử rằng node bị tách không phải là node gốc; chúng ta sẽ kiểm tra việc tách node gốc sau này): Một node mới và rỗng được tạo. Nó là anh em với node sẽ được tách và được đưa vào bên phải của nó. Mục dữ liệu C được chuyển vào node mới. Mục dữ liệu B được chuyển vào node cha của node được tách. Mục dữ liệu A không thay đổi. Hai node con bên phải nhất bị hủy kết nối từ node được tách và kết nối đến node mới. Þ Quá trình tách node con sẽ xảy ra các trường hợp sau đây: Node cha của node cần tách có một khóa. 1. Node cần tách là node con bên phải của node cha nó Khi đó ta thêm một node mới có một mục dữ liệu, có giá trị khóa là khóa thứ 3 của node đang tách. Chuyển giá trị khóa thứ 2 của node cần tách nên node cha. Và gán lại quan hệ cha con của các node như hình dưới đây. 2. Node cần tách là node con bên trái của node cha nó. Ta thêm một node mới có một mục dữ liệu, có giá trị khóa là khóa thứ 1 của node đang tách. Chuyển giá trị khóa thứ 2 của node cần tách nên node cha. Và gán lại quan hệ cha con của các node như hình dưới đây. Node cha của node cần tách có hai khóa. 1.Node cần tách là node con bên phải (con thứ nhất) của node cha nó. Khi đó ta thêm một node mới có một mục dữ liệu, có giá trị khóa là khóa thứ 3 của node đang tách. Chuyển giá trị khóa thứ 2 của node đang tách nên node cha. Và gán lại quan hệ cha con của các node như hình dưới đây. 2. Node cần tách là node con bên giữa (con thứ hai) của node cha nó. Khi đó ta thêm một node mới có một mục dữ liệu, có giá trị khóa là khóa thứ 3 của node đang tách. Chuyển giá trị khóa thứ 2 của node đang tách nên node cha. Và gán lại quan hệ cha con của các node như hình dưới đây. 3. Node cần tách là node con phải (con thứ ba) của node cha nó. Khi đó ta thêm một node mới có một mục dữ liệu, có giá trị khóa là khóa thứ 3 của node đang tách. Chuyển giá trị khóa thứ 2 của node đang tách nên node cha. Và gán lại quan hệ cha con của các node như hình dưới đây. Một ví dụ về việc tách node trình bày trên hình 4.4. Một cách khác để mô tả sự tách node là một 4-node được chuyển đổi sang hai 2-nút. Chú ý rằng ảnh hưởng của sự tách node là dịch chuyển dữ liệu đi lên về bên phải. Sự sắp xếp lại này nhằm mục đích giữ cho cây cân bằng. Hình 4.4: Tách một nút (i ) Trước khi chèn vào (ii) Sau khi chèn vào 2. Tách node gốc Khi gặp phải node gốc đầy tại thời điểm bắt đầu tìm kiếm điểm chèn, kết quả của việc tách thực hiện như sau: Node mới được tạo ra để trở thành gốc mới và là cha của node được tách. Node mới thứ hai được tạo ra để trở thành anh em với node được tách. Mục dữ liệu C được dịch chuyển sang node anh em mới. Mục dữ liệu B được dịch chuyển sang node gốc mới. Mục dữ liệu A vẫn không đổi. Hai node con bên phải nhất của node được phân chia bị hủy kết nối khỏi nó và kết nối đến node mới bên phải. Một ví dụ về việc tách node trình bày trên hình 4.5 cho ta thấy rõ hơn quá trình tách node gốc. Hình 4.5 Tách node gốc i) Trước khi thêm vào ii) Sau khi thêm vào Hình 4.5 chỉ ra việc tách node gốc. Tiến trình này tạo ra một node gốc mới ở mức cao hơn mức của node gốc cũ. Kết quả là chiều cao tổng thể của cây được tăng lên 1. Đi theo node được tách này, việc tìm kiếm điểm chèn tiếp tục đi xuống phía dưới của cây. Trong hình 4.5 mục dữ liệu với khoá 41 được thêm vào lá phù hợp. 3. Tách theo hướng đi xuống Chú ý rằng, bởi vì tất cả các node đầy được tách trên đường đi xuống nên việc tách node không gây ảnh hưởng gì khi phải đi ngược lên trên của cây. Node cha của bất cứ node nào bị tách phải đảm bảo rằng không phải là node đầy, để đảm bảo node cha này có thể chấp nhận mục dữ liệu B mà không cần thiết nó phải tách ra. Tất nhiên nếu node cha này đã có hai con thì khi node con bị tách, nó sẽ trở thành node đầy. Tuy nhiên điều này chỉ có nghĩa là nó có thể sẽ bị tách ra khi lần tìm kiếm kế tiếp gặp nó. Hình 4.6 trình bày một loạt các thao tác chèn vào một cây rỗng. Có 4 node được tách, 2 node gốc và 2 node lá. Thêm vào 70, 30, 50 Thêm 40 Thêm vào 20, 80 Thêm vào 25, 90 Thêm vào 75 Thêm vào 10 Hình 4.6 Minh họa thêm một node vào cây 2-3-4 V. Chèn node Các mục dữ liệu mới luôn luôn được chèn vào tại các node lá . Nếu mục dữ liệu được thêm vào node mà có node con, thì số lượng của các node con cần thiết phải được chuyển đổi để duy trì cấu trúc cho cây, đây là lý do tại sao phải có số node con nhiều hơn 1 so với các mục dữ liệu trong một nút. Việc thêm vào cây 2-3-4 trong bất cứ trường hợp nào thì quá trình cũng bắt đầu bằng cách tìm kiếm node lá phù hợp. Nếu không có node đầy nào (node có đủ 3 mục dữ liệu) được bắt gặp trong quá trình tìm kiếm, việc chèn vào khá là dễ dàng. Khi node lá phù hợp được tìm thấy, mục dữ liệu mới đơn giản là thêm vào nó. Hình 4.3 trình bày một mục dữ liệu với khoá 18 được thêm vào cây 2-3-4. Việc chèn vào có thể dẫn đến phải di chuyển một hoặc hai mục dữ liệu trong node vì thế các khoá sẽ nằm với trật tự đúng sau khi mục dữ liệu mới được thêm vào. Trong ví dụ này số 23 phải được đẩy sang phải để nhường chỗ cho 18. Hình 4.3 Chèn vào không làm tách cây (i) trước khi chèn vào (ii) sau khi chèn vào VI. Tính hiệu quả của Cây 2-3-4 Trong cây đỏ-đen node trên mỗi mức phải được duyệt trong quá trình tìm kiếm, hoặc tìm kiếm một node đã tồn tại hoặc chèn vào một node mới. Số lượng các mức trong cây đỏ-đen (cây nhị phân cân bằng) là log2(N+1), vì thế thời gian tìm kiếm là tỷ lệ với giá trị này. Một node cũng phải được duyệt trong cây 2-3-4, nhưng cây 2-3-4 thì ngắn hơn (có ít mức hơn) so với cây đỏ-đen khi số lượng các mục dữ liệu như nhau. Xem hình 4.8, ở đây cây 2-3-4 có ba mức còn cây đỏ-đen có năm mức. Cụ thể hơn, trong cây 2-3-4 có đến 4 con trên một nút. Nếu mỗi node là đầy, chiều cao của cây phải tỷ lệ với log4(N). Logarith với cơ số 2 và cơ số 4 khác nhau bởi một thừa số hằng của 2. Kết quả, chiều cao của cây 2-3-4 sẽ thấp hơn một nửa so với chiều cao của cây đỏ-đen, miễn là tất cả các node là đầy. Bởi vì tất cả chúng là không đầy, chiều cao của cây 2-3-4 nằm trong khoảng log2(N+1) và log2(N+1)/2. Kết quả là việc giảm chiều cao của cây 2-3-4 sẽ dẫn đến việc giảm một ít thời gian tìm kiếm so với cây đỏ-đen. Mặt khác, có nhiều mục dữ liệu để kiểm tra trong mỗi nút, điều này sẽ tăng thời gian tìm kiếm. Bởi vì các mục dữ liệu trong mỗi node được kiểm tra sử dụng tìm tuyến tính, điều này sẽ nhân thời gian tìm kiếm hơn với một số lượng tỷ lệ với M, số lượng trung bình của các mục dữ liệu trên một nút. Kết quả là thời gian tìm kiếm xấp xỉ M*log4(N). Một vài node chỉ chứa 1 mục dữ liệu, một vài node chứa 2, và một vài node chứa 3. Nếu chúng ta ước lượng trung bình là 2, thời gian tìm kiếm sẽ xấp xỉ là 2*log4(N). Đây là hằng số nhỏ có thể bỏ qua trong biễu diễn độ phức tạp theo ký hiệu Big-O. Kết quả, với cây 2-3-4 số lượng tăng lên của các mục dữ liệu trên node dẫn đến việc hủy chiều cao giảm xuống của cây. Thời gian tìm kiếm của cây 2-3-4 và cây nhị phân cân bằng như cây đỏ-đen là xấp xỉ bằng nhau, và cả hai đều bằng O (log(N)). VII. Chuyển từ cây 2-3-4 sang cây đỏ đen. Một cây 2-3-4 có thể được chuyển sang cây đỏ-đen bằng cách áp dụng các luật sau: Chuyển đổi bất kỳ 2-node ở cây 2-3-4 sang node đen ở cây đỏ-đen. Chuyển đổi bất kỳ 3-node sang node con C (với hai con của chính nó) và node cha P (với các node con C và node con khác). Không có vấn đề gì ở đây khi một mục trở thành node con và mục khác thành node cha. C được tô màu đỏ và P được tô màu đen. Chuyển đổi bất kỳ 4-node sang node cha P và cả hai node con C1, C2 màu đỏ. Hình 4.7 trình bày các chuyển đổi này. Các node con trong các cây con được tô màu đỏ; tất cả các node khác được tô màu đen. Hình 4.8 trình bày cây 2-3-4 và cây đỏ-đen tương ứng với nó bằng cách áp dụng các chuyển đổi này. Các đường chấm xung quanh các cây con được tạo ra từ 3-node và 4-nút. Các luật của cây đỏ-đen tự động thoả mãn với sự chuyển đổi này. Kiểm tra rằng: Hai node đỏ không bao giờ được kết nối, và số lượng các node đen là như nhau ở mọi đường dẫn từ gốc đến lá (hoặc node con null). Bạn có thể nghĩ rằng một 3-node ở cây 2-3-4 là tương đương với node cha có một node con màu đỏ ở cây đỏ-đen, và một 4-node là tương đương với node cha có hai node con đỏ. Điều này nghĩa là node cha màu đen với node con đen ở cây đỏ-đen không biểu diễn một 3-node ở cây 2-3-4; nó chỉ biểu diễn một 2-node với node con 2-node khác. Tương tự, một node cha màu đen với 2 con màu đen không biểu diễn cho 4-nút. Hình 4.7 Chuyển đổi từ cây 2-3-4 sang cây đỏ-đen Hình 4.8 Cây 2-3-4 và cây đỏ-đen tương ứng Sự tương đương Không những cấu trúc của cây đỏ-đen phù hợp với cây 2-3-4, mà các thao tác hoạt động của hai loại cây này cũng tương đương nhau. Trong cây 2-3-4 nó được giữ cân bằng bằng việc tách nút. Trong cây đỏ-đen hai phương thức cân bằng là sự lật và quay màu. Việc tách 4-node và lật màu Trong cây 2-3-4 khi bạn tìm xuống điểm chèn cho node mới, bạn tách mỗi 4-node thành hai 2-nút. Trong cây đỏ-đen bạn thực hiện việc lật màu. Làm thế nào mà các thao tác này là tương đương với nhau? Hình 4.9 Tách 4-node và lật màu Trong hình 4.9.i trình bày một 4-node trong cây 2-3-4 trước khi bị tách nút; Hình 4.9.ii trình bày tình trạng sau khi tách nút. 2-node (là cha của 4-nút) trở thành 3-nút. Trong hình 4.9.iii. trình bày cây đỏ-đen tương đương với cây 2-3-4 ở hình 4.9.i,. Đường chấm viền quanh sự tương đương của 4-nút. Lật màu đưa đến kết quả cây đỏ-đen ở hình 4.9.iv. Bây giờ các node 40 và 60 là màu đen và 50 là màu đỏ. Kết quả 50 và node cha của nó hình thành sự tương đương với một 3-nút, như trình bày bằng đường chấm. Điều này tương tự 3-node định dạng bằng việc tách node trong hình 4.9.ii. Kết quả chúng ta thấy rằng việc tách một 4-node trong quá trình chèn ở cây 2-3-4 là tương đương với việc thực hiện lật màu trong quá trình chèn ở cây đỏ-đen. Tách 3-node và quay Khi một 3-node ở cây 2-3-4 được chuyển sang cây đỏ-đen tương đương của nó, có thể có hai sự sắp xếp, như trình bày trong hình 4.8. Cả hai mục dữ liệu có thể trở thành node cha. Tùy thuộc vào node nào được chọn, node con sẽ là node con bên trái hoặc node con bên phải, và độ dốc của đường thẳng nối node cha và node con sẽ ở bên trái hoặc bên phải. Cả hai sự sắp xếp là hợp lệ; Tuy nhiên, chúng không tham gia để cân bằng cây. Chúng ta hãy xem xét tình huống trong ngữ cảnh lớn hơn. Hình 4.10-i trình bày cây 2-3-4 còn hình 4.10-ii, và 4.10-iii trình bày các cây đỏ-đen tương đương được suy ra từ cây 2-3-4 bằng cách áp dụng các luật chuyển đổi ở trên. Sự khác nhau giữa chúng là cách lựa chọn hai mục dữ liệu nào trong 3-node để tạo node cha; Trong hình ii, 80 là node cha, trong hình iii, 70 là node cha. Mặc dù cách sắp xếp này là hợp lệ, bạn có thể thấy rằng cây trong hình ii là không cân bằng trong khi cây trong hình iii là cân bằng. Với cây đỏ-đen được cho trong hình ii, chúng ta sẽ quay nó sang phải (và thực hiện việc chuyển đổi hai màu) để cân bằng nó. Điều đáng ngạc nhiên sự quay này cho kết quả chính xác giống như cây trong hình iii. Hình 4.10: 3-node và phép quay CHƯƠNG II. MÔ PHỎNG THUẬT TOÁN TRÊN CÂY 2-3-4 I. Tổng quan về mô phỏng thuật toán. 1. Khái niệm thuật toán và các đặc trưng của thuật toán. Thuật toán là một dãy hữu hạn các thao tác được sắp xếp theo một trình tự xác định sao cho sau khi thực hiện dãy các thao tác ấy, từ Input của bài toán nhận được Output cần tìm. Các thuật toán có một số tính chất chung, đó là: Đầu vào (Input): Một thuật toán có các giá trị đầu vào từ một tập xác định. Đầu ra (Output): Từ mỗi tập giá trị đầu vào, thuật toán sẽ tạo ra các giá trị đầu ra. Các giá trị đầu ra chính là nghiệm của bài toán. Tính xác định: Các bước của thuật toán phải được mô tả một cách chính xác. Tính đúng đắn: Một thuật toán phải cho các giá trị đầu ra đúng đối với mỗi tập giá trị đầu vào. Tính hữu hạn: Một thuật toán phải tại ra các giá trị đầu ra sau một số hữu hạn (có thể rất lớn) các bước thực hiện đỗi với mỗi tập đầu vào. Tính hiệu quả: Mỗi bước của thuật toán phải thực hiện được một cách chính xác và trong một khoảng thời gian chấp nhận được. Tính tổng quát: Thuật toán cần phải áp dụng được cho mọi tập dữ liệu đầu vào của bào toán, chứ không phải chỉ cho một tập đặc biệt các giá trị đầu vào. 2. Khái niệm mô phỏng thuật toán. Mô phỏng thuật toán là quá trình tách dữ liệu , thao tác ngữ nghĩa và tạo mô phỏng đồ họa cho quá trình trên [Stasko 1990]. Mô phỏng thuật toán được thiết kế để giúp người dùng có thể hiểu thuật toán, đánh giá chương trình và sửa lỗi chương trình. Một chương trình máy tính chứa các cấu trúc dữ liệu của thuật toán mà nó thực thi. Trong quá trình thực thi chương trình, các giá trị trong cơ sở dữ liệu được thay đổi. Mô phỏng thuật toán sử dụng biểu diễn đồ họa để biểu diễn cấu trúc dữ liệu và chỉ ra sự thay đổi giá trị trong cơ sở dữ liệu trong mỗi trạng thái. Thông qua đó, người sử dụng có thể xem được từng bước thực thi chương trình và nhờ vậy có thể hiểu chi tiết được thuật toán. Mô phỏng thuật toán cũng được dùng để đánh giá một chương trình đã có bằng cách cung cấp các mô phỏng cho các thành phần của hệ thống, nhờ đó có thể kiểm tra được hiệu năng của hệ thống. Bên cạnh việc giúp người sử dụng hiểu hơn về hệ thống, mô phỏng thuật toán còn được dùng để giúp thực hiện quá trình dò lỗi dễ dàng hơn. II. Các yêu cầu về mô phỏng thuật toán. Phản ánh đúng nội dung của thuật toán Có thể thực hiện giải thuật theo hình thức từng bước một để theo dõi giá trị của các biến và các đối tượng trong bài toán. Có hình ảnh động (có thể có âm thanh khi cần) để mô tả trực quan quá trình thi hành của thuật toán. Có thể kiểm định thuật toán trong trường hợp ngẫu nhiên, trường hợp xấu nhất, trường hợp tốt nhất. Tạo các mức độ sử dụng khác nhau cho người học. 6.Cấu trúc tổng quát của mô phỏng. INPUT GIẢI THUẬT OUTPUT Cấu trúc dữ liệu trừu tượng. Dữ liệu mẫu - Tự động - Từng bước Dữ liệu ngẫu nhiên Dữ liệu nhập trực tiếp Biểu diễn đồ họa Độ phức tạp của thuật toán III. Quy trình thiết kế nhiếm vụ mô phỏng thuật toán. Sơ đồ quy trình thiết kế nhiệm vụ mô phỏng thuật toán như sau: Cơ chế sinh dữ liệu vào (Input) Xây dựng mô hình mô phỏng Input/Output Nghiên cứu và phân thích giải thuật Phân tích giải thuật thành nhiều bước Tổng hợp các bước thành giải thuật Những khó khăn thuận lợi khi tiếp thu giải thiật IV. Mô phỏng thuật toán trên Cây 2-3-4. 1. Giới thiệu ngôn ngữ mô phỏng. Visual FoxPro là một trong các ngôn ngữ lập trình quản trị cơ sơ dữ liệu. Visual FoxPro đã được nhiều thế hệ những người lập trình Việt Nam sử dụng viết các trình ứng dụng quản lý điểm, quản lý tài chính, quản lý bán hàng… từ những phiên bản đầu tiên của FoxPro for DOS, đến các phiên bản FoxPro for Windows và ngôn ngữ lập trình Visual FoxPro như hiện tại. Ngôn ngữ Visual FoxPro dễ sử dụng, đã được dạy trong nhiều trường phổ thông Việt Nam. Visual FoxPro ngoài tính năng cung cấp các chức năng giúp người lập trình có thể tạo ra các hệ quản trị cơ sở dữ liệu, nó còn cung cấp cho chúng ta tập hợp các lệnh, các hàm giúp người lập trình có thể lập trình hướng đối tượng trên đó. Lập trình hướng đối tượng trong Visual FoxPro là một hướng xây dựng và sử dụng ngôn gữ lập trình bằng các phân loại các đối tượng thành ra các lớp, điều khiển chương trình thông qua các tác vụ của các đối tượng. Lập trình hướng đối tượng tạo điều kiện cho người lập trình sắp xếp, tổ chức các môđun trong chương trình một cách khoa học, chặt chẽ. Trên cơ sở đó tiết kiệm thời gian viết mã lệnh và tạo thuận lợi cho công tác bảo trì phần mềm. Ngoài ra, Visual FoxPro còn cung cấp các chức năng giúp cho người lập trình có thể xây dựng giao diện phần mềm thân thiện và dễ sử dụng. Bên cạnh đó, nó còn cung cấp cho chúng ta một số lớp đồ họa, giúp cho việc xây dựng các hình ảnh mô phỏng được thuận tiện, đơn giản, tốn ít thời gian, mà không cần phải viêt mã lệnh. 2. Phân tích và thiết kế thuật toán mô phỏng Cây 2-3-4. a. Phân tích. Các node trong Cây 2-3-4 được sắp xếp theo giá trị các khóa của chúng – giống cây tìm kiếm nhị phân. Các giá trị khóa được sinh ra một cách ngẫu nhiên, nhiều lần. Mỗi node có thể là hình chữ nhật, có hiện thị các mục giá trị. Do đó cần xây dựng một lớp riêng cho các node gọi là lớp “node234” Trong quá trình tạo ra Cây 2-3-4 với các node, có ba thao tác cơ bản nhất đó là : Tìm kiếm, tách node, và chèn. Trong thao tác tìm kiếm, ta cần tìm kiếm xem giá trị cần chèn đã xuất hiện trong cây của chúng ta chưa. Nếu xuất hiện rồi, ta bỏ qua không chèn giá trị đó vào nữa. Nếu chưa ta sẽ chèn giá trị đó vào vị trí tìm được trong cây. Khi chèn các giá trị vào các node. Sẽ xảy ra một trường hợp là node đã đầy các mục dữ liệu. Yêu cầy chúng ta phải tách node, sau đó chèn giá trị khóa vào vị trí node cần chèn. b. Thiết kế. Xây dựng lớp “node234” - Các thành phần cơ bản của lớp: lớp “node234” là một container trong đó chứa 3 thành phần : Ba textbox, một tên là txt_1, một tên là txt_2, một tên là txt_3 để chứa lần lượt ba giá trị khóa của node đó. - Một đối tượng “node234” khi sinh ra cần được thay đổi kích thước theo giá trị khóa của node đó. Ngoài ra khi một node sinh ra được add thêm các giá trị khóa thì ta cần phải kiểm tra xem giá trị đó có xuất hiện trong node đó hoặc các con của node đó hay không. Vì vậy ta cần viết các thủ tục Refesh để thay đổi kích thước cho node khi số mục dữ liệu trong node thay đổi, và thêm các phương thức add_key, search_key rồi viết code cho nó để kiểm tra và thêm mục giá trị vào cho node đó. Ngoài ra ta cần phải viết thêm thủ tục noi_canh để nối các node với nhau. - Để quản lí quan hệ của mỗi node với các đối tượng khác ta thêm vào một số tính chất giống như biến con trỏ: pparent để quản lí cha của node; nkey[3]: quản lí các mục dữ liệu; child[4] : quản lí các con của node Thủ tục add_key được viết như sau: Node234 Add_key Lparameters n_key With This If (.so_muc < 3) j = .so_muc If (j > 0) DO case CASE (n_key < .nkey(j)) Do While (j >= 1) And (n_key < .nkey(j)) .nkey(j+1) = .nkey(j) j = j - 1 Enddo j = j + 1 .nkey(j)= n_key .so_muc = .so_muc + 1 .txt_1.Value = .nkey(1) .txt_2.Value = .nkey(2) .txt_3.Value = .nkey(3) CASE (n_key > .nkey(j)) j = j + 1 .nkey(j)= n_key .so_muc = .so_muc + 1 .txt_1.Value = .nkey(1) .txt_2.Value = .nkey(2) .txt_3.Value = .nkey(3) ENDCASE Else .so_muc = .so_muc + 1 .nkey(.so_muc)= n_key .txt_1.Value = .nkey(1) Endif ENDIF Endwith Thủ tục search_key được viết như sau: Node234 Search_key Lparameters n_key comat = .F. With This Do Case Case .so_muc = 1 *.nkey(1)=.txt_1.Value If (n_key = .nkey(1)) comat = .T. Return Else If (n_key < .nkey(1)) Return 1 Else Return 2 Endif Endif Case .so_muc=2 If (n_key = .nkey(1)) Or (n_key= .nkey(2)) comat = .T. RETURN .T. Else Do Case Case (n_key < .nkey(1)) Return 1 Case (n_key < .nkey(2)) Return 2 Otherwise Return 3 Endcase Endif Case .so_muc=3 For ii = 1 To 3 If (n_key = .nkey(ii)) comat = .T. RETURN .T. Endif Next If (comat = .F.) Do Case Case (n_key < .nkey(1)) Return 1 Case (n_key < .nkey(2)) Return 2 Case (n_key < .nkey(3)) Return 3 Otherwise Return 4 Endcase Endif Endcase Endwith Thủ tục noi_canh được viết như sau: Node234 Noi_canh Private All With This y1=.pparent.Top+.pparent.Height IF .class.pparent.class x1=.pparent.left+.pparent.width/2 else Do Case Case This=.pparent.Child(1) x1=.pparent.Left Case This=.pparent.Child(2) x1=.pparent.Left +.pparent.m2.Left Case This=.pparent.Child(3) x1=.pparent.Left +.pparent.m3.Left Case This=.pparent.Child(4) x1=.pparent.Left+ .pparent.Width ENDCASE ENDIF x2= .Left+.Width/2 y2=.Top .pline.Width=Abs(x1-x2) .pline.Height=Abs(y1-y2) .pline.Left=Min(x1,x2) .pline.Top=Min(y1,y2) If (x1>x2 And y1>y2) Or (x1<x2 And y1<y2) .pline.LineSlant="\" Else .pline.LineSlant="/" Endif .pline.ZOrder(1) .pline.Bordercolor = RGB(255,129,90) IF this.parent.root .pline.Visible=.T. ENDIF Endwith Sau khi mỗi một node được sinh ra, có node có một mục giá trị, có node có hai hoặc ba mục giá trị. Vì vậy, ta phải thay đổi kích thước của các node theo số mục dữ liệu của nó. Và ta cần viết trong thủ tục Refresh của lớp để thay đổi lại kích thước của node theo số mục giá trị của nó. Node234 Refresh WITH this IF (.so_muc = 1) .width = .txt_1.width .txt_2.visible = .F. .txt_3.visible = .F. ELSE IF (.so_muc = 2) .txt_3.visible = .F. .width = .txt_1.width+.txt_2.width ENDIF ENDIF ENDWITH Khi thi hành ta thấy giao diện của chương trình như sau: Để tạo ra giao diện như khi thi hành, ta cần viết các thủ tục sau: Form Init WITH thisform .root=.grand .grand.pparent=.grand .grand.left=.width/2 ENDWITH Form Add_node Lparameters nKey Private All .so_node = .so_node + 1 With Thisform Dimension .Nodes(.so_node) cnamenode="node"+Alltrim(Str(.so_node)) If Type("."+cnamenode)"O" .AddObject(cnamenode,"node234",nKey,.so_node) .Nodes(.so_node)=.&cnamenode .Nodes(.so_node).lb.Caption=Alltrim(Str(.so_node)) .Nodes(.so_node).Visible=.T. Endif cline="line"+Alltrim(Str(.so_node)) If Type("."+cline)"O" .AddObject(cline,"line") Endif .Nodes(.so_node).pline=.&cline .&cline..borderwidth=2 Return .Nodes(.so_node) Endwith Form Insert_key Lparameters oNode,nKey If Type("oNode")"O" &&OR UPPER(oNode.class)"NODE234" Return Endif Private All With Thisform * .edit1.Value=.edit1.Value+"Insert key = "+ Alltrim(Str(nKey))+Chr(13)+Chr(10) If oNode=.root .oKey.lb.Caption=Alltrim(Str(nKey)) .oKey.Left=.text1.Left .oKey.Top=.text1.Top .oKey.Visible=.T. .oKey.automove(.root.Left+.root.width-.oKey.width,.root.Top) Endif If Upper(.root.Class)"NODE234" && chua co nut goc NewNode=.add_node(nKey) .root=NewNode NewNode.pParent=.grand NewNode.x=.grand.Left+.grand.Width/2 NewNode.Left=NewNode.x-NewNode.Width/2 NewNode.Top=.grand.Top .root.pline.Visible=.F. Else k = oNode.search_key(nKey) If (Type("k") = "L") .oKey.visible=.F. Return .T. Else If Upper(oNode.Child(1).Class)"NODE234" If oNode.so_muc<3 oNode.add_key(nKey) Wait '' Timeout 0.5 .oKey.Visible=.F. Else * tach va them muc vao 1 trong 2 nut se tach ra WAIT '' TIMEOUT 0.5 .split_node(oNode,nKey) Endif ELSE .oKey.top=oNode.top+oNode.height-.oKey.height Do Case Case k=1 .oKey.Left=oNode.Left-.oKey.Width/2 Case k=2 .oKey.Left=oNode.Left+oNode.m2.Left-.oKey.Width/2 Case k=3 .oKey.Left=oNode.Left+oNode.m3.Left-.oKey.Width/2 Case k=3 .oKey.Left=oNode.Left+oNode.Width-.oKey.Width/2 ENDCASE Wait '' Timeout 0.5 .oKey.automove(oNode.Child(k).Left+oNode.Child(k).width/2,oNode.Child(k).Top) .insert_key(oNode.Child(k),nKey) Endif Endif Endif If oNode.grand And Upper(oNode.Child(1).Class)"NODE234" .tree_refresh Endif Endwith Return .F. Form Split_node Lparameters oNode,nKey Private All If Type("oNode")"O" Return Endif oParent=oNode.pparent With Thisform c1 = oNode.Child(1) c2 = oNode.Child(2) c3 = oNode.Child(3) c4 = oNode.Child(4) k2=oNode.m2.Value If Upper(oParent.Class)"NODE234" && ONode la nut goc NewNode = .add_node(oNode.m1.Value) NewNode.pparent=oNode oNode.Child(1)=NewNode NewNode.Child(1)=c1 NewNode.Child(2)=c2 c1.pparent=NewNode c2.pparent=NewNode NewNode.Left=oNode.Left NewNode.Top=oNode.Top NewNode = .add_node(oNode.m3.Value) NewNode.pparent=oNode oNode.Child(2)=NewNode NewNode.Child(1)=c3 NewNode.Child(2)=c4 c3.pparent=NewNode c4.pparent=NewNode NewNode.Left=oNode.Left NewNode.Top=oNode.Top oNode.m1.Value=oNode.m2.Value oNode.so_muc=1 oNode.Refresh oNode.Child(1).Left=oNode.Child(1).Left-oNode.Child(1).Width Wait '' Timeout 0.5 If Type("nKey")="N" If nKey<k2 oNode.Child(1).add_key(nKey) Else oNode.Child(2).add_key(nKey) Endif Endif Else c3 = oNode.Child(3) c4 = oNode.Child(4) NewNode = .add_node(oNode.m3.Value) NewNode.Left=oNode.Left NewNode.Top=oNode.Top oNode.so_muc=1 oNode.Refresh NewNode.pparent=oParent NewNode.automove(oNode.Left+oNode.Width,oNode.Top+oNode.height/2) .oKey1.lb.Caption=Alltrim(Str(oNode.m2.Value)) .oKey1.top=oNode.top .oKey1.left=oNode.left .oKey1.Visible=.T. .oKey1.automove(oParent.Left,oParent.Top+oParent.height/2) Wait '' Timeout 0.5 .oKey1.Visible=.F. oParent.add_key(oNode.m2.Value) NewNode.Child(1)= c3 NewNode.Child(2) = c4 c3.pparent = NewNode c4.pparent = NewNode Do Case Case oParent.so_muc = 2 oNode.so_muc=1 oNode.Refresh If oNode = oParent.Child(1) oParent.Child(3) =oParent.Child(2) oParent.Child(2) = NewNode NewNode.pparent = oParent Else oParent.Child(3) = NewNode NewNode.pparent = oParent Endif Case oParent.so_muc = 3 Do Case Case oNode = oParent.Child(1) oParent.Child(4) = oParent.Child(3) oParent.Child(3) = oParent.Child(2) oParent.Child(2) = NewNode Case oNode = oParent.Child(2) oParent.Child(4) = oParent.Child(3) oParent.Child(3) = NewNode Case oNode=oParent.Child(3) oParent.Child(4) = NewNode Endcase Endcase If Type("nKey")="N" If nKey>k2 NewNode.add_key(nKey) Else oNode.add_key(nKey) Endif Endif If oParent.so_muc = 3 .split_node(oParent) Endif Endif oNode.so_muc=1 oNode.Refresh NewNode.noi_canh .tree_refresh Endwith Form Tinh_x Lparameters oNode With Thisform Private All If Type("onode") = "O" And Upper(oNode.Class)="NODE234" If Upper(oNode.Child(1).Class)="NODE234" For i=1 To oNode.so_muc+1 .tinh_x(oNode.Child(i)) Next Endif .visit_x(oNode) Endif Endwith Form Tinh_y Private All With Thisform .root.Top= .grand.top For i=2 To .so_node cname=".Node"+Alltrim(Str(i)) oNode=.Nodes(i) .Nodes(i).Top=.Nodes(i).pparent.Top+50 Next Endwith Form Visit_x Lparameters oNode If Type("oNode")"O" Return ENDIF PRIVATE all With Thisform If Upper(oNode.child(1).Class)="NODENULL" oNode.x=.x+oNode.width/2 .x=.x+oNode.Width+15 oNode.left=oNode.x-onode.width/2 Else x1=0 so_con=oNode.so_muc+1 For i=1 To so_con x1=x1+oNode.Child(i).x Next oNode.x=x1/so_con Endif Endwith TÀI LIỆU THAM KHẢO Giáo trình Visual FoPro. Cẩm nang thuật toán. Các tài liệu trên mạng.

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

  • docly thuyet mo phong.doc