Đề tài Tìm hiểu về thuật toán sắp xếp

Mục lục

NGHIÊN CỨU KHOA HỌC 1

Đề tài : Tìm hiểu về Thuật Toán Sắp Xếp 1

Mục lục 2

PHẦN MỞ ĐẦU 4

1. Lý do chọn đề tài 4

2. Mục tiêu và nhiệm vụ 5

Chương 1. MỘT SỐ KIẾN THỨC CƠ SỞ 6

1.1. Thuật toán 6

1.1.1. Khái niệm thuật toán 6

1.1.2. Các đặc trưng của thuật toán 7

Chương 2. MÔ PHỎNG THUẬT TOÁN 10

2.1. Tổng quan về mô phỏng thuật toán 10

2.1.1. Khái niệm mô phỏng thuật toán 10

2.1.2. Lịch sử mô phỏng thuật toán 11

2.1.3. Tác dụng của mô phỏng thuật toán 14

2.1.4. Kiến trúc của hệ thống mô phỏng thuật toán 18

2.1.5. Lựa chọn công cụ mô phỏng thuật toán 20

2.2. Một số yêu cầu đối với mô phỏng thuật toán 21

2.2.1. Mô tả đúng theo thuật toán 21

2.2.2. Hệ thống mô phỏng phải được thực hiện theo từng bước 21

2.2.3. Mô phỏng thuật toán phải có tính động 21

2.2.4. Phải tạo ra sự phân cấp cho người học 22

2.2.5. Cấu trúc của mô phỏng thuật toán 22

2.3. Quy trình thiết kế nhiệm vụ mô phỏng thuật toán 23

2.3.1. Nghiên cứu và phân tích giải thuật 23

2.3.2. Phân tích giải thuật thành nhiều bước, sau đó lần lượt mô phỏng từng bước đó 26

2.3.3. Phân tích khả năng tổng hợp các bước đã phân tích thành giải thuật 27

2.3.4. Phân tích những khó khăn và thuận lợi với những người lần đầu tiên biết đến giải thuật 27

2.4. Kết luận 28

Chương3 : CHƯƠNG TRÌNH ỨNG DỤNG THUẬT TOÁN SẮP XẾP 29

3.1 CÁC THUẬT TOÁN SẮP XẾP ĐƠN GIẢN 30

3.1.1 Sắp xếp lựa chọn 30

3.1.2 Sắp xếp xen vào 32

3.1.3 Sắp xếp nổi bọt 33

3.2 Sắp xếp hòa nhập 35

3.3 Sắp xếp nhanh 38

3.4 Sắp xếp sử dụng cây thứ tự bộ phận 45

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

