Tài liệu Thiết kế và đánh giá thuật toán được biên soạn nhằm phục vụ công việc giảng dạy và học tập môn học Thiết kế và đánh giá thuật toán của ngành học Khoa học máy tính thuộc khoa Công nghệ thông tin trường Đại học sư phạm kỹ thuật Nam Định. Tài liệu cũng rất cần thiết cho tất cả các ngành học thuộc khoa Công nghệ thông tin. Mời các bạn cùng tham khảo phần 1 bài giảng Thiết kế và đánh giá thuật toán dưới đây.
Trang 1BỘ LAO ĐỘNG - THƯƠNG BINH VÀ XÃ HỘI TRƯỜNG ĐẠI HỌC SƯ PHẠM KỸ THUẬT NAM ĐỊNH
Trang 2Lời nói đầu
Xây dựng một thuật toán tốt để giải bài bài toán đã cho là bước quan trọng nhất trong việc giải bài toán đó trên máy tính điện tử Để có được một thuật một thuật toán tốt cần phải nắm vững các kỹ thuật thiết kế, phân tích, đánh giá thuật toán cùng các thuật toán cơ bản cho một số lớp bài bài toán điển hình
Tài liệu Thiết kế và đánh giá thuật toán được biên soạn nhằm phục vụ công việc
giảng dạy và học tập môn học Thiết kế và đánh giá thuật toán của ngành học Khoa học máy tính thuộc khoa Công nghệ thông tin trường Đại học sư phạm kỹ thuật Nam Định Tài liệu cũng rất cần thiết cho tất cả các ngành học thuộc khoa Công nghệ thông tin
Nội dung của tài liệu trình bày các kỹ thuật thiết kế thuật toán thông dụng và cơ
sở phân tích, đánh giá độ phức tạp của thuật toán Tài liệu gồm 6 chương:
Chương 1: Tổng quan về thiết kế và đánh giá thuật toán
Chương 2: Kỹ thuật chia để trị
Chương 3: Kỹ thuật tham lam
Chương 4: Kỹ thuật quay lui
Chương 5: Kỹ thuật nhánh và cận
Chương 6: Kỹ thuật quy hoạch động
Trong từng chương các vấn đề đưa ra đều được minh họa bằng các ví dụ Cuối mỗi chương đều có một hệ thống các bài tập nhằm giúp người học củng cố các kiến thức đã được học đồng thời rèn luyện khả năng vận dụng các kiến thức để giải quyết một số bài toán trong thực tế Với các bài tập khó tài liệu đã đưa ra hướng dẫn giải để giúp người học thuận lợi trong qua trình nghiên cứu và giải quyết các bài tập Cuối tài liệu là phần cài đặt một số thuật toán đã được thiết kế nhằm giúp người học thuận lợi hơn trong việc nắm bắt và vận dụng các kỹ thuật thiết kế thuật toán
Tài liệu được biên soạn theo chương trình môn học Thiết kế và đánh giá thuật toán của ngành học Khoa học máy tính thuộc khoa Công nghệ thông tin trường Đại học
sư phạm kỹ thuật Nam Định Nội dung tài liệu được biên soạn dựa trên cơ sở nội dung các bài giảng của tác giả trong một số năm qua tại khoa Công nghệ thông tin trường Đại học sư phạm kỹ thuật Nam Định
Trong quá trình biên soạn, tác giả đã nhận được nhiều ý kiến đóng góp cùng với
sự động viên, khích lệ của bạn bè đồng nghiệp trong khoa và trong trường Tác giả xin được tỏ lòng cảm ơn với những ý kiến đóng góp và động viên khích lệ này
Trang 3Với lần biên soạn đầu tiên, mặc dù đã hết sức cố gắng song chắc chắn tài liệu không thể tránh khỏi những thiếu sót Rất mong nhận được các ý kiến đóng góp để tài liệu ngày càng hoàn thiện hơn
Phạm Cao Hào
Trang 4MỤC LỤC
Chương 1 Tổng quan về thiết kế và đánh giá thuật toán 1
1.1 Thuật toán 1
1.1.1 Khái niệm thuật toán 1
1.1.2 Các đặc trưng cơ bản của thuật toán 1
1.2 Sự cần thiết của thiết kế và đánh giá thuât toán 2
1.3 Diễn tả thuật toán 3
1.4 Thiết kế thuật toán 7
1.4.1 Modul hoá và thiết kế từ trên xuống 7
1.4.2 Phương pháp là mịn dần (tinh chỉnh từng bước) 7
1.4.3 Một số kỹ thuật thiết kế 8
1.5 Phân tích thuật toán 9
1.5.1 Thời gian thực hiên thuật toán 9
1.5.2 Độ phức tạp tính toán của thuật toán 10
1.5.3 Ðộ phức tạp của chương trình có gọi chương trình con không đệ qui 16
1.5.4 Phân tích các thuật toán đệ quy 17
1) Thành lập phương trình truy hồi 18
2) Giải phương trình truy hồi 19
Bài tập chương 1 31
Chương 2 Kỹ thuật chia để trị 37
2.1 Nội dung kỹ thuật 37
2.2 Các ví dụ áp dụng 37
2.2.1 Tìm min và max 37
2.2.2 Một số thuật toán sắp xếp 40
1) Sắp xếp nhanh 40
2) Sắp xếp trộn 44
2.2.3 Tìm kiếm nhị phân 51
2.2.4 Nhân các số nguyên lớn 53
Bài tập chương 2 57
Chương 3 Kỹ thuật tham lam 62
3.1 Nội dung kỹ thuật 62
3.1.1 Bài toán tối ưu tổ hợp 62
3.1.2 Nội dung kỹ thuật tham lam 62
3.2 Các ví dụ áp dụng 62
Trang 53.2.1 Bài toán người giao hàng 62
3.2.2 Bài toán chiếc ba lô 65
3.2.3 Bài toán tô màu bản đồ 70
3.2.4 Tìm cây khung nhỏ nhất 74
3.2.5 Tìm đường đi ngắn nhất 77
3.2.6 Bài toán phân công công việc 79
Bài tập chương 3 84
Chương 4 Kỹ thuật quay lui 86
4.1 Nội dung kỹ thuật 86
4.2 Các ví dụ áp dụng 87
4.2.1 Đưa ra các dãy nhị phân độ dài n 87
4.2.2 Đưa ra các hoán vị của n số nguyên 88
4.2.3 Đưa ra các tập con của tập gồm n số nguyên 90
4.2.4 Bài toán xếp hậu 92
4.2.5 Tìm đường đi trên đồ thị 94
4.2.6 Bài toán ngựa đi tuần 99
Bài tập chương 4 104
Chương 5 Kỹ thuật nhánh và cận 111
5.1 Nội dung kỹ thuật 111
5.2 Các ví dụ áp dụng 114
5.2.1 Bài toán người du lịch 114
5.2.2 Bài toán chiếc ba lô 128
Bài tập chương 5 133
Chương 6 Kỹ thuật quy hoạch động 137
6.1 Nội dung kỹ thuật 137
6.2 Các ví dụ áp dụng 140
6.2.1 Tính số tổ hợp 140
6.2.2 Bài toán nhân nhiều ma trận 143
6.2.3 Bài toán chiếc ba lô 149
6.2.4 Xâu con chung dài nhất 154
Bài tập chương 6 164
Phụ lục 171
Tài liệu tham khảo 195
Trang 7Chương 1 TỔNG QUAN VỀ THIẾT KẾ VÀ ĐÁNH GIÁ THUẬT TOÁN 1.1 Thuật toán
1.1.1 Khái niệm thuật toán
Thuật toán (Algorithm) đã được biết đến từ rất lâu Đầu tiên thuật toán được hiểu như là các qui tắc thực hiện các phép tính số học với các con số được viết trong
hệ thập phân Cùng với sự phát triển của máy tính, khái niệm thuật toán được hiểu theo nghĩa rộng hơn Khái niệm thuật toán được định nghĩa một cách hình thức chính xác thông qua máy Turing ở đây chúng ta sẽ xem xét khái niệm thuật toán một cách trực quan
Thuật toán (hay giải thuật, thuật giải) là một khái niệm cơ sở của tin học Mỗi bài toán trong thực tế bao gồm hai phần:
- Input: Các đại lượng cho trước (đại lượng vào)
- Output: Các đại lượng cần tìm (đại lượng ra)
Như vậy việc giải bài toán là việc xác định tường minh output theo input bằng một quá trình có thể thực hiện một cách hiệu quả Đó chính là nội dung cơ bản của lý thuyết tính toán Khi cho bài toán, ta cần tìm ra 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 theo đó, từ input ta sẽ tìm
ra được output theo yêu cầu
Một cách trực quan thuật toán giải một bài toán là một dãy hữu hạn các chỉ dẫn (quy tắc, thao tác hay phép toán) hết sức rõ ràng và chính xác được sắp xếp theo một trình tự xác định để sao cho sau một số hữu hạn lần thực hiên các chỉ dẫn đó thì biến đổi được input thành output
1.1.2 Các đặc trưng cơ bản của thuật toán
Trang 86) Tính phổ dụng
Một thuật toán được xem là có tính phổ dụng cao nếu nó có thể dùng để giải bất cứ bài toán nào trong một lớp các bài toán chứ không phải là một bài toán cụ thể
1.2 Sự cần thiết của thiết kế và đánh giá thuật toán
Xây dựng một thuật toán tốt để giải bài toán đã cho là bước quan trọng nhất trong việc giải bài toán đó trên máy tính điện tử Để có được một thuật toán tốt cần phải nắm vững các kỹ thuật thiết kế, phân tích, đánh giá thuật toán cùng các thuật toán cơ bản cho một số lớp bài toán điển hình
Trong khi giải một bài toán chúng ta có thể có một số thuật toán khác nhau, vấn đề là cần phải đánh giá các thuật toán đó để lựa chọn một thuật toán tốt (nhất) Thông thường thì ta sẽ căn cứ vào các tiêu chuẩn sau:
(1) Thuật toán đúng đắn
(2) Thuật toán đơn giản
(3) Thuật toán thực hiện nhanh
Với yêu cầu (1), để kiểm tra tính đúng đắn của thuật toán chúng ta có thể cài đặt thuật toán đó và cho thực hiện trên máy với một số bộ dữ liệu mẫu rồi lấy kết quả thu được so sánh với kết quả đã biết Thực ra thì cách làm này không chắc chắn bởi vì có thể thuật toán đúng với tất cả các bộ dữ liệu chúng ta đã thử nhưng lại sai với một bộ dữ liệu nào đó Vả lại cách làm này chỉ phát hiện ra thuật toán sai chứ chưa chứng minh được là nó đúng Tính đúng đắn của thuật toán cần phải được chứng minh bằng toán học Điều này không đơn giản và do vậy chúng ta sẽ không
Trang 9trình lại rất quan trọng đặc biệt đối với những chương trình mà khi thực hiện cần dữ liệu nhập lớn do đó yêu cầu (3) sẽ được xem xét một cách kĩ càng Ta gọi nó là hiệu quả thời gian thực hiện của thuật toán
1.3 Diễn tả thuật toán
Có nhiều cách diễn tả thuật toán Người ta thường diễn tả thuật toán bằng một trong các cách sau:
1) Liệt kê từng buớc
Thuật toán có thể trình bày dưới dạng ngôn ngữ tự nhiện theo trình tự các bước thực hiện trong thuật toán
2) Sơ đồ khối (Lưu đồ)
Dùng các hình vẽ (có qui ước) để diễn tả thuật toán Lưu đồ cho hình ảnh trực quan và tổng thể của thuật toán nên thường được sử dụng
Trang 10if (biểu thức) khối lệnh 1 else
khối lệnh 2 /* Dạng hai */
Sự lồng nhau của các toán tử if :
C cho phép sử dụng các toán tử if lồng nhau có nghĩa là trong các khối lệnh (1 và 2) ở trên có thể chứa các toán tử if - else khác Trong trường hợp này, nếu không sử dụng các dấu đóng mở ngoặc cho các khối thì sẽ có thể nhầm lẫn giữa các if-else Chú ý là máy sẽ gắn toán tử else với toán tử if không có else gần nhất
* Cấu trúc rẽ nhánh - toán tử switch:
switch (biểu thức nguyên)
{
case n1
khối lệnh 1 case n2
Trang 11khối lệnh 2
case nk khối lệnh k [ default
khối lệnh k+1]
}
Với ni là các số nguyên, hằng ký tự hoặc biểu thức hằng Các ni cần có giá trị khác nhau Đoạn chương trình nằm giữa các dấu { } gọi là thân của toán tử switch
default là một thành phần không bắt buộc phải có trong thân của switch
* Cấu trúc lặp với toán tử while :
Toán tử while dùng để xây dựng chu trình lặp dạng :
while (biểu thức)
Lệnh hoặc khối lệnh;
Như vậy toán tử while gồm một biểu thức và thân chu trình Thân chu trình
có thể là một lệnh hoặc một khối lệnh
Hoạt động của chu trình như sau :
Máy xác định giá trị của biểu thức, tuỳ thuộc giá trị của nó máy sẽ chọn cách thực hiện như sau :
Nếu biểu thức có giá trị 0 (biểu thức sai), máy sẽ ra khỏi chu trình và chuyển tới thực hiện câu lệnh tiếp sau chu trình trong chương trình
Nếu biểu thức có giá trị khác không (biểu thức đúng), máy sẽ thực hiện lệnh hoặc khối lệnh trong thân của while Khi máy thực hiện xong khối lệnh này nó lại thực hiện xác định lại giá trị biểu thức rồi làm tiếp các bước như trên
* Cấu trúc lặp với toán tử for :
Toán tử for dùng để xây dựng cấu trúc lặp có dạng sau :
for (biểu thức 1; biểu thức 2; biểu thức 3)
Lệnh hoặc khối lệnh ; Toán tử for gồm ba biểu thức và thân for Thân for là một câu lệnh hoặc một
Trang 12vắng mặt nhưng phải giữ dấu ;
Thông thường biểu thức 1 là toán tử gán để tạo giá trị ban đầu cho biến điều khiển, biểu thức 2 là một quan hệ logic biểu thị điều kiện để tiếp tục chu trình, biểu thức ba là một toán tử gán dùng để thay đổi giá trị biến điều khiển
Hoạt động của toán tử for :
Toán tử for hoạt động theo các bước sau :
do
Lệnh hoặc khối lệnh;
while (biểu thức) ;
Hoạt động của chu trình như sau :
Máy thực hiện các lệnh trong thân chu trình
Khi thực hiện xong tất cả các lệnh trong thân của chu trình, máy sẽ xác định giá trị của biểu thức sau từ khoá while rồi quyết định thực hiện như sau :
Nếu biểu thức đúng (khác 0) máy sẽ thực hiện lặp lại khối lệnh của chu trình lần thứ hai rồi thực hiện kiểm tra lại biểu thức như trên
Nếu biểu thức sai (bằng 0) máy sẽ kết thúc chu trình và chuyển tới thực hiện lệnh đứng sau toán tử while
Trang 13Những điều lưu ý với toán tử while ở trên hoàn toàn đúng với do while
* Câu lệnh break
Câu lệnh break cho phép ra khỏi các chu trình với các toán tử for, while, do while và switch Khi có nhiều chu trình lồng nhau, câu lệnh break sẽ đưa máy ra khỏi chu trình bên trong nhất chứa nó không cần điều kiện gì
* Câu lệnh continue :
Trái với câu lệnh break, lệnh continue dùng để bắt đầu một vòng mới của chu trình chứa nó Trong while và do while, lệnh continue chuyển điều khiển về thực hiện ngay phần kiểm tra, còn trong for điều khiển được chuyển về bước khởi đầu lại (tức là bước : tính biểu thức 3, sau đó quay lại bước 2 để bắt đầu một vòng mới của chu trình) Lệnh continue chỉ áp dụng cho chu trình chứ không áp dụng cho switch
1.4 Thiết kế thuật toán
1.4.1 Modul hoá và thiết kế từ trên xuống
Các bài toán giải được trên máy tính ngày càng phức tạp và đa dạng Các thuật toán giải chúng ngày càng có quy mô lớn đòi hỏi nhiều thời gian và công sức của nhiều người Tuy nhiên công việc sẽ đơn giản hơn nếu như ta chia bài toán ra thành các bài toán nhỏ Điều đó cũng có nghĩa là nếu coi bài toán là modul chính thì cần chia thành các modul con Đến lượt mình các modul con lại phân rã thành các modul con thích hợp
Như vậy việc tổ chức lời giải thể hiện theo một cấu trúc phân cấp Chiến thuật giải bài toán như vậy là “chia để trị”, thể hiện chiến thuật đó ta
dùng thiết kế từ trên xuống Đó là cách nhìn nhận vấn đề một cách tổng quát, đề cập đến các công việc chính, sau đó mới bổ sung dần các chi tiết
1.4.2 Phương pháp làm mịn dần (tinh chỉnh từng bước)
Đầu tiên thuật toán được trình bày dưới dạng ngôn ngữ tự nhiên thể hiện ý chính công việc Các bước sau sẽ chi tiết hóa dần tương ứng với các công việc nhỏ hơn Đó là các bước làm mịn dần đặc tả thuật toán và hướng về ngôn ngữ lập trình
mà ta dự định cài đặt
Quá trình thiết kế và phát triển thuật toán sẽ thể hiện dần từ ngôn ngữ tự nhiên, sang ngôn ngữ mã giả rồi đến ngôn ngữ lập trình, và đi từ mức “làm cái gì“đến “làm như thế nào”
Trang 141.4.3 Một số kỹ thuật thiết kế
Trên cơ sở lý thuyết máy Turing, người ta chia được các bài toán thành 2 lớp không giao nhau : Lớp giải được bằng thuật toán, và lớp không giải được bằng thuật toán
Đối với lớp các bài toán giải được bằng thuật toán, dựa vào các đặc trưng của quá trình thiết kế của thuật toán, ta có thể chỉ ra một số các kỹ thuật thiết kế thuật toán cơ bản sau đây :
1) Kỹ thuật chia để trị
Chia bài toán thành các bài toán đủ nhỏ, giải các bài toán nhỏ rồi tổng hợp kết quả lại
2) Kỹ thuật quay lui
Tìm kiếm theo ưu tiên
Đối với mỗi bước thuật toán, ưu tiên theo độ rộng hay chiều sâu để tìm kiếm Chẳng hạn thuật toán giải bài toán 8 hậu
3) Kỹ thuật tham lam
Ý tưởng là : Xác định trật tự xử lý để có lợi nhất, Sắp xếp dữ liệu theo trật tự
đó, rồi xử lý dữ liệu theo trật tự đã nêu Công sức bỏ ra là tìm ra trật tự đó Chẳng hạn thuật toán tìm cây khung nhỏ nhất
4) Kỹ thuật nhánh và cận
Trong quá trình tìm kiếm lời giải, ta phân hoạch tập các phương án của bài toán ra thành hai hay nhiều tập con được biểu diễn như là các nút của cây tìm kiếm
và cố gắng bằng phép đánh giá cận cho các nút, tìm cách loại bỏ các nhánh của cây
mà ta biết chắc không chứa phương án tối ưu
5) Kỹ thuật Quy hoạch động
Kỹ thuật quy hoạch động dựa vào một nguyên lý, gọi là nguyên lý tối ưu của Bellman :
“ Nếu lời giải của bài toán là tối ưu thì lời giải của các bài toán con cũng tối
ưu ”
Kỹ thuật này tổ chức tìm kiếm lời giải theo kiểu từ dưới lên Xuất phát từ các bài toán con nhỏ và đơn giản nhất, tổ hợp các lời giải của chúng để có lời giải của bài toán con lớn hơn và cứ như thế cuối cùng được lời giải của bài toán ban đầu
Trang 151.5 Phân tích thuËt toán
Trong khi giải một bài toán chúng ta có thể có một số thuật toán khác nhau, vấn đề là cần phải đánh giá các thuật toán đó để lựa chọn một thuật toán tốt (nhất) Thông thường thì ta sẽ căn cứ vào các tiêu chuẩn sau:
- Thuật toán đơn giản
- Thuật toán thực hiện nhanh
Khi chúng ta viết một chương trình để sử dụng một vài lần thì yêu cầu thuật toán đơn giản là quan trọng Chúng ta cần một giải thuật dễ viết chương trình để nhanh chóng có được kết quả, thời gian thực hiện chương trình không được đề cao
vì dù sao thì chương trình đó cũng chỉ sử dụng một vài lần mà thôi
Tuy nhiên khi một chương trình được sử dụng nhiều lần thì yêu cầu tiết kiệm thời gian thực hiện chương trình lại rất quan trọng đặc biệt đối với những chương trình mà khi thực hiện cần dữ liệu nhập lớn do đó yêu cầu thuật toán thực hiện nhanh sẽ được xem xét một cách kĩ càng Ta gọi nó là hiệu quả thời gian thực hiện của thuật toán Hơn nữa khối lượng dữ liệu lớn mà dung lượng bộ nhớ lại có giới hạn thì không thể bỏ qua yêu cầu về tiết kiệm bộ nhớ được Tuy nhiên cân đối giữa yêu cầu về thời gian và không gian không mấy khi có được một giải phấp trọn vẹn
Sau đây ta sẽ chỉ chú ý đến việc phân tích thời gian thực hiện thuật toán
1.5.1 Thêi gian thùc hiÖn thuËt toán
Một phương pháp để xác định hiệu quả thời gian thực hiện của một thuật toán là lập trình nó và đo lường thời gian thực hiện của hoạt động trên một máy tính xác định đối với tập hợp được chọn lọc các dữ liệu vào
Thời gian thực hiện không chỉ phụ thuộc vào thuật toán mà còn phụ thuộc vào tập các chỉ thị của máy tính, chất lượng của máy tính và kĩ xảo của người lập trình Sự thi hành cũng có thể điều chỉnh để thực hiện tốt trên tập đặc biệt các dữ liệu vào được chọn Ðể vượt qua các trở ngại này, các nhà khoa học máy tính đã chấp nhận tính phức tạp của thời gian được tiếp cận như một sự đo lường cơ bản sự thực thi của thuật toán Thuật ngữ tính hiệu quả sẽ đề cập đến sự đo lường này và đặc biệt đối với sự phức tạp thời gian trong trường hợp xấu nhất
Nói chung thì thời gian thực hiện thuật toán không chỉ phụ thuộc vào kích thước mà còn phụ thuộc vào tính chất của dữ liệu vào Nghĩa là dữ liệu vào có cùng kích thước nhưng thời gian thực hiện giải thuật có thể khác nhau Chẳng hạn chương trình sắp xếp dãy số nguyên tăng dần, khi ta cho vào dãy có thứ tự thì thời gian thực hiện khác với khi ta cho vào dãy chưa có thứ tự, hoặc khi ta cho vào một
Trang 16dãy đã có thứ tự tăng thì thời gian thực hiện cũng khác so với khi ta cho vào một dãy đã có thứ tự giảm
Vì vậy thường ta coi T(n) là thời gian thực hiện chương trình trong trường hợp xấu nhất trên dữ liệu vào có kích thước n, tức là: T(n) là thời gian lớn nhất để thực hiện chương trình đối với mọi dữ liệu vào có cùng kích thước n
Để đánh giá thời gian thực hiện thuật toán người ta tìm cách đánh giá độc lập với các yếu tố bên ngoài như máy tính hay các yếu tố liên quan đến máy tính Cách đánh giá như vậy dẫn tới khái niệm về cấp độ lớn của thời gian thực hiện thuật toán hay độ phức tạp tính toán của thuật toán
1.5.2 Độ phức tạp tính toán của thuật toán
Nếu thời gian thực hiện một thuật toán là T(n) =cn2
(với c là hằng số, n là kích thước dữ liệu đầu vào) thì ta nói: Độ phức tạp tính toán của thuật toán này có cấp là n2
(hay cấp độ lớn của thời gian thực hiện thuật toán là n2) và ta ký hiệu: T(n) = O(n2) (ký hiệu chữ O lớn)
Một cách tổng quát có thể định nghĩa:
Một hàm f(n) được xác định là O(g(n)) và viết là f(n) =O(g(n)) và được gọi
là cấp g(n) nếu tồn tại các hằng số c và n0 sao cho:
f(n) ≤ cg(n) khi n ≥ n0
nghĩa là f(n) bị chặn trên bởi một hằng số nhân với g(n), với mọi giá trị của n tăng
từ một điểm nào đó Thông thường các hàm thể hiện độ phức tạp tính toán của thuật toán có dạng :
Hình 1.1 Bảng giá trị của một số hàm số
Các hàm như 2n , n!, nn được gọi là hàm loại mũ Một thuật toán mà thời gian
Trang 17thực hiện của nó có cấp là các hàm loại mũ thì tốc độ rất chậm Các hàm như n3
, n2, nlog2n, n, log2n được gọi là các hàm loại đa thức Một thuật toán mà thời gian thực hiện có độ phức tạp là một hàm đa thức thì chấp nhận được tức là có thể cài đặt để thực hiện, còn các thuật toán có độ phức tạp hàm mũ thì phải tìm cách cải tiến thuật toán
Các quy tắc xác định độ phức tạp của thuật toán:
Xác định độ phức tạp tính toán của một thuật toán bất kỳ có thể dẫn tới những bài toán phức tạp Tuy nhiên, trong thực tế, đối với một số thuật toán ta cũng
có thể phân tích được bằng một số quy tắc đơn giản
* Quy tắc tổng:
Giả sử T1(n) và T2(n) là độ phức tạp tính toán của hai đoạn chương trình P1
và P2 mà T1(n) = O(f(n)); T2(n) = O(g(n)) thì độ phức tạp tính toán khi thực hiện P1
và tiếp theo là P2 sẽ là: T1(n) + T2(n) = O(max (f(n),g(n))
Trang 18* Quy tắc nhân:
Giả sử T1(n) và T2(n) là độ phức tạp tính toán của hai đoạn chương trình P1
và P2 mà T1(n) = O(f(n)); T2(n) = O(g(n)) thì độ phức tạp tính toán khi P1 và P2 lồng nhau sẽ là: T1(n).T2(n) = O(f(n).g(n))
Câu lệnh: for ( i=1; i<=n; i++) x =x+1;
Có độ phức tạp tính toán O(n.1) = O(n)
Câu lệnh : for ( i= 1; i<=n; i++)
Trang 19Ta có thể coi phép toán tích cực ở đây là phép: p = p*x/j
Ta thấy nó được thực hiện: 1 +2+…+ n = n(n+1)/2 lần
Vậy độ phức tạp tính toán của thuật toán này được đánh giá là T(n) = O(n2
) Thuật toán có thể được viết theo một cách khác:
Trang 20Bây giờ độ phức tạp tính toán lại là: T(n) = O(n) Vì phép gán p=p*x/i chỉ thực hiện n lần
Trong thuật toán ta coi phép so sánh (a[j-1]>a[j]) là phép toán tích cực Phép
toán này nằm trong vòng lặp for(j=n; j>=i+1; j ) nên nó được thực hiện (n-i) lần Vòng lặp for(j=n; j>=i+1; j ) nằm trong vòng lặp for(i=1;i<=n-1; i++) thực hiện
(n-1) lần Do vậy số lần thực hiện phép toán tích cực (a[j-1]>a[j]) sẽ là:
1
) 1 n ( n ) i n (
Nên độ phức tạp tính toán của thuật toán là O(n2)
Chú ý:
Ta biết rằng thời gian thực hiện thuật toán không phải chỉ phụ thuộc vào kích thước dữ liệu mà còn phụ thuộc vào tình trạng dữ liệu nhập nữa Chẳng hạn, khi xếp tăng dần một dãy các số nguyên mà dãy các so nguyên đĩ đã có sẵn thứ tự tăng dần, hoặc ngược lại, hoặc ngẫu nhiên Lúc đó khi phân tích thời gian thực hiện thuật toán ta sẽ phải xét tới: đối với mọi dữ liệu vào có kích thước n thì T(n) trong trường hợp tốt nhất, xấu nhất là như thế nào? T(n) trung bình? Việc xác định T(n) trung bình thường khó và phức tạp đòi hỏi những công cụ toán học đặc biệt, hơn nữa việc tính trung bình có thể có nhiều cách quan niệm khác nhau Trong trường hợp T(n) khó xác định người ta thường đánh giá thuật toán qua giá trị xấu nhất của T(n)
Trang 21VÝ dô 1.5
timkiem(v)
/*Cho vectơ V có n phần tử, thuật toán này thực hiện tìm trong V một phần tử có giá trị bằng X cho trước Nếu tìm thấy trả về chỉ số của phần tử đó, nếu không tìm thấy trả về giá trị 0*/
Trường hợp xấu nhất khi X bằng V[n] hoặc không tìm thấy: n lần thực hiện Vậy: Ttốt = O(1) và Txấu = O(n)
Thì ta xác định độ phức tạp tính toán của thuật toán là O(n)
* Qui tắc tổng quát để phân tích một chương trình:
Giả sử rằng, các lệnh gán không chứa các lời gọi hàm Khi đó để đánh giá thời gian thực hiện một chương trình, ta có thể áp dụng một số quy tắc sau
1 Thời gian thực hiện các lệnh đơn: gán, đọc, viết là O(1)
2 Lệnh hợp thành (khối lệnh) : thời gian thực hiện lệnh hợp thành được xác định bởi luật tổng
3 Lệnh if : Giả sử thời gian thực hiện các lệnh S1, S2 là O(f(n)) và O(g(n)) tương ứng Khi đó thời gian thực hiện lệnh if là O(max (f(n), g(n)))
4 Lệnh witch: Lệnh này được đánh giá như lệnh if
5 Lệnh while : Giả sử thời gian thực hiện lệnh S (thân của while) là O(f(n)) Giả sử g(n) là số tối đa các lần thực hiện lệnh S Khi đó thời gian thực hiện lệnh while là O(f(n)g(n))
6 Lệnh do .while :Giả sử thời gian thực hiện khối lệnh trong thân do while là O(f(n)) Giả sử g(n) là số lần tối đa các lần thực hiện khối lệnh trong thân
do while Khi đó thời gian thực hiện lệnh do while là O(f(n)g(n))
7 Lệnh for : Lệnh này được đánh giá tương tự như lệnh while
1.5.3 Ðộ phức tạp của chương trình có gọi chương trình con không đệ qui
Trang 22Nếu chúng ta có một chương trình với các chương trình con không đệ quy,
để tính thời gian thực hiện của chương trình, trước hết chúng ta tính thời gian thực hiện của các chương trình con không gọi các chương trình con khác Sau đó chúng
ta tính thời gian thực hiện của các chương trình con chỉ gọi các chương trình con mà thời gian thực hiện của chúng đã được tính Chúng ta tiếp tục quá trình đánh giá thời gian thực hiện của mỗi chương trình con sau khi thời gian thực hiện của tất cả các chương trình con mà nó gọi đã được đánh giá Cuối cùng ta tính thời gian cho chương trình chính
Giả sử ta có một hệ thống các chương trình gọi nhau theo sơ đồ sau:
Hình 1.2 Chương trình gọi chương trình con không đẹ quy
Chương trình A gọi hai chương trình con là B và C, chương trình B gọi hai chương trình con là B1 và B2, chương trình B1 gọi hai chương trình con là B11 và B12
Ðể xác định độ phức tạp tính toán của A, ta thực hiện theo các bước sau:
1 Xác định độ phức tạp tính toán của C, B2, B11 và B12 Vì các chương trình con này không gọi chương trình con nào cả
2 Xác định độ phức tạp tính toán của B1 Vì B1 gọi B11 và B12 mà độ phức tạp tính toán của B11 và B12 đã được tính ở bước 1
3 Xác định độ phức tạp tính toán của B Vì B gọi B1 và B2 mà độ phức tạp tính toán của B1 đã được tính ở bước 2 và độ phức tạp tính toán của B2 đã được tính ở bước 1
4 Xác định độ phức tạp tính toán của A Vì A gọi B và C mà độ phức tạp tính toán của B đã được tính ở bước 3 và độ phức tạp tính toán của C đã được tính ở bước 1
Ví dụ 1.6
Thuật toán sắp xếp nổi bọt
Trước hết viết thủ tục Swap để thực hiện việc hoàn đổi hai phần tử cho nhau, sau đó trong thủ tục Bubble, khi cần sẽ gọi đến thủ tục Swap này
void Swap (x, y)
Trang 23độ phức tạp tính toán của Swap là O(1) vì nó chỉ bao gồm 3 lệnh gán Do vậy ta có
thể coi phép toán tích cực là phép so sánh (a[j-1]>a[j]) và khi đó dễ thấy độ phức
tạp tính toán của thuật toán là:
1.5.4 Phân tích các thuật toán đệ quy
Nhiều thuật toán dựa trên sự phân rã đệ qui một bài toán lớn thành các bài toán nhỏ, rồi dùng lời giải các bài toán nhỏ để giải bài toán ban đầu Thời gian chạy của thuật toán như thế được xác định bởi kích thước và số lượng các bài toán con và giá phải trả của sự phân rã Nên các thuật toán đệ qui có thời gian chạy phụ thuộc vào thời gian chạy cho các dữ liệu nhập có kích thước nhỏ hơn, điều này được diễn dịch thành một công thức toán học gọi là công thức truy hồi hay phương trình truy hồi, hệ thức truy hồi Do đó, để tính độ phức tạp của thuật toán, ta thường phải giải các phương trình truy hồi
Với các thuật toán có các lời gọi đệ quy, ta không thể áp dụng cách tính như vừa trình bày trong mục 1.3.3 bởi vì một chương trình đệ quy sẽ gọi chính bản thân nó
Có thể thấy hình ảnh chương trình đệ quy A như sau:
Trang 24Hình 1.3 Chương trình đệ quy A
Với các chương trình đệ quy, trước hết ta cần thành lập các phương trình truy hồi, sau đó giải phương trình truy hồi, nghiệm của phương trình truy hồi sẽ là thời gian thực hiện của chương trình đệ quy
1) Thành lập phương trình truy hồi
Phương trình truy hồi là một phương trình biểu diễn mối liên hệ giữa T(n) và T(k), trong đó T(n) là thời gian thực hiện chương trình với kích thước dữ liệu nhập
là n, T(k) thời gian thực hiện chương trình với kích thước dữ liệu nhập là k, với k <
n Ðể thành lập được phương trình truy hồi, ta phải căn cứ vào chương trình đệ quy
Thông thường một chương trình đệ quy để giải bài toán kích thước n, phải có
ít nhất một trường hợp dừng ứng với một n cụ thể và lời gọi đệ quy để giải bài toán kích thước k (k<n)
Để thành lập phương trình truy hồi, ta gọi T(n) là thời gian để giải bài toán kích thước n, ta có T(k) là thời gian để giải bài toán kích thước k Khi dừng, ta phải xem xét khi đó chương trình làm gì và tốn hết bao nhiêu thời gian, chẳng hạn thời gian này là c(n) Khi đệ quy chưa dừng thì phải xét xem có bao nhiêu lời gọi đệ quy với kích thước k ta sẽ có bấy nhiêu T(k) Ngoài ra ta còn phải xem xét đến thời gian
để phân chia bài toán và tổng hợp các lời giải, chẳng hạn thời gian này là d(n) Dạng tổng quát của một phương trình truy hồi sẽ là:
Trong đó C(n) là thời gian thực hiện chương trình ứng với trường hợp đệ quy dừng F(T(k)) là một đa thức của các T(k), d(n) là thời gian để phân chia bài toán và tổng hợp các kết quả
Trang 25x là số nguyên lớn nhất nhỏ hơn hoặc bằng x
x là số nguyên nhỏ nhất lớn hơn hoặc bằng x)
Ví dụ 1.7
Xét hàm tính giai thừa viết bằng thuật toán đệ quy như sau:
int Giai_thua(n) {
hiện một lệnh gán gt=1, nên tốn O(1), do đó ta có T(1) = C1 Trong trường hợp n>1
chương trình phải gọi đệ quy Giai_thua(n-1), việc gọi đệ quy này tốn T(n-1), sau
khi có kết quả của việc gọi đệ quy, chương trình phải nhân kết quả đó với n và gán cho gt Thời gian để thực hiện phép nhân và phép gán là một hằng C2 Vậy ta có:
Ðây là phương trình truy hồi để tính thời gian thực hiện của chương trình đệ quy Giai_thua
2) Giải phương trình truy hồi
Một số phương pháp giải phương trình truy hồi:
* Phương pháp thay thế
Dùng đệ quy để thay thế bất kỳ T(m) với m < n vào phía phải của phương trình cho đến khi tất cả T(m) với m > 1 được thay thế bởi biểu thức của các T(1) hoặc T(0) Vì T(1) và T(0) luôn là hằng số nên chúng ta có công thức của T(n) chứa các số hạng chỉ liên quan đến n và các hằng số Từ công thức đó ta suy ra T(n)
VÝ dô 1.8
Hàm tính n! trong ví dụ 1.7
int Giai_thua(n) {
C1 nếu n 1 T(n-1)+C2 nếu n >1 T(n) =
Trang 26if (n<=1) gt=1;
else gt=n* Giai_thua(n-1);
return(gt);
}
Ta đã có phương trình truy hồi để tính thời gian thực hiện của chương trình
đệ quy Giai_thua là:
- Với n 1, chỉ cần thực hiện lệnh gán gt = 1, do đó T(1) = O(1)
- Với n > 1 cần thực hiện lệnh gán gt= n*Giai_thua(n - 1)
Do đó thời gian T(n) là O(1) (để thực hiện phép nhân và phép gán) cộng với
T(n-1) (để thực hiện lời gọi đệ qui Giai_thua(n – 1)) Tóm lại, ta có quan hệ sau:
Để giải phương trình truy hồi, tìm T(n), chúng ta áp dụng phương pháp thế lặp Ta
có phương trình truy hồi
Trang 27= nc1 + c2nlog2n
Và T(n) = O(nlog2n)
* Phương pháp đoán nghiệm
Ta đoán một nghiệm f(n) và dùng chứng minh quy nạp để chứng tỏ rằng T(n)
≤ f(n) với mọi n Thông thường f(n) là một trong các hàm quen thuộc như log2n, n, nlog2n, n2, n3, 2n, n!, nn
Ðôi khi chúng ta chỉ đoán dạng của f(n) trong đó có một vài tham số chưa xác định (chẳng hạn f(n) = an2 với a chưa xác định) và trong quá trình chứng minh quy nạp ta sẽ suy diễn ra giá trị thích hợp của các tham số
Ví dụ 1.10 Giải phương trình truy hồi sau:
T(n) =
Trang 28Giả sử rằng T(k) ≤ f(k), tức là T(k) ≤ aklog2k + b với mọi k < n (giả thiết quy nạp)
Ta phải chứng minh T(n) ≤ anlog2n + b với mọi n
Giả sử n ≥ 2, từ phương trình đã cho ta có T(n) = 2T(
≤ (anlog2n + b) + [b + (c2 - a)n] Nếu lấy a ≥ c2 + b (**) ta được
thì T(n) ≤ anlog2n + b với mọi n
Như vậy với b c1 và a c1 + c2 thì ta sẽ có T(n) ≤ (c1 + c2)nlog2n +c1 với mọi n Hay nói cách khác T(n) là O(nlog2n)
* Phương pháp dùng phương trình đặc trưng với phương trình truy hồi tuyến tính
thuần nhất hệ số hằng
Phương trình truy hồi (công thức truy hồi) tuyến tính thuần nhất bậc k với
hệ số hằng số có dạng:
T(n)=
Trang 29an = c1an-1 + c2an-2 + + ckan-k trong đó c1, c2, , ck là các số thực, ck 0
Nếu cho trước k điều kiện ban đầu a0 = c0, a1 = c1, , ak-1 = ck-1 ,thì theo qui nạp toán học, dãy số thoả mãn phương trình truy hồi nêu trong định nghĩa sẽ được xác định duy nhất
Phương pháp cơ bản để giải phương trình truy hồi tuyến tính thuần nhất là tìm nghiệm dưới dạng an = rn, trong đó r là hằng số Ta có an = rn là nghiệm của phương trình truy hồi an = c1an-1 + c2an-2 + + ckan-k khi và chỉ khi:
rn = c1rn-1 + c2rn-2 + + ckrn-kHay rn - c1rn-1 - c2rn-2 - - ckrn-k = 0
Vậy, dãy {an} với an = rn là nghiệm khi và chỉ khi r là nghiệm của phương trình đại số trên Phương trình này được gọi là phương trình đặc trưng của công thức truy hồi và nghiệm của nó được gọi là nghiệm đặc trưng của phương trình truy hồi Các nghiệm đặc trưng sẽ dùng cho công thức tường minh của tất cả các nghiệm của phương trình truy hồi
Đối với phương trình truy hồi tuyến tính thuần nhất bậc 2 khi phương trình đặc trưng có hai nghiệm phân biệt r1, r2 Khi đó dãy số {an} là nghiệm của công thức truy hồi an = c1an-1 + c2an-2 khi và chỉ khi
2 0 1 1
r r
r C C
Trang 30Vậy khi chọn các giá trị a1 và a2 này thì dãy {an} với an = a1r1n + a2r2n thoả mãn các điều kiện đầu Vì phương trình truy hồi và các điều kiện đầu xác định duy nhất, nên an = a1r1n + a2r2n
Ví dụ 1.11
Giả sử rằng các con thỏ không bao giờ chết Biết rằng một cặp thỏ sau 2 tháng tính từ khi ra đời sẽ sinh ra một cặp thỏ mới và sau đó cứ mỗi tháng lại sinh ra một cặp thỏ mới Hỏi nếu tháng đầu có một cặp thỏ thì đến tháng thứ n sẽ có bao nhiêu cặp thỏ?
Vì vậy có thể tính Fn theo hệ thức truy hồi sau:
Dãy số thể hiện Fn với các giá trị của n được gọi là dãy số Fibonacci
Từ hệ thức truy hồi trên ta dễ dàng có được giải thuật sau để tính số cặp thỏ
Trang 31Giải phương trình đặc trưng r2- r- 1 = 0 ta thu được hai nghiệm:
2
5 1
2
5 1 2
5 1 5
1 ) n ( T
Hay:
T(n) = O n
) 2
5 1 (
Như vậy độ phức tạp tính toán của giải thuật là cấp hàm mũ
Trong các ví dụ sau tài liệu chỉ đưa ra các cách giải phương trình truy hồi (hệ thức truy hồi, công thức truy hồi) từ đó người đọc có thể dễ dàng vận dụng để xác định độ phức tạp tính toán của giải thuật tương ứng
Ví dụ 1.12
Tìm nghiệm của công thức truy hồi an = an-1 + 2an-2, với a0 = 2, a1 = 7
Giải: Phương trình đặc trưng của công thức truy hồi này có dạng r2 - r -2 = 0 Nghiệm của nó là r=2 và r=-1 Theo định lý 1 dãy {an} là nghiệm của công thức truy hồi khi và chỉ khi an = 12n + 2(-1)n với các hằng số a1 và a2 nào đó Từ các điều kiện đầu ta suy ra:
a0 = 2 = 1 + 2
a1 = 7 = a12 + a2(-1)
Giải ra ta được 1 = 3 và 2 = -1 Vậy nghiệm của công thức truy hồi với điều kiện đầu là dãy {an} với an = 3.2n - (-1)n
Trang 32Trong trường hợp phương trình đặc trưng của công thức truy hồi tuyến tính thuần nhất bậc 2 có nghiệm đặc trưng là nghiệm bội (chỉ có một nghiệm r0) khi đó dãy số { an} là nghiệm của công thức truy hồi an = c1an-1 + c2an-2 khi và chỉ khi
a1 =6 suy ra 1 = 1 và 2 = 1 Do vậy nghiệm của hệ thức truy hồi và các điều kiện
ban đầu đã cho là: an = 3n + n3n
Tổng quát hóa kết quả trên cho trường hợp hệ thức truy hồi tuyến tính thuần nhất hệ số hằng bậc k > 2 Giả sử phương trình đặc trưng rk- c1rk-1- c2rk-2- - ck = 0
có k nghiệm phân biệt r1, r2, , rk Khi đó dãy số {an} là nghiệm của hệ thức truy hồi an = c1an-1 + c2an-2+ + ckan-k khi và chỉ khi
Giải: Phương trình đặc trưng r3- 6r2 + 11r- 6 = 0
có 3 ngiệm r1 = 1, r2 = 2, r3 = 3 Vì vậy, nghiệm có dạng
Trang 33Chia bài toán kích thước n thành a bài toán con mỗi bài toán con có kích thước n/b Giải các bài toán con này và tổng hợp các kết quả ta được lời giải của bài toán ban đầu Với các bài toán con ta cũng áp dụng kỹ thuật này thì đến một lúc bài toán con sẽ có kích thước là 1 Kỹ thuật này sẽ dẫn ta đến một giải thuật đệ quy
Gọi thời gian để giải quyết bài toán đã cho kích thước n là T(n), thời gian để giải quyết bài toán con kích thước n/b là T(n/b), thời gian để giải quyết bài toán con kích thước 1 là 1, thời gian để phân tích bài toán thành các bài toán con kích thước n/b và tổng hợp kết quả là d(n) thì ta sẽ có phương trình (1)
Với phương trình (1) khi n>1 ta có:
1 i
0 j
j j
1 k
0 j
j j
1 k
0 j
j j
1 k
0 j
j j
= ak + a d ( b )
1 k
0 j
j k j
Trang 34Trong (2) hàm ak được gọi là nghiệm thuần nhất Nghiệm thuần nhất biểu diễn thời gian để giải các bài toán con.Ta có:
0 j
j k j
Theo qui tắc tổng trong (2) ta nhận thấy trong hai nghiệm: nghiệm riêng và nghiệm thuần nhất nghiệm nào lớn hơn thì đó là nghiệm của phương trình truy hồi Việc xác định nghiệm riêng nói chung là phức tạp, ở đây ta chỉ quan tâm đến một lớp các phương trình dạng (1) mà ở đó hàm tiến triển có những dạng mà từ đó ta có thể dễ dàng tìm được nghiệm riêng
Hàm nhân: Một hàm f(n) được gọi là hàm nhân nếu với mọi n, m nguyên
dương ta đều có f(n.m)=f(n).f(m)
Ví dụ 1.15
Hàm f(n) = nk là một hàm nhân vì:
f(n.m) = (nm)k = nk.mk = f(n).f(m) + Tìm nghiệm của (1) trong trường hợp d(n) là hàm nhân
Nếu d(n) là hàm nhân thì khi đó ta có:
0 j
j k j
) b ( d
1 ) b ( d a
1 ) ) b ( d
a ( ) b ( d ) ) b ( d
a ( ) b ( d
k k
j 1
k
0 j
) b ( d
a k k
(3)
Khi đó xảy ra các trường hợp sau:
1- Nếu a > d(b) thì trong (3) ta có ak > dk(b), theo quy tắc tổng độ phức tạp của nghiệm riêng là O(ak) bằng độ phức tập của nghiệm thuần nhất và đều bằng O(nlog b a ), tức la T(n) =O(nlog b a)
Trang 35n ) = O(n2) 2- Nếu a < d(b) thì ak < dk(b) thì theo quy tắc tổng độ phức tạp nghiệm riêng
là O(dk(b)) lớn hơn của nghiệm thuần nhất là O(ak) Do đó:
T(n) = O(dk(b))
Ta có: k log b n log b d ( b ) log d ( b ) n log b d ( b )
n )]
b ( d [ )]
b ( d [ ) b (
n ) = O(n3) 3- Nếu a = d(b) thì (3) không xác định, trong trường hợp này ta phải tính trực tiếp nghiệm riêng Ta có:
) a 1 a k
) b ( d
a ( ) b (
1 k
0 j
k j 1
k
0 j
Nên nghiệm riêng sẽ là: nlog b alogbn
Nghiệm riêng lớn hơn nghiệm thuần nhất (nlog b alogbn> alog b n)
Vậy ta có: T(n) = O(nlog b alogbn)
Ví dụ 1.18
1 nếu n =1 4T(n/2)+ n nếu n >1 T(n) =
1 nếu n =1 4T(n/2)+ n3 nếu n >1 T(n) =
Trang 36Giải phương trình truy hồi sau:
Giải:
Phương trình có dạng (1) với a=2, b=2 và d(n) = n Vì d(n) = n nên d(n) là hàm nhân và d(b) = d(2) =2= a
Vậy T(n) = O(nlog 2 2log2n) = O(nlog2n)
+ Tìm nghiệm của (1) trong trường hợp d(n) không phải là một hàm nhân Trong trường hợp này ta phải tính trực tiếp nghiệm riêng rồi lấy nghiệm lớn hơn trong hai nghiệm: nghiệm riêng và nghiệm thuần nhất làm nghiệm của (1)
log 2 2 )
b ( d
a
1 k
0 j
k k
1 k
0 j
j k 2 j k j 1
k
0
j
j k
1 nếu n =1 2T(n/2)+ nlog2n nếu n >1 T(n) =
Trang 38j=j-1;
} a[j+1]=x;
ADJUST được gọi trong HEAP_SORT
Trang 39// Chọn con ứng với khoá lớn nhất trong hai con của i
Ta coi phép toán tích cực là phép so sánh (j<n)&&(k[j]<k[j+1]) có thời gian
là O(1) Thuật toán trên chỉ duyệt trên một nhánh (nhánh có khoá lớn hơn) của cây nhị phân Do vậy sau mỗi lần lặp thì số nút chỉ còn lại một nửa Nếu số nút lúc đầu
là n thì trong trường hợp xấu nhất thì lệnh lặp while phải thực hiện i lần sao cho 2i
Trang 40}
}
Trong thuật toán này vòng lặp for(i=[n/2];i>=1;i ) thực hiện lặp n/2 lần, mỗi lần
lặp gọi ADJUST(i,n) Do vậy độ phức tạp tính toán của HEAP_SORT là O(nlog2n)
6 Giải các phương trình truy hồi sau:
3
1 2
1 1
S với n nguyên dương
Viết một giải thuật không đệ quy và một giải thuật đệ quy tính tổng trên và đánh giá độ phức tạp tính toán của các giải thuật đó
1 nếu n =1 3T(n/2)+ n2 nếu n >1 T(n) =
1 nếu n =1 8T(n/2)+ n3 nếu n >1 T(n) =
1 nếu n =1 3T(n/2)+ n nếu n >1 T(n) =
a)