Giỏo trỡnh này được viết cho sinh viờn cỏc trường Cao đẳng Đại học
Để học tốt giỏo trỡnh này sinh viờn cần cú kiến thức về tin học cơ sở và đó biết một ngụn ngữ lập trỡnh bậc cao như Pascal, Delphi, C hay C++.
Giỏo trỡnh được viết nhằm những mục tiờu sau đõy:
ó Giới thiệu cho học sinh về khỏi niệm về kiểu dữ liệu trừu tượng.
ó Cung cấp cho học sinh kiến thức cơ bản về những kiểu dữ liệu trừu tượng, cấu trỳc dữ liệu thụng dụng, nõng cao và những thao tỏc trờn cỏc cấu trỳc đú.
ó Cung cấp một số thuật toỏn cơ bản và rốn luyện một số kĩ năng phõn tớch thuật toỏn cho học sinh.
ó Rốn luyện cho học sinh cỏch ỏp dụng cỏc cấu trỳc dữ liệu đó học và tư duy thuật toỏn để cú thể thiết kế và cài đặt một số chương trỡnh bằng một ngụn ngữ bậc cao.
Để cú thể học tốt cỏc chương sau, sinh viờn cần đọc kĩ chương 1 và chương 2. Chương 1 giới thiệu những khỏi niệm cơ bản về thuật giải. Chương 2 giới thiệu những khỏi niệm cơ bản về kiểu dữ liệu trừu tượng. Đõy là khỏi niệm mới đối với học sinh. Một kiểu dữ liệu trừu tượng là một tập hợp cỏc đối tượng và được xỏc định hoàn toàn bởi cỏc phộp toỏn cú thể biểu diễn trờn cỏc đối tượng đú. Người sử dụng được phộp tỡm hiểu cỏc đối tượng và thực hiện cỏc thao tỏc trờn cỏc đối tượng của kiểu dữ liệu thụng qua cỏc phộp toỏn đú mà khụng cần quan tõm đến việc đối tượng được cài như thế nào trong ngụn ngữ lập trỡnh. Chỳng tụi cú đưa ra một vài vớ dụ đơn giản về kiểu dữ liệu trừu tượng, cỏch đặc tả và xõy dựng chỳng để định hướng cho việc đặc tả và xõy dựng những kiểu dữ liệu trừu tượng trong những chương sau.
Trong khi trỡnh bày mỗi kiểu dữ liệu trừu tượng ở mỗi chương sau, chỳng tụi cố gắng khuyến khớch sinh viờn trước hết phải hiểu một cỏch khỏi quỏt về kiểu dữ đú trước khi quan tõm đến việc cài đặt chi tiết bờn trong. Điều đú khụng cú nghĩa là chỳng tụi khụng coi trọng việc cài đặt. Việc tỏch riờng đặc tả bờn ngoài và bờn trong cho phộp ta nhỡn rừ trước hết vào bản chất của một kiểu dữ liệu trừu tượng là tập cỏc thao tỏc trờn cỏc đối tượng của kiểu dữ liệu đú. Và chỉ khi đó hiểu rừ bản chất của cỏc thao tỏc trờn một kiểu dữ liệu trừu tượng chỳng ta mới chuyển tới việc cài đặt những thao tỏc đú bằng một ngụn ngữ bậc cao nào đú. Cụng cụ cho phộp ta búc tỏch một cỏch khỏi niệm hỡnh thức bờn ngoài và chi tiết bờn trong đú chớnh là kiểu dữ liệu trừu tượng.
Từ chương 3 đến chương 6 dành cho việc nghiờn cứu một số kiểu dữ liệu trừu tượng cơ bản tuyến tớnh và phi tuyến. Đú là cỏc kiểu dữ liệu trừu tượng ngăn xếp, hàng đợi, cõy, bảng tỡm kiếm, tập hợp và đồ thị. Với mỗi kiểu dữ liệu trừu tượng đưa ra, chỳng đều được đặc tả bởi cỏc thao tỏc cơ bản và hướng dẫn cài đặt một số thao tỏc. Những thao tỏc đưa ra là những thao tỏc cơ bản nhất. Tuy nhiờn, tuỳ theo điều kiện và hoàn cảnh, học sinh cú thể bổ sung thờm một số những thao tỏc khỏc. Trong việc xõy dựng cỏc kiểu dữ liệu trừu tượng, chỳng tụi rất nhấn mạnh việc quan tõm đầu tiờn là đặc tả được cỏc thao tỏc cần thiết (xõy dựng mụ đun ngoài), sau đú mới đến việc cài đặt cỏc thao tỏc (xõy dựng mụ đun trong). Ngoài ra, chỳng tụi cũng quan tõm đến việc hướng dẫn sử dụng cỏc kiểu dữ liệu trừu tượng thụng qua những vớ dụ về những mụ đun ứng dụng.
Sinh viờn ắt hẳn đó được giới thiệu một vài thuật toỏn sắp xếp đơn giản trờn mảng từ phần Tin học đại cương. Tuy nhiờn, bài toỏn sắp xếp và tỡm kiếm là bài toỏn hết sức cơ bản đối với mỗi sinh viờn Cao đẳng và Đại học nờn chỳng tụi dành riờng chương 7 để núi về một số giải thuật sắp xếp đơn giản cũng như một số giải thuật sắp xếp nõng cao, cần đến một chỳt kiến thức về kiểu dữ liệu trừu tượng vừa học trong cỏc chương trước. Một số phộp so sỏnh cỏc giải thuật sắp xếp cũng được đề cập trong chương này.
Để giỳp học sinh cú thờm một số kiến thức về thuật giải, trong chương 8, chỳng tụi trỡnh bày một số phương phỏp thiết kế thuật giải như đệ qui, quay lui, chia dể trị, tham lam, qui hoạch động. Đõy là những phương phỏp rất cơ bản và thụng dụng trong khoa học mỏy tớnh. Bạn đọc cú thể tỡm thấy nhiều vớ dụ minh hoạ cho những phương phỏp này. Cỏc vớ dụ đều được cài đặt cụ thể bằng ngụn ngữ lập trỡnh Pascal.
Cuối mỗi chương đều cú hệ thống bài tập để học sinh làm và tự kiểm tra kiến thức của mỡnh.
Cuối cựng là phần phụ lục, trong đú chỳng tụi cú đưa ra một số bài tập lớn, để học sinh ỏp dụng những kiến thức đó học tập giải quyết những vấn đề phức tạp hơn so với bài tập sau mỗi chương. Với những bài tập lớn này, để nõng cao hiệu quả, yờu cầu học sinh cần đặc tả rừ bài toỏn, phõn tớch và thiết kế thuật giải, sau đú cài đặt trờn một ngụn ngữ cụ thể. Tiếp theo học sinh cần phải kiểm thử phần mềm mỡnh vừa cài đặt và cú những nhận xột về độ phức tạp của thuật toỏn mỡnh thiết kế, về cỏc hướng cần mở rộng cho cỏc bài tập này. Chỉ khi nào học sinh làm tốt cỏc bài tập lớn đú mới cú thể núi là họ đó thành cụng trong việc học tập mụn học này.
Chủ biờn và hiệu đớnh toàn bộ nội dung bản thảo:
Chương 1: Mở đầu
chương 2: .
Chương 3: .
Chương 4,
Chương 6:
Chương 7:
Chương 8:
380 trang |
Chia sẻ: oanh_nt | Lượt xem: 1259 | Lượt tải: 1
Bạn đang xem trước 20 trang nội dung tài liệu Tài liệu tham khảo ngôn ngữ Pascal, để xem tài liệu hoàn chỉnh bạn click vào nút DOWNLOAD ở trên
Lời nói đầu
Giáo trình này được viết cho sinh viên các trường Cao đẳng Đại học
Để học tốt giáo trình này sinh viên cần có kiến thức về tin học cơ sở và đã biết một ngôn ngữ lập trình bậc cao như Pascal, Delphi, C hay C++...
Giáo trình được viết nhằm những mục tiêu sau đây:
Giới thiệu cho học sinh về khái niệm về kiểu dữ liệu trừu tượng.
Cung cấp cho học sinh kiến thức cơ bản về những kiểu dữ liệu trừu tượng, cấu trúc dữ liệu thông dụng, nâng cao và những thao tác trên các cấu trúc đó.
Cung cấp một số thuật toán cơ bản và rèn luyện một số kĩ năng phân tích thuật toán cho học sinh.
Rèn luyện cho học sinh cách áp dụng các cấu trúc dữ liệu đã học và tư duy thuật toán để có thể thiết kế và cài đặt một số chương trình bằng một ngôn ngữ bậc cao.
Để có thể học tốt các chương sau, sinh viên cần đọc kĩ chương 1 và chương 2. Chương 1 giới thiệu những khái niệm cơ bản về thuật giải. Chương 2 giới thiệu những khái niệm cơ bản về kiểu dữ liệu trừu tượng. Đây là khái niệm mới đối với học sinh. Một kiểu dữ liệu trừu tượng là một tập hợp các đối tượng và được xác định hoàn toàn bởi các phép toán có thể biểu diễn trên các đối tượng đó. Người sử dụng được phép tìm hiểu các đối tượng và thực hiện các thao tác trên các đối tượng của kiểu dữ liệu thông qua các phép toán đó mà không cần quan tâm đến việc đối tượng được cài như thế nào trong ngôn ngữ lập trình. Chúng tôi có đưa ra một vài ví dụ đơn giản về kiểu dữ liệu trừu tượng, cách đặc tả và xây dựng chúng để định hướng cho việc đặc tả và xây dựng những kiểu dữ liệu trừu tượng trong những chương sau.
Trong khi trình bày mỗi kiểu dữ liệu trừu tượng ở mỗi chương sau, chúng tôi cố gắng khuyến khích sinh viên trước hết phải hiểu một cách khái quát về kiểu dữ đó trước khi quan tâm đến việc cài đặt chi tiết bên trong. Điều đó không có nghĩa là chúng tôi không coi trọng việc cài đặt. Việc tách riêng đặc tả bên ngoài và bên trong cho phép ta nhìn rõ trước hết vào bản chất của một kiểu dữ liệu trừu tượng là tập các thao tác trên các đối tượng của kiểu dữ liệu đó. Và chỉ khi đã hiểu rõ bản chất của các thao tác trên một kiểu dữ liệu trừu tượng chúng ta mới chuyển tới việc cài đặt những thao tác đó bằng một ngôn ngữ bậc cao nào đó. Công cụ cho phép ta bóc tách một cách khái niệm hình thức bên ngoài và chi tiết bên trong đó chính là kiểu dữ liệu trừu tượng.
Từ chương 3 đến chương 6 dành cho việc nghiên cứu một số kiểu dữ liệu trừu tượng cơ bản tuyến tính và phi tuyến. Đó là các kiểu dữ liệu trừu tượng ngăn xếp, hàng đợi, cây, bảng tìm kiếm, tập hợp và đồ thị... Với mỗi kiểu dữ liệu trừu tượng đưa ra, chúng đều được đặc tả bởi các thao tác cơ bản và hướng dẫn cài đặt một số thao tác. Những thao tác đưa ra là những thao tác cơ bản nhất. Tuy nhiên, tuỳ theo điều kiện và hoàn cảnh, học sinh có thể bổ sung thêm một số những thao tác khác. Trong việc xây dựng các kiểu dữ liệu trừu tượng, chúng tôi rất nhấn mạnh việc quan tâm đầu tiên là đặc tả được các thao tác cần thiết (xây dựng mô đun ngoài), sau đó mới đến việc cài đặt các thao tác (xây dựng mô đun trong). Ngoài ra, chúng tôi cũng quan tâm đến việc hướng dẫn sử dụng các kiểu dữ liệu trừu tượng thông qua những ví dụ về những mô đun ứng dụng.
Sinh viên ắt hẳn đã được giới thiệu một vài thuật toán sắp xếp đơn giản trên mảng từ phần Tin học đại cương. Tuy nhiên, bài toán sắp xếp và tìm kiếm là bài toán hết sức cơ bản đối với mỗi sinh viên Cao đẳng và Đại học nên chúng tôi dành riêng chương 7 để nói về một số giải thuật sắp xếp đơn giản cũng như một số giải thuật sắp xếp nâng cao, cần đến một chút kiến thức về kiểu dữ liệu trừu tượng vừa học trong các chương trước. Một số phép so sánh các giải thuật sắp xếp cũng được đề cập trong chương này.
Để giúp học sinh có thêm một số kiến thức về thuật giải, trong chương 8, chúng tôi trình bày một số phương pháp thiết kế thuật giải như đệ qui, quay lui, chia dể trị, tham lam, qui hoạch động. Đây là những phương pháp rất cơ bản và thông dụng trong khoa học máy tính. Bạn đọc có thể tìm thấy nhiều ví dụ minh hoạ cho những phương pháp này. Các ví dụ đều được cài đặt cụ thể bằng ngôn ngữ lập trình Pascal.
Cuối mỗi chương đều có hệ thống bài tập để học sinh làm và tự kiểm tra kiến thức của mình.
Cuối cùng là phần phụ lục, trong đó chúng tôi có đưa ra một số bài tập lớn, để học sinh áp dụng những kiến thức đã học tập giải quyết những vấn đề phức tạp hơn so với bài tập sau mỗi chương. Với những bài tập lớn này, để nâng cao hiệu quả, yêu cầu học sinh cần đặc tả rõ bài toán, phân tích và thiết kế thuật giải, sau đó cài đặt trên một ngôn ngữ cụ thể. Tiếp theo học sinh cần phải kiểm thử phần mềm mình vừa cài đặt và có những nhận xét về độ phức tạp của thuật toán mình thiết kế, về các hướng cần mở rộng cho các bài tập này. Chỉ khi nào học sinh làm tốt các bài tập lớn đó mới có thể nói là họ đã thành công trong việc học tập môn học này.
Chủ biên và hiệu đính toàn bộ nội dung bản thảo:
Chương 1: Mở đầu
chương 2: .
Chương 3: .
Chương 4,
Chương 6:
Chương 7:
Chương 8:
Chương I: Mở đầu
Chương này trình bày một số khái niệm cơ bản đầu tiên về bài toán theo quan điểm Tin học, thuật giải, cấu trúc dữ liệu, mối quan hệ giữa cấu trúc dữ liệu và thuật giải. Những khái niệm về độ phức tạp của thuật giải và phân tích thuật giải cũng được giới thiệu trong chương.
Khái niệm về cấu trúc dữ liệu và thuật giải
1.1.1. Khái niệm bài toán
Trong phạm vi Tin học, ta có thể quan niệm bài toán là bất cứ việc gì ta muốn máy tính thực hiện. Như vậy những việc như viết một dòng chữ ra màn hình, giải phương trình bậc hai, giải hệ phương trình bậc nhất hai ẩn số, quản lý các cán bộ trong một cơ quan... đều là các ví dụ về bài toán.
Khi dùng máy tính giải bài toán, ta chỉ cần quan tâm đến hai yếu tố: đưa vào máy thông tin gì (thông tin vào) và cần lấy ra thông tin gì (thông tin ra), Thông tin vào còn được gọi là Input, thông tin ra còn được gọi là Output. Như vậy để phát biểu một bài toán, ta chỉ cần đặc tả Input và Output của bài toán.
Ví dụ 1. Bài toán tìm ước chung lớn nhất
Input. Hai số nguyên dương M và N
Output. Ước chung lớn nhất của M và N
Ví dụ 2. Bài toán tìm nghiệm của đa thức một biến:
Input: số nguyên dương n; các số thực a0, a1, . . ., an;
Output: Trả lời câu hỏi có bao nhiêu số thực x khác nhau và giá trị của chúng (nếu có) sao cho
a0 + a1x +...+ anxn = 0
Ví dụ 3. Bài toán phân tích số:
Input: Số nguyên dương N³2;
Output: Các số nguyên tố p1,..., pk; các số nguyên dương a1,...,ak; sao cho
n = p1a1...pkak;
Ví dụ 4. Bài toán kiểm tra tính nguyên tố:
Input: Số nguyên dương n;
Output: Trả lời câu hỏi n là số nguyên tố hay không?.
Ví dụ 5. Bài toán quản lý hồ sơ cán bộ:
Input: Hồ sơ gốc của các cán bộ trong cơ quan;
Output: Những biểu bảng thống kê, phân loại cán bộ, các quy trình lưu trữ, quản lý hồ sơ cán bộ.
Ví dụ 6. Bài toán thứ 10 của Hilbert:
Input: P là đa thức (nhiều ẩn) với hệ số nguyên;
Output: Trả lời câu hỏi P có nghiệm nguyên hay không?
Qua các ví dụ trên, ta thấy các bài toán được cấu tạo bởi hai thành phần cơ bản:
- Thông tin vào (input): Cho ta biết những thông tin đã có (các giả thiết);
- Thông tin ra (output): Các thông tin cần tìm từ input (kết luận);
1.1.2. Khái niệm thuật giải
Như vậy trong giáo trình này, việc cho một bài toán có nghĩa là cho input và mô tả output cần tìm. Vấn đề đặt ra là thế nào là giải một bài toán?
Trước hết cần lưu ý rằng trong toán học có một xu hướng nghiên cứu định tính các bài toán. Theo xu hướng này, khi xem xét các bài toán, người ta chỉ cần chứng tỏ sự tồn tại của output khi cho input và nếu có thể, xét xem có bao nhiêu "lời giải" và nghiên cứu dáng điệu của chúng. Trong các nghiên cứu như vậy, nhiều khi ta không cần tìm ra lời giải một cách tường minh nhưng bằng cách dùng các công cụ toán học khác nhau một cách thích hợp ta vẫn có thể chứng minh chặt chẽ các điều khẳng định liên quan đến lời giải. Mục tiêu của xu hướng nghiên cứu này là chỉ ra điều kiện tồn tại và nếu có thể, sự duy nhất của lời giải. Sự tồn tại lời giải theo nghĩa này không luôn cho ta cách thức thực tế để tìm ra lời giải.
Trong khuôn khổ của Tin học, việc giải bài toán có nghĩa trao được cho máy tính cách giải. Để giao bài toán cho máy tính, ta cần hướng dẫn cho máy các thao tác mà về nguyên tắc máy có thể thực hiện được. Một cách giải bài toán như vậy được gọi là một thuật giải (còn gọi là thuật toán hay giải thuật).
Nói một cách đầy đủ hơn,
Thuật giải A để giải bài toán P là một dãy hữu hạn các thao tác đơn giản được sắp xếp theo một trình tự xác định sao cho sau khi thực hiện dãy thao tác đó, từ Input của bài toán, ta nhận được Output cần tìm.
Chính quan niệm về việc giải bài toán như vậy là cốt lõi để ta có thể chuyển giao công việc đó cho máy tính. Như vậy, khác với quan niệm về việc giải một bài toán theo toán học truyền thống, việc giải bài toán theo quan niệm của Tin học là quá trình thực hiện một dãy hữu hạn các thao tác đơn giản để có thể từ Input dẫn ra Output một cách tường minh.
Trong định nghĩa nêu trên, thao tác đơn giản được hiểu là thao tác mà máy tính có thể thực hiện được. Ví dụ các phép toán số học, các phép so sánh hai giá trị số là các thao tác đơn giản.
Mô tả thuật giải
Ngay từ khi học môn Tin học cơ sở học sinh đã được làm quen với khái niệm thuật giải và được học cách mô tả hay diễn đạt những thuật giải đơn giản. Thuật giải cho những bài toán nhỏ hay thuật giải cho những bài toán lớn khi được thiết kế ở mức thô hay được mô tả bằng sơ đồ khối. Cách dùng sơ đồ khối để minh họa thuật giải có lợi thế là rất trực quan, dễ hiểu. Tuy nhiên, với những bài toán lớn, phức tạp, việc dùng sơ đồ khối để mô tả thuật giải có những hạn chế nhất định. Hạn chế này là có thể là do khuôn khổ giấy hay màn hình ta dùng để vẽ sơ đồ hay tầm bao quát, theo dõi của mắt ta trên sơ đồ. Hạn chế này cũng nảy sinh khi bài toán của ta phức tạp, có nhiều vòng lặp lồng nhau, khi đó việc trình bày sơ đồ có rất nhiều hạn chế.
Ta cũng hay dùng phương pháp liệt kê các bước giải để mô tả thuật giải. Phương pháp này cũng rất dễ hiểu đối với các bài toán nhỏ. Tuy nhiên, đối với các bài toán lớn, việc liệt kê sẽ trở nên khó khăn và nếu được thì cũng gây khó khó theo dõi, nhất là đối với vòng lặp. Hơn thế nữa việc liệt kê các bước giải ta hay phải dùng ngôn ngữ tự nhiên để mô tả các thao tác nên việc diễn đạt nhiều khi rất khó chính xác.
Phương pháp thứ ba hay được dùng để mô tả thuật giải là dùng giả mã hay ngôn ngữ phỏng trình. Việc dùng ngôn ngữ phỏng trình khắc phục được những nhiều nhược điểm của hai phương pháp trên, đặc biệt đối với những bài toán lớn, phức tạp. Cũng có ý kiến là ta nên chọn một ngôn ngữ lập trình thông dụng làm công cụ để diễn đạt thuật giải. Tuy nhiên, ai cũng biết là thuật giải cần phải độc lập với ngôn ngữ lập trình. Khi thuật giải được viết tốt nó có thể được cài đặt bằng bất cứ ngôn ngữ lập trình nào, tùy theo tính thích hợp của bài toán và ý muốn của người lập trình. Hơn thế nữa, thiết kế thuật giải là một trong những công việc quan trọng nhất của người lập trình. Nếu ta chọn một ngôn ngữ lập trình để mô tả thuật giải thì lúc nào ta cũng phải chú ý tuân thủ các tiểu tiết cú pháp của ngôn ngữ. Điều đó sẽ gây mất thì giờ và ảnh hưởng lớn đến sự tập trung vào công việc chính là thiết kế và mô tả thuật giải.
t ơ m
m ơ n mod m
n ơ t
False
True
Begin
End
m>0
Write n
Sau đây là ví dụ về mô tả thuật giải tìm ước chung lớn nhất của hai số nguyên m và n.
Cách 1: Dùng sơ đồ khối
Hình 1.1: Sơ đồ khối diễn tả thuật giải tìm ước chung lớn nhất của hai số nguyên không âm
--------------------------------------
Cách thứ hai: Dùng phương pháp liệt kê các bước giải, ta có thể viết như sau:.
Bước 1. Khi m > 0 thực hiện
t ơ m
m ơ n mod m
n ơ t
Bước 2. Kiểm tra xem nếu m > 0, quay lại bước 1
Bước 3. In kết quả là n và kết thúc
Cách thứ ba: Dùng ngôn ngữ phỏng trình.
Function Ucln(m,n)
var t
Begin
While m > 0 do
t ơ m
m ơ n mod m
n ơ t
Return n
-----------------------------
Trong thực tế, tùy theo từng giai đoạn thiết kế thuật giải, tùy theo mức độ, và đối tượng trình bày mà người lập trình thường hay chọn phương pháp thích hợp nhất trong ba phương tiện nêu trên.
Như vậy, việc diễn tả một thuật giải giải một bài toán nào đó có nghĩa là trình bày trình tự các thao tác cần thực hiện. Tuy nhiên, đó chỉ là cách diễn tả thuật giải giữa người với người.
Ngay khi thuật giải đã được diễn tả như vậy, con người có thể dùng để giải bài toán với mỗi bộ dữ liệu vào. Tuy nhiên, theo cách diễn tả này, máy tính vẫn chưa thể thực hiện các thao tác trong thuật giải dù các thao tác đó rất đơn giản. Thuật giải cần được diễn tả thành một chương trình gồm những dòng lệnh. Mỗi lệnh là một việc nào đó mà ta đòi hỏi máy tính thực hiện.
Ngôn ngữ dùng để viết chương trình được gọi là ngôn ngữ lập trình.
Phần đọc thêm
Khái niệm thuật giải trình bày ở trên đủ để ta có thể hình dung để có thể chuyển giao cho máy tính việc giải một bài toán ta cần diễn tả cách giải như thế nào và cũng là cách nhận biết thế nào là thuật giải đối với một bài toán nào đó.
Trong quá trình nghiên cứu cách giải bài toán theo nghĩa nêu trên,trong toán học đã diễn ra một quá trình phát triển đầy kịch tính. Cho tới đầu thế kỷ 20, với lòng tin tiên nghiệm vào việc mọi bài toán được phát biểu đúng đắn đều có thuật giải giải, nhiều nhà toán học đầy tài năng và tâm huyết đã tiến công vào các bài toán đang tồn tại như những lời thách đố trí tuệ con người.
Vấn đề then chốt nhất của lý thuyết tính toán phát sinh ở đây. Để chứng minh một bài toán nào đó có thuật giải giải nó, ta chỉ cần đề ra một thuật giải theo quan niệm trực giác của khái niệm này và kiểm định tính đúng đắn của nó đối với bài toán đã cho. Tuy nhiên, khi cần chứng minh việc không có thuật giải giải một bài toán nào đó, quan niệm trực quan về thuật giải không thể là cơ sở bảo đảm cho một chứng minh toán học chặt chẽ được. Do đó, muốn chứng minh những điều khẳng định "không có thuật giải giải một bài toán nào dó", ta cần có một định nghĩa toán học cho khái niệm thuật giải.
Vào khoảng những năm 1930-1936, lần lượt các nhà toán học K.Gõdel, S. Kleene, A.Church, A.Turing đã đề ra một số định nghĩa khác nhau cho khái niệm thuật giải. Trong số các định nghĩa toán học khác nhau (nhưng tương đương) về thuật giải, các khái niệm Máy Turing (1936) và Hàm đệ quy (1931-1936) được sử dụng rộng rãi hơn vì có nhiều thuận tiện cho các nghiên cứu cả về lý thuyết lẫn thực hành. Ta có thể xem máy Turing là một mô hình toán học của máy tính.
Chính nhờ có được định nghĩa toán học về thuật giải, người ta đã chứng minh được có những bài toán không có thuật giải giải. Chẳng hạn, bài toán thứ 10 của Hilbert (Ví dụ 5 nêu trên) không có thuật giải đểgiải.
1.1.4. Một số ví dụ về thuật giải
Ví dụ 1. Một thuật giải tìm ước chung lớn nhất (viết tắt là UC) của hai số nguyên dương M và N.(Bạn đọc có thể thấy sự khác nhau chút ít giữa thuật toán này và thuật toán trình bày ở phần trên).
Bước 1. Nếu M=N thì UC=M và kết thúc, nếu không thì chuyển đến bước 2.
Bước 2. Nếu M<N thì thay N bằng N-M và chuyển đến bước 1, nếu không thì thay M bằng M-N và chuyển đến bước 1.
Dãy thao tác trong thuật giải diễn ra trong nhiều nhất Max(M,N) bước.
Tuy nhiên, với cách diễn tả thuật giải trên, máy chưa có khả năng trực tiếp thực hiện. Ta cần diễn tả thuật giải đó bằng một ngôn ngữ mà máy tính có thể “hiểu” và thực hiện được. Cách diễn tả một thuật giải như vậy được gọi là một chương trình, ngôn ngữ dùng để viết chương trình được gọi là ngôn ngữ lập trình.
Ví dụ 2. Thuật giải tìm giá trị lớn nhất và nhỏ nhất của một dãy số
Ta xét bài toán sau:
Input: Số nguyên dương n và dãy n số a1, . . ., an.
Output: Giá trị lớn nhất Max và giá trị nhỏ nhất Min của dãy số
Sau đây là thuật giải đối với bài toán này:
Bước 1. Đặt Max= a1, Min= a1, i=2.
Bước 2. Nếu i<N thì thực hiện bước 3, nếu không thì kết thúc.
Bước 3
3.1. Nếu ai>Max thì Max= ai.
3.2. Nếu ai<Min thì Min= ai.
Tăng i một đơn vị và quay về bước 2.
Rõ ràng số thao tác cần tiến hành không quá n.
Ví dụ 3. Thuật giải để sắp xếp một dãy số cho trước thành một dãy đơn điệu không tăng.
Ta xét bài toán sau:
Input: Số nguyên dương n và dãy n số a1, . . ., an.
Output: Dãy số trên được sắp xếp lại thành một dãy số không tăng tức là số hạng trước lớn hơn hay bằng số hạng sau.
Một thuật giải giải bài toán này có thể nói vắn tắt như sau: với mỗi chỉ số i, 1ÊiÊn-1, xét các chỉ số j đứng sau i, i+1ÊjÊn, nếu ai<aj thì ta tráo đổi hai số hạng này cho nhau.
Một cách cụ thể hơn:
Bước 1. i=1;
Bước 2. Nếu i=n thì kết thúc, nếu không ( tức là i<n) thì
Bước 2.1. j=i+1;
Bước 2.2. Nếu (jÊn) và (ai<aj) thì tráo đổi giá trị của hai số này
Bước 2.3. Nếu j<n thì (tăng J một đơn vị và quay lại Bước 2.2), nếu không ( tức là j=n), chuyển sang bước 3.
Bước 3. Tăng i một đơn vị và quay lại Bước 2.
Rõ ràng số bước thao tác cần tiến hành không quá n(n+1).
1.1.5 Khái niệm về cấu trúc dữ liệu và mối quan hệ với thuật giải
ở trên ta đã nêu rõ khái niệm thuật giải. Đối tượng của thuật giải chính là dữ liệu hay thuật giải bao giờ cũng phải tác động lên dữ liệu để có kết quả của bài toán. Một tập dữ liệu có thể gồm những phần tử rời rạc, chẳng có quan hệ gì với nhau, nhưng cũng có thể gồm những phần tử có quan hệ, ràng buộc nhất định. Khi những thành phần của tập dữ liệu có mối quan hệ với nhau, chúng cần được tổ chức theo những phương thức nhất định thành những cấu trúc dữ liệu để thuật giải tác động lên đó được hiệu quả.
Trong giáo trình Tin học cơ sở, ta đã làm quen với một số cấu trúc dữ liệu đơn giản như bản ghi, mảng...Trong giáo trình này ta sẽ lần lượt nghiên cứu những cấu trúc dữ liệu phức tạp hơn cả tuyến tính và phi tuyến tính.
Khi lập chương trình cho máy tính, công việc tổ chức dữ liệu và thiết kế những thuật giải tương thích, hiệu quả là rất quan trọng. Chính vì thế cấu trúc dữ liệu và thuật giải là hai thành tố quan trọng của chương trình máy tính và đều là những đối tượng nghiên cứu quan trọng của khoa học máy tính. Hơn nữa, chúng có quan hệ mật thiết với nhau, ảnh hưởng và điều phối lẫn nhau để tạo ra lời giải cho bài toán. Trong phạm vi của giáo trình này, ta nghiên cứu những cấu trúc dữ liệu cơ bản và những thuật giải hay thao tác thường thực hiện trên các cấu trúc dữ liệu đó.
1.2. Khái niệm về độ phức tạp của thuật giải
Mỗi thuật giải chỉ giải một bài toán nào đó nhưng có thể có nhiều thuật giải khác nhau giải cùng một bài toán. Ví dụ, để sắp xếp một dãy số bất kỳ thành một dãy đơn điệu, có rất nhiều thuật giải khác nhau.
Cùng một bài toán, có thể có nhiều thuật giải khác nhau. Việc lựa chọn một thuật giải thích hợp nhất hay tốt nhất cho một bài toán luôn là mối quan tâm của người lập trình. Vậy làm thế nào để chọn được thuật giải thích hợp nhất trong những thuật giải của một bài toán? Nói cụ thể hơn, những tiêu chí nào là quan trọng để đánh giá một thuật giải? Thuật giải càng trong sáng, có cấu trúc tốt thì mã hóa càng nhanh, bảo trì chương trình càng dễ dàng và do đó giảm được giá thành của phần mềm. Tuy nhiên, có những thuật giải rất trong sáng, dễ hiểu nhưng lại không khả thi về mặt thời gian thực hiện hay không gian nhớ cần thiết khi thực hiện nó. Vậy làm thế nào để đánh giá được thời gian thực hiện và không gian nhớ dành cho một thuật giải? Cách đơn giản nhất là ta cài đặt thuật giải rồi cho máy chạy chưong trình với một tập dữ liệu vào nào đó và đo thời gian thực hiện chương trình cũng như tính không gian nhớ cần thiết cho nó và ta có kết quả về thời gian và không gian cần thiết cho thuật giải. Tuy nhiên, cách tính này chỉ mới áp dụng trên một tập dữ liệu đầu vào cụ thể. Sẽ là không thích hợp nếu ta dùng kết quả này để áp dụng chung cho các tập dữ liệu đầu vào khác. Nếu bài toán đơn giản và đầu vào chỉ gồm một số ít phần tử thì ta cũng chẳng cần quan tâm nhiều đến việc lựa chọn thuật giải làm gì. Ví dụ như bài toán sau: Ta cần chọn ra số lớn nhất trong 3 số nguyên hay thậm chí trong vài chục số nguyên đã cho. Bạn có thể dùng bất cứ thuật giải nào bạn nghĩ ra hay bạn thích, miễn là nó đúng, có lẽ thời gian thực hiện chúng cũng không khác nhau bao nhiêu. Một thuật toán tìm kiếm tuần tự để tìm một số điện thoại trong một danh sách điện thoại gồm 50 số thì có thể chấp nhận được nhưng nếu áp dụng thuật toán đó cho một danh sách gồm hàng chục ngàn số điện thoại trong một thành phố, hẳn là không thể chấp nhận được.
Như vậy, vấn đề đặt ra rất tự nhiên là khi giải một bài toán, ta muốn chọn một thuật giải tốt. Chúng ta cần tìm ra một phương pháp nào đó để có thể cho phép ta đánh giá được thuật giải này tốt hơn thuật giải kia với một tập dữ liệu vào bất kì. Ví dụ, khi một danh bạ điện thoại gồm từ một trăm số điện thoại trở lên, cách tổ chức thành thư mục và việc tìm kiếm theo thư mục chắc chắn sẽ giúp ta tìm kiếm tốt hơn là việc tìm kiếm tuần tự.
Nhưng thế nào là thuật giải tốt. Đây là nội dung nghiên cứu của lý thuyết độ phức tạp tính toán. Lý thuyết này có thể được xây dựng trên một nền tảng toán học chặt chẽ từ một hệ tiên đề. Tuy nhiên, trong phạm vi giáo trình này, chúng tôi chỉ giới thiệu một số vấn đề đơn giản .
Khi dùng máy tính thực hiện một chương trình thể hiện một thuật giải nào đó, hệ điều hành cần cung cấp cho chương trình đó các tài nguyên như giờ CPU, bộ nhớ, . . . Độ phức tạp của một thuật giải được đo bằng số lượng các tài nguyên cần dùng để thực hiện chương trình đó. Khi so sánh hai thuật giải cùng giải một bài toán, một thuật giải này được xem là tốt hơn thuật giải kia nếu nó dùng ít tài nguyên hơn.
Trong các tài nguyên cần dùng để chạy chương trình, hiện nay người ta quan tâm nhiều nhất đến thời gian vì đó là dạng tài nguyên không tái tạo được. Một thuật giải được xem là tốt nếu chương trình tương ứng chạy nhanh hay chính xác hơn, chạy trong thời gian chấp nhận được. Do đó, để đánh giá độ phức tạp của một thuật giải, ta thường ước tính số thao tác cơ bản cần dùng để thực hiện thuật giải. Thao tác cơ bản của một thuật giải là thao tác mà số lần thựchiện nó không ít hơn số lần thực hiện các thao tác khác.
Ví dụ về thao tác cơ bản:
Bài toán
Thao tác cơ bản
Tìm phần tử x trong một danh sách
So sánh x với một phần tử của danh sách
Nhân hai ma trận thực
Phép nhân hai số thực, phép cộng hai số thực
Sắp xếp một danh sách
So sánh hai phần tử của danh sách
Với mỗi bài toán, ta xét kích thước đầu vào hay gọi đơn giản là kích thước của bài toán. Kích thước đầu vào một bài toán được đo bằng số lượng dữ liệu vào cần xử lý. Ví dụ nếu dữ liệu vào là một dãy số có N số hạng thì kích thước bài toán dược xem bằng N, nếu dữ liệu vào là một đồ thị có N đỉnh, kích thước bài toán bằng N, nếu dữ liệu vào là một xâu ký tự độ dài L thì kích thước bài toán bằng L, nếu dữ liệu vào là một bảng có M dòng và N cột thì kích thước bài toán bằng MxN. . .
Khi đó, số các thao tác cơ bản mà một thuật giải sẽ được biểu thị như một hàm f(K) của biến số K là kích thước của bài toán. Khi đánh giá hàm f(K) người ta thường dùng ký hiệu O hay chính xác hơn là khái niệm W của giải tích có nghĩa là cùng bậc. Ví dụ, giải thuật tìm Min-Max của một dãy số trong Mục 8.1 có độ phức tạp 2N, ta nói độ phức tạp đó cỡ W(N) có nghĩa là cùng bậc với N khi N tiến đến vô cùng; tương tự, giải thuật sắp xếp dãy số có độ phức tạp cở W(N2).
Việc dùng ký hiệu W để thể hiện độ phức tạp của giải thuật cho ta một đánh giá tổng thể không bị sa vào những tính toán tỉ mỉ hơn nhưng không thật cần thiết. Ví dụ, cùng là hai phép toán cộng và nhân hai số, rõ ràng phép cộng thực hiện nhanh hơn nhiều so với phép nhân nhưng sự khác biệt đó chỉ thể hiện bằng việc thời gian tính toán sai khác một hằng số nhân. Do đó, nếu dùng ký hiệu W, ta có thể bỏ qua sự khác biệt đó.
Khi nói về độ phức tạp về thời gian, có một số bậc được sử dụng thường xuyên và người ta đã đặt tên cho chúng. Một cách vắn tắt, nếu một thuật toán có độ phức tạp là t và t n0 và c là một hằng số dương, còn n là kích cỡ đầu vào thì người ta gọi thuật toán đó có độ phức tạp tuyến tính. Nếu t < cn2 thì t được gọi là có độ phức tạp bình phương. Tương tự t được gọi là có độ phức tạp lập phương, đa thức hay mũ nếu nó có bậc lần lượt của các hàm n3, nk hay an , với k và a là những hằng số nào đó.
Để dễ hình dung về sự tăng của một số hàm với n là kích thước của bài toán, dưới đây ta phác họa một số đồ thị của độ phức tạp loga, độ phức tạp tuyến tính, độ phức tạp bình phương, độ phức tạp lập phương và độ phức tạp mũ.
O(log(n)
O(n)
O(n2)
O(n3)
O(an)
Hình 2.1: Phác hoạ các đường biểu diễn độ phức tạp của một số thuật giải
-----------------------------------------
1.3. Phân tích thuật toán
1.3.1. Khái niệm về phân tích thuật toán
Phân tích một giải thuật có nghĩa là đánh giá độ phức tạp của giải thuật đó.
Thực ra không có một cẩm nang để phân tích thuật toán mà chủ yếu là ta sử dụng kiến thức toán học, sự trực quan, suy luận, và một số kĩ thuật cơ bản để tính độ phức tạp của một thuật giải.
Có một số cấu trúc phổ biến trong các thuật giải: cấu trúc tuần tự, cấu trúc lặp và cấu trúc đệ quy.
Với cấu trúc tuần tự: Nếu ta có hai đoạn trình thực hiện tuần tự là P1 và P2 , độ phức tạp của chúng lần lượt là t1 = O(f(n)), t2 = O(g(n)) thì
Các file đính kèm theo tài liệu này:
- 216_tai_lieu_tham_khao_mon_hoc.doc