doc42 trang | Chia sẻ: maiphuongdc | Lượt xem: 7037 | Lượt tải: 1download
Bạn đang xem trước 20 trang tài liệu Đề tài Tìm hiểu về thuật toán sắp xếp, để xem tài liệu hoàn chỉnh bạn click vào nút DOWNLOAD ở trên
hỏng tốt hơn, chẳng hạn, chương trình mô phỏng cho phép sinh viên đưa vào tập dữ liệu của chính họ và thực hiện mô phỏng trên tập dữ liệu này chứ không chỉ dừng lại ở việc quan sát những tập dữ liệu mẫu. Hơn nữa, nhiều nghiên cứu gần đây bởi Kehoe et al. (1999) cho thấy có thể sử dụng mô phỏng như một công cụ giáo dục. Thí nghiệm được thực hiện trong một thái độ khác từ các thí nghiệm khác. Những sinh viên được chia thành hai nhóm và cả hai nhóm đều học thuật toán ‘binomial heap” (đống nhị thức). Một nhóm học thuật toán bởi sự tương tác với mô phỏng trong khi nhóm còn lại là đọc những hình dạng phẳng về các điểm khóa thao tác của thuật toán. Sự khác nhau trong thí nghiệm này là kịch bản bài tập về nhà. Những sinh viên được đưa cho những câu hỏi trước khi bắt đầu khóa học. Trong suốt thời gian kiểm tra thử, những sinh viên có thể truy cập tới bài dạy và thời gian để hoàn thành bài kiểm tra thử này được cho tương đối nhiều. Các kết quả của thí nghiệm này cho thấy nhóm được trang bị chương trình mô phỏng thuật toán thực hiện bài kiểm tra thử tốt hơn nhóm kia. Các sinh viên của nhóm có sử dụng mô phỏng thuật toán phản hồi rằng mô phỏng đã giúp đỡ họ hiểu thuật toán tốt hơn. Báo cáo của Kehoe et al (1999) đã trình diễn một cách sử dụng mô phỏng thuật toán trong việc dạy để đạt được giá trị sư phạm cao hơn. Nó đã được thuyết trình rằng mô phỏng thuật toán được sử dụng tốt hơn trong các tình trạng học tương tác và mô phỏng (như một bài tập về nhà). Cũng như vậy, mô phỏng thuật toán có thể có tính sư phạm hơn khi nó được sử dụng trong việc phối hợp với các cách học khác hoặc giúp đỡ những chỉ dẫn khác để giải thích làm thế nào thực hiện một thao tác của thuật toán. Báo cáo cũng nói rằng với mô phỏng thuật toán người ta có thể dễ dàng học các thao tác theo thủ tục của các thuật toán. Ngoài ra nó có thể làm cho việc học một thuật toán bớt đáng sợ hơn vì nó làm cho thuật toán dễ tiếp cận hơn. Stasko et al. (1993) đã kết luận từ thí nghiệm của họ một số điều kiện mà mô phỏng thuật toán có thể có lợi nhất. Một trong số những điều kiện này là hỗ trợ mô phỏng thuật toán với những chỉ dẫn thúc đẩy toàn diện. Khi mô phỏng thuật toán đóng vai trò chỉ dẫn này, màn hình mô phỏng phải được bổ sung bởi các mô tả văn bản của các thao tác đang diễn ra. Một điều kiện khác đó là hệ thống mô phỏng thuật toán cần phải bao gồm các chức năng: quay lại hoặc lặp lại những bước thực hiện thuật toán để cho phép những người dùng sao lưu và xem lại những thao tác quan trọng. Một số bài giảng đòi hỏi các trạng thái thực hiện thuật toán cũng cần phải được ghi lại và cung cấp lại được. Sự phản hồi của sinh viên cũng là quý giá trong việc cải thiện chất lượng chỉ dẫn của mô phỏng. Mặc dù những kết quả được đưa ra từ những nghiên cứu thực nghiệm này không phải luôn có lợi, thì cũng không có nghĩa rằng mô phỏng thuật toán không hiệu quả trong dạy học. Hiện nay đang có nhiều nghiên cứu đang được tiến hành về thiết kế và đánh giá mô phỏng thuật toán. Hansen et al. (1999) tin rằng các kết quả trong các nghiên cứu thực nghiệm trên chưa tốt không phải vì mô phỏng thuật toán là phương pháp dạy học không tốt, mà vì cách thức thực hiện các mô phỏng chưa tốt. Họ đã phát triển một hệ thống trực quan hóa giải thuật siêu phương tiện gọi là HalVis (Hypermedia Algorithm Visualizations). Dựa vào framework của chúng, họ đã thiết kế các trực quan hóa giải thuật, và họ đã hướng dẫn vài thí nghiệm thực nghiệm bởi việc sử dụng hệ thống này. Tất cả các kết quả thí nghiệm cho thấy trực quan hóa giải thuật bằng đồ họa có hiệu quả hơn so với các phương pháp dạy truyền thống. Những kết quả này cho thấy rằng để mô phỏng thuật toán có hiệu quả và có lợi cho người dùng, thì việc thiết kế cho thích hợp và cách thức mô phỏng là những yếu tố quan trọng. Để mô phỏng thuật toán có hiệu quả thì hệ thống mô phỏng cần phải đáp ứng những điều sau : Truy cập mở (Open access): Người dùng có thể truy cập hệ thống mô phỏng mở. Hơn nữa, nếu có cài đặt hệ thống mô phỏng trong trường học, thì họ có thể truy cập tới hệ thống này từ nhà hoặc từ bất cứ nơi nào khác. Mô phỏng một cách có điều khiển (Control animation): Người dùng có thể tự tạo tập dữ liệu của chính mình khi sử dụng hệ thống mô phỏng. Trong khi các tập dữ liệu được cài đặt sẵn cũng có thể giúp đỡ sinh viên có những sự hiểu biết ban đầu, hệ thống nên có cả 2 tùy chọn này. Tương tác (Ineractivity): Hệ thống mô phỏng phải cung cấp được sự tương tác giữa người dùng và hệ thống. Sự tương tác bao gồm: người dùng xem theo từng bước, hủy, chạy nhanh tới một bước mong muốn, hay xem lại từ đầu, ... Lịch sử (History): Hệ thống mô phỏng cho phép người dùng xem lại các bước trước trong quá trình thực hiện. Phản hồi (Feedback): Phải tiếp thu phản hồi của sinh viên về việc sử dụng hệ thống mô phỏng để ước lượng hiệu quả của hệ thống cũng như để cải thiện hệ thống. Kiến trúc của hệ thống mô phỏng thuật toán Đa số các hệ thống mô phỏng thuật toán có những thư viện hỗ trợ thủ tục mô phỏng và giao diện mô phỏng. Vài hệ thống mô phỏng đòi hỏi phải đưa vào trực tiếp bằng tay những thông điệp gửi tới các thủ tục mô phỏng trong chương trình thực hiện thuật toán. Những hệ thống mô phỏng thuật toán ra đời sớm như: BALSA and TAGO là sự kiện – điều khiển (event-driven), nghĩa là chúng có một chương trình phát sinh những sự kiện trong dạng những thông điệp tới một máy chủ thông điệp. Máy chủ thông điệp chuyển thông điệp tới những cảnh quan tương ứng. Một cảnh quan là một cửa sổ trong một thiết bị màn hình nơi người dùng nhìn những đối tượng mô phỏng. Thông điệp bao gồm thông tin của một đối tượng mô phỏng. Sau khi cảnh quan nhận thông điệp, nó tính toán lại đối tượng và kéo lại nó trên cảnh quan. Vài hệ thống gần đây được viết bằng Java và tất cả đều có những kiến trúc tương tự nhau. Ví dụ như: JSamba, hệ thống POLKA tiền tiêu (xem gatech.due/gvu/softviz/parviz/samba.html) và JAWAA (Java và mô phỏng thuật toán trên mạng, xem phát triển bởi Pierson và Rodger tại trường đại học Duke vào năm 1996. Những hệ thống này chấp nhận framework của TANGO như kiến trúc của nó. Tất cả các hệ thống sẽ gồm có 3 thành phần, các hàm mô phỏng (animator), kênh mô phỏng (animation interpreter) và trình diễn mô phỏng (animation viewer) như đã chỉ ra trong sơ đồ sau: Màn hình trình diễn mô phỏng Các hàm mô phỏng File kịch bản ASCII Kênh mô phỏng Hình 1. Kiến trúc của hệ thống mô phỏng thuật toán Các hàm mô phỏng: Chứa các thư viện để vẽ các đối tượng mô phỏng trên thiết bị màn hình. Màn hình trình diễn mô phỏng: Cung cấp một môi trường đồ họa để trình diễn mô phỏng trên thiết bị màn hình tới người dùng cuối. Kênh mô phỏng: Đóng vai trò như một kênh truyền thông giữa hệ thống mô phỏng và người dùng cuối. Nó đọc một file kịch bản ASCII được cung cấp bởi người dùng cuối mà trong đó có chứa mô phỏng văn bản cung cấp việc phát sinh những lệnh. Kênh mô phỏng dịch các lệnh kịch bản thành các lệnh mô phỏng tương ứng và chuyển qua những tham số điều khiển của đối tượng mô phỏng tới các hàm mô phỏng. Các hàm mô phỏng vẽ đối tượng được mô phỏng theo các tham số điều khiển của đối tượng đó tới Animation viewer. Các tham số điều khiển bao gồm tọa độ x và y chỉ rõ nơi đối tượng được mô phỏng xuất hiện trong Animation viewer hoặc màu sắc của đối tượng được mô phỏng. Lựa chọn công cụ mô phỏng thuật toán Trong mục này, chúng ta sẽ phân tích cách tiếp cận khác để xây dựng hệ thống mô phỏng và tính khả thi của chúng. Chúng ta cũng sẽ ước lượng một vài công cụ mô phỏng thuật toán thích hợp để xây dựng hệ thống mô phỏng thuật toán. Công cụ thích hợp nhất sẽ được lựa chọn và các căn chỉnh trên sự lựa chọn này sẽ được cung cấp. Có ba cách tiếp cận có thể để xây dựng hệ thống mô phỏng phân tách. Cách tiếp cận đầu tiên sẽ xây dựng hệ thống từ đầu nhờ việc sử dụng ngôn ngữ C#. Cách tiếp cận thứ hai sẽ lựa chọn hệ thống mô phỏng thuật toán có mục đích chung thích hợp để xây dựng các thành phần tương tác của hệ thống phân tách từ đầu. Cách tiếp cận cuối cùng là lựa chọn một hệ thống mô phỏng thuật toán phân tách đã tồn tại và sửa đổi hệ thống đó thành hệ thống cuối cùng. Một số yêu cầu đối với mô phỏng thuật toán Mô tả đúng theo thuật toán Thuật toán được đưa ra mô phỏng phải chính xác, các bước thực hiện thuật toán phải trực quan và phản ánh đúng theo nội dung thuật toán đã đưa ra để đảm bảo tính đúng đắn của thuật toán. Để kiểm tra tính đúng đắn của thuật toán, ta có thể cài đặt giải thuật đó trên máy tính rồi đưa vào các bộ dữ liệu xác định, lấy kết quả thu được xác định với kết quả đã biết. Bộ dữ liệu đưa vào phải đảm bảo kết quả thu được phải vét kín các trường hợp nghiệm của bài toán (trường hợp thông thường và các trường hợp đặc biệt). Làm theo cách này thì không chắc chắn, ta chỉ phát hiện được thuật toán sai chứ không khẳng định được luôn đúng. Tính đúng đắn chỉ có thể khẳng định bằng phương pháp chứng minh toán học. Hệ thống mô phỏng phải được thực hiện theo từng bước Thuật toán thường là trìu tượng, nếu để chương trình chạy tự động thì người dùng sẽ khó hiểu. Vì vậy, cần phải có chế độ thực hiện mô phỏng thuật toán theo từng bước, để người học có thể quan sát, theo dõi sự thay đổi giá trị của từng biến. Nhờ đó, sẽ giúp cho người học hiểu thuật toán rõ hơn và nhanh hơn. Mô phỏng thuật toán phải có tính động Để mô tả trực quan hóa quá trình thực hiện của thuật toán ta nên đưa vào hình ảnh động (có thể có âm thanh) để thể hiện sự thay đổi của dữ liệu trong quá trình thực thi. Thuật toán phải được thử nghiệm trong mọi trường hợp để đảm bảo thời gian thực thi tốt nhất Một thuật toán được mô phỏng phải đảm bảo là thuật toán tốt, dễ hiểu và đúng đắn. Muốn vậy ta phải thử nghiệm trong các trường hợp dữ liệu ngẫu nhiên, tốt nhất, xấu nhất. Nếu thuật toán vẫn chạy tốt và trong một thời gian cho phép thì thuật toán mới hiệu quả. Ta không thể chấp nhận một thuật toán đúng mà thời gian chạy quá lớn. Phải tạo ra sự phân cấp cho người học Đối tượng học thuật toán thường là các sinh viên. Họ có trình độ tiếp thu khác nhau, nên ta phải đưa ra nhiều chế độ thao tác khác nhau để người học được phép lựa chọn. Cấu trúc của mô phỏng thuật toán INPUT ALGORITHM OUTPUT - Dữ liệu mẫu - Dữ liệu trực tiếp - Tự động - Từng bước Cấu trúc dữ liệu trừu tượng Biểu diễn bằng demo Độ phức tạp của thuật toán Hình 2. Cấu trúc của mô phỏng thuật toán Quy trình thiết kế nhiệm vụ mô phỏng thuật toán Phân tích giải thuật thành nhiều bước Những khó khăn thuận lợi khi tiếp thu giải thuật Tổng hợp các bước thành giải thuật Xây dựng mô hình mô phỏng Input, Output Cơ chế sinh dữ liệu vào Nghiên cứu và phân tích giải thuật Hình 3. Sơ đồ quy trình thiết kế nhiệm vụ mô phỏng thuật toán Nghiên cứu và phân tích giải thuật Trước khi lập trình cho máy tính giải một bài toán, điều đầu tiên là chúng ta phải đi xác định bài toán, để từ đó xây dựng giải thuật cho bài toán. Một bài toán đưa ra có thể có nhiều hơn một giải thuật, vấn đề là ta phải đi đánh giá các giải thuật đó để lựa chọn ra một giải thuật tốt nhất. Vậy như thế nào là một giải thuật tốt? Để làm được điều này ta có thể căn cứ vào các tiêu chuẩn sau: Giải thuật đưa ra phải đúng đắn Giải thuật phải đơn giản (dễ hiểu) Giải thuật phải thực hiện nhanh (độ phức tạp của thuật toán phải thấp) Khi đưa ra một giải thuật, điều đầu tiên chúng ta quan tâm đến đó là tính đúng đắn của giải thuật đó. Để biết giải thuật mình đưa ra có đúng đắn hay chưa ta có thể cài đặt giải thuật bằng một ngôn ngữ lập trình cụ thể và cho thực hiện trên máy với bộ dữ liệu mẫu, lấy kết quả thu được so sánh với kết quả đã biết. Cách làm này nói chung là chưa chắc chắn, vì kết quả có thể đúng với bộ dữ liệu mẫu, nhưng với bộ dữ liệu khác thì chưa khẳng định là đúng được. Mặt khác, cách làm này thực tế chỉ phát hiện ra giải thuật sai chứ không kết luận được là giải thuật đúng. Tính đúng đắn của giải thuật cần phải được chứng minh bằng toán học. Nhưng điều này không hề đơn giản. Vì vậy, chúng ta có thể kiểm tra tính đúng đắn của giải thuật bằng cách kiểm tra với các bộ dữ liễu mẫu, sao cho các bộ dữ liệu này phải phủ kín các trường hợp nghiệm có thể của bài toán. Sau khi xây dựng giải thuật của bài toán xong. Khâu tiếp theo là chúng ta tiến hành cài đặt giải thuật của bài toán bằng một ngôn ngữ lập trình nào đó. Nếu bài toán với dữ liệu nhỏ, không quan tâm đến thời gian chạy chương trình (tức là thuật toán chỉ được sử dụng một vài lần) thì giải thuật sẽ tốt hơn nếu việc cài đặt nó là dễ dàng và người dùng dễ hiểu. Tuy nhiên, giải thuật cho một bài toán sau khi được cài đặt thường xử lý với dữ liệu lớn và được sử dụng nhiều lần trong chương trình. Vì thế khi xây dựng một giải thuật, người lập trình thường quan tâm đến độ phức tạp của thuật toán (thường là độ phức tạp về thời gian mà đã được đề cập rất kỹ ở mục 1.3). Điều này dẫn đến việc giải thuật được xây dựng phải có tính hiệu quả về thời gian thực hiện chương trình. Các phương pháp diễn tả giải thuật Phương pháp liệt kê từng bước (sử dụng ngôn ngữ tự nhiên) Giả mã và ngôn ngữ lập trình thân thiện với người dùng (ví dụ như: PASCAL) Dùng sơ đồ khối Hiện nay, trong ba phương pháp trên thì việc dùng giả mã và một ngôn ngữ lập trình thân thiện với người dùng để diễn tả một giải thuật được đề cập đến nhiều hơn cả; được sử dụng trong dạy học cấu trúc dữ liệu và giải thuật mà rất nhiều tài liệu đã đưa ra. Phân tích các trường hợp đặc biệt của dữ liệu đầu vào, các giá trị của biến điều khiển lúc thoát khỏi vòng lặp. Các giá trị của biến lúc thoát khỏi vòng lặp thường là một dấu hiệu đặc biệt để thoát khỏi vòng lặp. Dữ liệu đầu vào thường gồm nhiều bộ dữ liệu khác nhau về giá trị, tuy nhiên trong số đó phải có một số bộ dữ liệu đặc biệt. Những bộ dữ liệu đó đặc biệt về giá trị dữ liệu đầu vào hoặc đặc biệt về kết quả trả ra. Bộ dữ liệu đầu vào đặc biệt giúp ta không cần chạy thử chương trình cũng có thể biết kết quả thu được. Vì vậy, những bộ dữ liệu đặc biệt thường được dùng làm giá trị kiểm thử để đánh giá thuật toán đúng hay sai, hoặc đánh giá chương trình được viết để chạy trên máy tính có đúng với thuật toán đưa ra hay không? Phân tích đánh giá các lỗi có thể mắc phải khi viết chương trình thực thi giải thuật Bài toán sau khi được xác định và dựa trên ý tưởng ta sẽ xây dựng được giải thuật của bài toán đó. Sau đó tiến hành cài đặt thuật toán này bằng một ngôn ngữ lập trình cụ thể ở một môi trường lập trình trên máy tính để máy thực hiện tự động giải thuật cho ta kết quả của bài toán. Một bài toán có thể được viết bằng nhiều ngôn ngữ lập trình. Vì vậy giải thuật phải được viết sao cho mọi lập trình viên của các ngôn ngữ lập trình đều có thể hiểu được và dễ dàng chuyển từ giải thuật sang cài đặt bằng ngôn ngữ lập trình mà họ thông thạo. Vì thế, khi viết giải thuật cho một bài toán, nên viết bằng ngôn ngữ tự nhiên, gần gũi, dễ hiểu và ít gò bó. Tuy nhiên, việc sử dụng một ngôn ngữ lập trình bậc cao để cài đặt giải thuật thường gặp phải một số vấn đề: Phải tuân thủ chặt chẽ các quy tắc về cú pháp Phụ thuộc vào cấu trúc dữ liệu mặc định của ngôn ngữ Ngôn ngữ tự nhiên thường rất đa nghĩa, nên việc chuyển từ ngôn ngữ tự nhiên sang ngôn ngữ lập trình cũng dễ mắc phải lỗi bởi vì câu lệnh được chuyển không đúng với nghĩa thực của nó. Chính vì vậy mà ta có thể sử dụng giả mã để viết giải thuật. Vì giả mã dùng ngôn ngữ tựa Pascal – một ngôn ngữ lập trình bậc cao thân thuộc với hầu hết người dùng để viết giải thuật cho một bài toán. Phân tích sự giống nhau và khác nhau của các giải thuật tương tự Một bài toán khi đưa ra có thể có nhiều giải thuật, tuy nhiên trong số những giải thuật đó ta cần lựa chọn ra một giải thuật để làm việc. Câu hỏi đặt ra ở đây là nên chọn giải thuật nào trong số các giải thuật đó? Muốn vậy ta phải đánh giá xem giải thuật nào là đơn giản, thời gian thực hiện nhanh, tốn ít bộ nhớ, tối ưu,… nhằm lựa chọn ra giải thuật tốt nhất để giải bài toán sao cho dễ mô phỏng. Phân tích giải thuật thành nhiều bước, sau đó lần lượt mô phỏng từng bước đó Việc phân chia giải thuật ra làm các modul, mỗi modul thực hiện một công việc khác nhau rất có ý nghĩa trong việc tinh chỉnh giải thuật. Phân tích giải thuật ra thành nhiều bước khác nhau và tiến hành mô phỏng từng bước của giải thuật đó giúp người dùng dễ theo dõi giải thuật hơn. Từ đó có thể hiểu được cơ chế hoạt động của chương trình. Dựa trên các bước của giải thuật được phân tích, ta xây dựng các đoạn code mô phỏng từng bước của thuật toán. Nhờ đó người dùng dễ dàng hiểu thuật toán hơn. Phân tích khả năng tổng hợp các bước đã phân tích thành giải thuật Với mỗi thuật toán, khi đã phân tích thành các bước, vấn đề còn lại là tổng hợp chúng lại thành giải thuật của bài toán. Điều này không có gì khó khăn, ta chỉ việc cài đặt lại giải thuật đó bằng một ngôn ngữ lập trình cụ thể (Java chẳng hạn) rồi thiết kế, chỉnh sửa để thực hiện mô phỏng thuật toán đó là tốt nhất có thể. Phân tích những khó khăn và thuận lợi với những người lần đầu tiên biết đến giải thuật Khi người học lần đầu tiên tiếp thu giải thuật mới sẽ gặp những thuận lợi và khó khăn sau: Khó khăn: Đối với môn học cấu trúc dữ liệu và giải thuật, có rất nhiều giải thuật phức tạp, trừu tượng, khó hiểu và khó hình dung. Vì vậy, để nắm được giải thuật này thật chắc không phải là điều đơn giản với người học. Thuận lợi: Khi học những giải thuật bằng phương pháp truyền thống, tức là chỉ bằng lý thuyết sẽ làm cho người học cảm thấy rất mơ hồ về giải thuật đó. Chính vì lẽ đó, nếu ta sử dụng mô phỏng với một bên là hình vẽ và một bên cho phép hiển thị giả mã của giải thuật thì người dùng có thể vừa theo dõi thuật toán, vừa có thể ‘nhìn thấy’ cách mà thuật toán thực hiện trên một hình cụ thể. Từ đó người dùng hiểu thuật toán dễ hơn và sâu sắc hơn. Kết luận Thông qua việc giới thiệu một cách tổng quan nhất về mô phỏng thuật toán, ta đã thấy được tác dụng to lớn của mô phỏng thuật toán trong giáo dục. Trên cơ sở đó, ta cũng đã hiểu được kiến trúc của một hệ thống mô phỏng thuật toán. Từ đó đưa ra một số công cụ cho phép xây dựng một hệ thống mô phỏng thuật toán bằng cách lựa chọn một công cụ thích hợp nhất. Sau khi đã có công cụ lập trình, ta tiến hành xây dựng một quy trình thiết kế hệ thống mô phỏng thuật toán nhằm đáp ứng nhu cầu người dùng. CHƯƠNG 3 : CHƯƠNG TRÌNH ỨNG DỤNG THUẬT TOÁN SẮP XẾP Sắp xếp là một quá trình biến đổi một danh sách các đối tượng thành một danh sách thoả mãn một thứ tự xác định nào đó. Sắp xếp đóng vai trò quan trọng trong tìm kiếm dữ liệu. Chẳng hạn, nếu danh sách đã được sắp xếp theo thứ tự tăng dần (hoặc giảm dần), ta có thể sử dụng kỹ thuật tìm kiếm nhị phân hiệu quả hơn nhiều tìm kiếm tuần tự… Trong thiết kế thuật toán, ta cũng thường xuyên cần đến sắp xếp, nhiều thuật toán được thiết kế dựa trên ý tưởng xử lý các đối tượng theo một thứ tự xác định. Các thuật toán sắp xếp được chia làm 2 loại: sắp xếp trong và sắp xếp ngoài. Sắp xếp trong được thực hiện khi mà các đối tượng cần sắp xếp được lưu ở bộ nhớ trong của máy tính dưới dạng mảng. Do đó sắp xếp trong còn được gọi là sắp xếp mảng. Khi các đối tượng cần sắp xếp quá lớn cần lưu ở bộ nhớ ngoài dưới dạng file, ta cần sử dụng các phương pháp sắp xếp ngoài, hay còn gọi là sắp xếp file. Trong chương này, chúng ta trình bày các thuật toán sắp xếp đơn giản, các thuật toán này dòi hỏi thời gian O(n2) để sắp xếp mảng n đối tượng. Sau đó chúng ta đưa ra các thuật toán phức tạp và tinh vi hơn, nhưng hiệu quả hơn, chỉ cần thời gian O(nlogn). Mảng cần được sắp xếp có thể là mảng số nguyên, mảng các số thực, hoặc mảng các xâu ký tự. Trong trường hợp tổng quát, các đối tượng cần được sắp xếp chứa một số thành phần dữ liệu, và ta cần sắp xếp mảng các đối tượng đó theo một thành phần dữ liệu nào đó. Thành phần dữ liệu đó được gọi là khoá sắp xếp. Chẳng hạn, ta có một mảng các đối tượng sinh viên, mỗi sinh viên gồm các thành phần dữ liệu: tên, tuổi, chiều cao,…, và ta muốn sắp xếp các sinh viên theo thứ tự chiều cao tăng, khi đó chiều cao là khoá sắp xếp. Từ đây về sau, ta giả thiết rằng, mảng cần được sắp xếp là mảng các đối tượng có kiểu Item, trong đó Item là cấu trúc sau: struct Item { keyType key; // Khoá sắp xếp // Các trường dữ liệu khác }; Vấn đề sắp xếp bây giờ được phát biểu chính xác như sau. Cho mảng A[0..n-1] chứa n Item, chúng ta cần sắp xếp lại các thành phần của mảng A sao cho: A[0].key <= A[1].key <= .. <= A[n-1].key 3.1 CÁC THUẬT TOÁN SẮP XẾP ĐƠN GIẢN Mục này trình bày các thuật toán sắp xếp đơn giản: sắp xếp lựa chọn (selection sort), sắp xếp xen vào (insertion sort), và sắp xếp nổi bọt (bubble sort). Thời gian chạy của các thuật toán này là O(n2), trong đó n là cỡ của mảng. 3.1.1 Sắp xếp lựa chọn Ý tưởng của phương pháp sắp xếp lựa chọn là như sau: Ta tìm thành phần có khóa nhỏ nhất trên toàn mảng, giả sử đó là A[k]. Trao đổi A[0] với A[k]. Khi đó A[0] là thành phần có khoá nhỏ nhất trong mảng. Giả sử đến bước thứ i ta đã có A[0].key <= A[1].key <= … <= A[i-1]. Bây giờ ta tìm thành phần có khóa nhỏ nhất trong các thành phần từ A[i] tới A[n-1]. Giả thành phần tìm được là A[k], i <= k <= n-1. Lại trao đổi A[i] với A[k], ta có A[0].key <=…<= A[i].key. Lặp lại cho tới khi i = n-1, ta có mảng A được sắp xếp. Ví dụ. Xét mảng A[0…5] các số nguyên. Kết quả thực hiện các bước đã mô tả được cho trong bảng sau A[0] A[1] A[2] A[3] A[4] A[5] I k 5 9 1 8 3 7 0 2 1 9 5 8 3 7 1 4 1 3 5 8 9 7 2 2 1 3 5 8 9 7 3 5 1 3 5 7 9 8 4 5 1 3 5 7 8 9 Sau đây là hàm sắp xếp lựa chọn: void SelectionSort(Item A[] , int n) // Sắp xếp mảng A[0..n-1] với n > 0 { (1) for (int i = 0 ; i < n-1 ; i++) { (2) int k = i; (3) for (int j = i + 1 ; j < n ; j++) (4) if (A[j].key < A[k].key) k = j; (5) swap(A[i],A[k]); } } Trong hàm trên, swap là hàm thực hiện trao đổi giá trị của hai biến. Phân tích sắp xếp lựa chọn. Thân của lệnh lặp (1) là các lệnh (2), (3) và (5). Các lệnh (2) và (5) có thời gian chạy là O(1). Ta đánh giá thời gian chạy của lệnh lặp (3). Số lần lặp là (n-1-i), thời gian thực hiện lệnh (4) là O(1), do đó thời gian chạy của lệnh (3) là (n-1-i)O(1). Như vậy, thân của lệnh lặp (1) có thời gian chạy ở lần lặp thứ i là (n-1-i)O(1). Do đó lệnh lặp (1) đòi hỏi thời gian (n-1-i)O(1) = O(1)(1 + 2 + …+ n-1) = O(1)n(n-1)/2 = O(n2) Vậy thời gian chạy của hàm sắp xếp lựa chọn là O(n2). 3.1.2 Sắp xếp xen vào Phương pháp sắp xếp xen vào là như sau. Giả sử đoạn đầu của mảng A[0..i-1] (với i >= 1) đã được sắp xếp, tức là ta đã có A[0].key <= … <= A[i-1].key. Ta xen A[i] vào vị trí thích hợp trong đoạn đầu A[0..i-1] để nhận được đoạn A[0..i] được sắp xếp. Với i = 1, đoạn đầu chỉ có một thành phần, đương nhiên là đã được sắp. Lặp lại quá trình đã mô tả với i = 2,…,n-1 ta có mảng được sắp. Việc xen A[i] vào vị trí thích hợp trong đoạn đầu A[o..i-1] được tiến hành như sau. Cho chỉ số k chạy từ i, nếu A[k].key < A[k-1].key thì ta trao đổi giá trị của A[k] và A[k-1], rồi giảm k đi 1. Ví dụ. Giả sử ta ta có mảng số nguyên A[0..5] và đoạn đầu A[0..2] đã được sắp 0 1 2 3 4 5 1 4 5 2 9 7 Lúc này i = 3 và k = 3 vì A[3] < A[2], trao đổi A[3] và A[2], ta có 0 1 2 3 4 5 1 4 2 5 9 7 Đến đây k=2, và A[2] < A[1], lại trao đổi A[2] và A[1], ta có 0 1 2 3 4 5 1 2 4 5 9 7 Lúc này k = 1 và A[1] >= A[0] nên ta dừng lại và có đoạn đầu A[0..3] đã được sắp Hàm sắp xếp xen vào được viết như sau: void InsertionSort (Item A[], int n) { (1) for ( int i = 1 ; i < n ; i++) (2) for ( int k = i ; k > 0 ; k--) (3) if (A[k].key < A[k-1].key) swap(A[k],A[k-1]); else break; } Phân tích sắp xếp xen vào Số lần lặp tối đa của lệnh lặp (2) là i, thân của lệnh lặp (2) là lệnh (3) cần thời gian O(1). Do đó thời gian chạy của lệnh (2) là O(1)i. Thời gian thực hiện lệnh lặp (1) là 3.1.3 Sắp xếp nổi bọt Ý tưởng của sắp xếp nổi bọt là như sau. Cho chỉ số k chạy từ 0, 1 , …, n-1, nếu hai thành phần kề nhau không đúng trật tự, tức là A[k].key >A[k+1].key  thì ta trao đổi hai thành phần A[k] và A[k+1]. Làm như vậy ta đẩy được dữ liệu có khoá lớn nhất lên vị trí sau cùng A[n-1]. Ví dụ. Giả sử ta có mảng số nguyên A[0..4]= (6,1,7,3,5).Kết quả thực hiện quá trình trên được cho trong bảng sau: A[0] A[1] A[2] A[3] A[4] 6 1 7 3 5 Trao đổi A[0] và A[1] 1 6 7 3 5 Trao đổi A[2] và A[3] 1 6 3 7 5 Trao đổi A[3] và A[4] 1 6 3 5 7 Lặp lại quá trình trên đối với mảng A[0,…, n-2] để đẩy dữ liệu có khoá lớn nhất lên vị trí A[n-2]. Khi đó ta có A[n-2].key ≤ A[n-1].key. Tiếp tục lặp lại quá trình đã mô tả trên các đoạn đầu A[0..i], với i = n-3, …,1, ta sẽ thu được mảng được sắp . Ta có hàm sắp xếp nổi bọt như sau: void BubbleSort( Item A[] , int n) { (1) for (int i = n-1 ; i > 0 ; i--) (2) for (int k = 0 ; k < i ; k++) (3) if ( A[k].key > A[k+1].key) Swap(A[k],A[k+1]); } Tương tự như hàm sắp xếp xen vào ,ta có thể đánh giá thời gian chạy của hàm sắp xếp nổi bọt là O(n2 ). Trong hàm BubbleSort khi thực hiện lệnh lặp (1), nếu đến chỉ số i nào đó, n-1 ≥ i > 1, mà đoạn đầu A[0..i] đã được s

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

  • doctim hieu ve thuat toan sap xep.doc