1. Trang chủ
  2. » Luận Văn - Báo Cáo

PHÂN TÍCH THIẾT KẾ THUẬT TOÁN VÀ TĂNG TRƯỞNG CỦA HÀM

75 711 0

Đang tải... (xem toàn văn)

Tài liệu hạn chế xem trước, để xem đầy đủ mời bạn chọn Tải xuống

THÔNG TIN TÀI LIỆU

Thông tin cơ bản

Định dạng
Số trang 75
Dung lượng 1,75 MB

Nội dung

Việc phân tích đã chỉ raphương pháp làm tăng thời gian với một số lượng các chỉ số đã được sắp xếp.Thuật toán chèn dưới đây chúng ta đã sử dụng phương pháp “Divide-and-conquer” divide-an

Trang 1

ĐẠI HỌC HUẾ

TRƯỜNG ĐẠI HỌC KHOA HỌC

  

TIỂU LUẬN

Môn học: Thiết kế và phân tích thuật toán

Đề tài: PHÂN TÍCH THIẾT KẾ THUẬT TOÁN

VÀ TĂNG TRƯỞNG CỦA HÀM

Giáo viên hướng dẫn: TS Hoàng Quang

Học viên thực hiện (Nhóm 6):

Hồ Thủy Sơn Nguyễn Hữu Lương Nguyễn Thị Phương Ngọc

Hồ Thị Thu Thủy

Lê Thị Thanh Châu

Trang 2

MỤC LỤC

ĐẠI HỌC HUẾ 1

LỜI NÓI ĐẦU 3

CHƯƠNG 2 4

MỞ ĐẦU 4

2.1 Thuật toán Sắp xếp chèn (Insertion sort) 4

2.2 Phân tích thuật toán 7

2.3 Thiết kế thuật toán 16

CHƯƠNG 3 33

TĂNG TRƯỞNG CỦA HÀM 33

Tổng quan 33

3.1 Hệ ký hiệu tiệm cận 33

3.2 Khái niệm chuẩn và các hàm thông dụng 44

Trang 3

LỜI NÓI ĐẦU

Thiết kế và phân tích thuật toán là môn học rất quan trọng và cần thiết chongười làm CNTT Là nền tảng cơ bản nhất giúp người lập trình hiểu được sâuhơn về hệ thống và có thể tính toán độ phức tạp để từ đó quyết định chọn mộtphương pháp tối ưu cho hệ thống phần mềm

Nội dung của tiểu luận này là cơ sở lý thuyết ban đầu để phục vụ việc thiết

kế phân tích thuật toán và sử dụng các ký hiệu để trình bày độ phức tạp tính toáncủa chương trình một cách rõ ràng nhất

Nội dung trình bày gồm 2 chương theo tài liệu dịch:

Introduction to Algorithms, Second Edition

Thomas H Cormen

Charles E Leiserson

Ronald L Rivest

Clifford Stein

The MIT Press

Cambridge , Massachusetts London, England

McGraw-Hill Book Company

Boston Burr Ridge , IL Dubuque , IA Madison , WI New York San Francisco St.Louis

Montréal Toronto

Chương 2: Mở đầu

Chương 3: Tăng trưởng của hàm

Mặc dù đã rất cố gắng nhưng tiểu luận này chắc không tránh khỏi nhữngsai sót Nhóm chúng em rất mong nhận được các ý kiến góp ý của thầy hướngdẫn và các bạn Xin chân thành cảm ơn TS Hoàng Quang đã tận tình hướng dẫn

và tạo điều kiện cho chúng em hoàn thành môn học này

Trang 4

CHƯƠNG 2

MỞ ĐẦU

Chương này là nền tảng cho phép bạn làm quen và dùng nó để thiết kế vàphân tích thuật toán Ta bắt đầu bằng việc nghiên cứu thuật toán sắp xếp chèn.Thuật toán này giải quyết các bài toán sắp xếp được giới thiệu trong Chương 1.Chúng ta định nghĩa “pseudecode” (mã giả) là từ ngữ quen thuộc với người đọc

và lập trình trên máy tính hoặc sử dụng nó để trình bày trong thuật toán Cónhững thuật toán đặc biệt mà chúng ta đã chấp nhận nó đã được sắp xếp rấtchính xác và sau đó phân tích thời gian chạy của nó Việc phân tích đã chỉ raphương pháp làm tăng thời gian với một số lượng các chỉ số đã được sắp xếp.Thuật toán chèn dưới đây chúng ta đã sử dụng phương pháp “Divide-and-conquer” (divide-and-conquer) để thiết kế một thuật toán và sử dụng nó để pháttriển thành một thuật toán gọi là thuật toán sắp xếp trộn Cuối cùng là việc phântích thời gian chạy của thuật toán sắp xếp trộn

2.1 Thuật toán Sắp xếp chèn (Insertion sort)

Thuật toán đầu tiên là thuật toán sắp xếp chèn nhằm giải quyết các bàitoán sắp xếp với thông số:

Input: Cho một dãy n số (a1, a2… an)

Output: Phép hoán vị (a1’, a2’… an’) thỏa mãn a1’<= a2’<=….<= an’

Ở đây, những con số mà chúng ta sắp xếp được gọi là khoá Trong chươngnày, điển hình chúng ta mô tả các thuật toán như là chương trình được viết bằngngôn ngữ mô phỏng tương tự như ngôn ngữ C, pascal, Java Sự khác nhau giữa

mã giả và mã thật là gì? Chúng ta nhận thấy rằng cho dù ý nghĩa phương pháphầu hết là trong sáng và xúc tích để đưa ra một thuật toán Nhiều khi phươngpháp trong sáng nhất là Tiếng Anh, vì vậy không phải ngạc nhiên nếu bạn dùngmột mệnh đề hoặc một câu Tiếng Anh mà không có từ “real” Một sự khác nhaugiữa mã giả và mã thật là mã giả không được sự quan tâm điển hình với việc cấpphát bộ nhớ của kỹ sư phần mềm Việc cấp phát bộ nhớ của dữ liệu được hiểu

Trang 5

ngầm, các đơn vị đo và xử lý lỗi được đặt ra để diễn đạt thuật toán một cách xúctích hơn.

Chúng ta bắt đầu với thuật toán sắp xếp chèn mà kết quả của thuật toánnày là sắp xếp một số phần tử nhỏ Thuật toán này làm việc như là cách người tasắp xếp những con bài trên tay Bắt đầu là tay trái rỗng và các con bài được úpxuống bàn Sau đó, trong cùng một thời gian chúng ta di chuyển một con bài từbàn và chèn vào vị trí chính xác trên bàn tay trái Để tìm vị trí chính xác cho mộtcon bài chúng ta so sánh nó với một con bài đã được sắp xếp sẵn sàng trên tay từphải qua trái Tại cùng một thời điểm, những con bài đã được đặt trên bài tay tráiđược sắp xếp và những con bài này là khởi đầu của những con bài trên đỉnh củabàn

Hình 2.1

Đoạn chương trình mô phỏng dưới đây cho ta giải thuật sắp xếp chèn Nó

thực hiện các thông số đầu vào là một mảng gồm n phần tử tức là chứa đựng n

độ dài cần được sắp xếp Đầu vào của mảng A là một dãy chưa được sắp xếp vàsau khi thuật toán kết thúc cho ta một mảng A đã được sắp xếp

Với “pseudecode” ta có thủ tục:

INSERTION-SORT(A)

1 for i ← 2 to length[A]

2 do m ← A[i] {m là khóa }

Trang 6

5 while j > 0 and A[j] > m

6 do A[j + 1] ← A[j]

7 j ← j - 1

8 A[j + 1] ← m

Diễn giải các bước lặp của thuật toán sắp xếp chèn

Ở hình bên dưới đây cho ta một mảng A với dãy số chưa được sắp xếp

A = (5, 2, 4, 6, 1, 3)

Đầu tiên ta xem chỉ số i như là con bài hiện tại đang ở trên tay Cho vòng lặp For chạy bởi i nó sẽ chia mảng A thành 2 đoạn A[1 j - 1] đây là đoạn cần được sắp xếp và A[j + 1 n] là số con bài còn lại ở trên bàn chưa được đưa vào Kết

quả các bước sắp xếp cho ta một dãy tăng dần như sau:

Hình 2.2

Cách sử dụng mã giả

1- Thụt đầu dòng để nêu rõ cấu trúc khối

2- Cấu trúc vòng lặp while, for và repeat và những cấu trúc có điều kiện

như if, then và else được thể hiện giống nhau như trong pascal

3- Ký tự "►" để ghi chú hoặc giải thích một đoạn lệnh

4- Phép gán nhiều biến cho một giá trị nào đó như: i ← j ← e tức là cả hai biến i và j là có giá trị e nó nên được xem là tương đương như là phép gán j ← e theo sau phép gán i ← j.

5- Biến (như là i, j và key) là biến cục bộ trong thủ tục đã cho trên Chúng

ta không nên sử dụng biến toàn cục với những mục đích không rõ

Trang 7

6- Các thành phần mảng được truy cập bằng cách đặc tả tên mảng theo sau

là chỉ số trong các dấu ngoặc vuông Ví dụ A[i] nêu rõ thành phần thứ i trong

mảng A

7- Dữ liệu phức hợp thường được tổ chức thành các đối tượng bao hàmcác thuộc tính hoặc các trường Để truy cập một trường cụ thể ta dùng tên trườngtheo sau là tên đối tượng của nó trong các dấu ngoặc vuông

Ví dụ ta xem một mảng như một đối tượng có thuộc tính length nêu rõ sốlượng thành phần mà nó chứa Để đặc tả số lượng thành phần trong mảng A taviết Length[A]

8- Việc cấp phát bộ nhớ sẽ được hiểu ngầm

Bài tập 2.1.1

Sắp xếp mảng A =〈31, 41, 59, 26, 41, 58> theo thứ tự tăng dần bằng mô

hình chuyển đổi thứ tự theo phương pháp chèn

2.2 Phân tích thuật toán

Phân tích một thuật toán nghĩa là dự đoán các tài nguyên (resource) màthuật toán yêu cầu Đôi khi, các tài nguyên như là bộ nhớ, băng thông mạng, hay

là phần cứng máy tính đều là mối quan tâm chính, nhưng thường thì chính thờigian tính toán mới là yếu tố mà chúng ta cần đo lường Nhìn chung, nhờ việcphân tích một vài thuật toán cho một vấn đề, ta có thể nhận ra thuật toán nào làhiệu quả nhất Việc phân tích có thể chỉ ra nhiều hơn một thuật toán thích hợp cóthể làm được, nhưng một vài thuật toán sử dụng nhiều tài nguyên hơn thường bịloại bỏ ra khỏi quá trình tiến hành

Trước khi chúng ta có thể phân tích một thuật toán, ta phải có một mô

Trang 8

những tài nguyên kỹ thuật và các chi phí thực hiện nó Trong hầu hết cuốn sáchnày, chúng ta sẽ thừa nhận một vi xử lý có đặc điểm chung, mô hình máy truycập ngẫu nhiên (RAM), một bộ xử lý chung, làm công nghệ thực thi và và ngầmhiểu rằng các thuật toán của chúng sẽ được thực thi dưới dạng các chương trìnhmáy tính Trong mô hình RAM, các câu lệnh được thực hiện lần lượt, khôngcùng thực thi đồng thời Tuy nhiên, ở chương sau, chúng sẽ có dịp nghiên cứucác mô hình của phần cứng số.

Nói đúng ra, một thuật toán (one) nên có định nghĩa chính xác các câu

lệnh của mô hình RAM và các chi phí của chúng Tuy nhiên, nếu làm như vậy,

sẽ trở nên dài dòng và sinh ra chút sự hiểu biết bên trong thiết kế và phân tíchthuật toán Lúc đó, chúng ta phải cẩn thận việc lạm dụng mô hình RAM Ví dụ,RAM có câu lệnh sắp xếp thì sao? Thì chúng ta có thể sắp xếp trong vừa một câulệnh Nhưng RAM sẽ không tồn tại, nếu các máy tính thực không có các câulệnh Vì vậy, một gợi ý là làm thế nào các máy tính thực được thiết kế Mô hìnhRAM chứa các câu lệnh thường được tìm thấy trong các máy tính thực: số học(cộng, trừ, nhân, chia, chia không có số dư, số thấp nhất, số cao nhất), thao tác

dữ liệu (nạp, lưu trữ, sao chép), và điều khiển (tách có điều kiện và không cóđiều kiện, gọi thủ tục con và gọi lại) Mỗi câu lệnh như thế mất một khoảng thờigian cố định

Các kiểu dữ liệu trong mô hình RAM là số nguyên và dấu phẩy động(floating point) Mặc dù chúng ta không tự nắm rõ một cách đúng đắn độ chínhxác trong cuốn sách này (của kiểu dữ liệu), ứng dụng đòi hỏi có sự chính xác vềkiểu dữ liệu Chúng ta còn thừa nhận giới hạn của kích cỡ mỗi từ dữ liệu Ví dụ,

khi làm việc với những đầu vào kích cỡ n, chúng ta thừa nhận rằng các số nguyên được đại diện bởi c*log2n bit với hằng số c>=1 Chúng ta yêu cầu c>=1

để mỗi từ (bit) có thể lưu giữ giá trị của n, cho phép chúng ta đưa vào các phần

tử đầu vào riêng lẻ, và chúng ta giới hạn c là hằng số để kích cỡ từ không phát

triển tùy tiện (nếu kích cỡ từ có thể phát triển tùy tiện, trong thời gian cố định,chúng ta có thể lưu trữ một số lượng lớn dữ liệu trong một từ và vận hành trên nó– rõ ràng là phi thực tế)

Trang 9

Một số máy tính thực chứa các câu lệnh không được liệt kê ở trên, và một

số câu lệnh miêu tả vùng xám của mô hình RAM Ví dụ, có phải thời gian cốđịnh của câu lệnh là mũ hóa? Trong trường hợp tổng quát, không mất nhiều câu

lệnh để tính x y khi x và y là các số thực Tuy nhiên, trong trạng thái giới hạn, hàm

mũ là sự vận hành hằng số thời gian Một số máy tính có câu lệnh “shift left” (di

chuyển trái), ở đó hằng số thời gian đổi chỗ các bit số nguyên bởi k vị trí sang

bên trái Trong hầu hết các máy tính, việc đổi chỗ các bit số nguyên bằng một vị

trí ở bên trái là tương đương nhân với 2 Đổi chỗ các bit k vị trí sang bên trái là

tương đương phép nhân 2k Vì thế, một số máy tính có thể tính 2k trong một câu

lệnh hằng số thời gian bằng cách đổi chỗ số nguyên 1 qua k vị trí sang trái, ngay khi k không nhiều hơn số các bit trong kí tự máy tính Chúng ta sẽ nỗ lực để

tránh những vùng xám trong mô hình RAM, nhưng chúng ta sẽ xem xét phéptính 2k như phép tính hằng số thời gian khi k là số nguyên dương đủ nhỏ.

Trong mô hình RAM, chúng ta không cố gắng mô hình hóa thứ tự bộ nhớ

mà rất phổ biến ở các máy tính cùng thời Chúng ta không mô hình hóa các khốinhớ hoặc bộ nhớ ảo (mà thường được bổ sung với phân trang theo yêu cầu) Mộtvài mô hình tính toán cố gắng giải thích tác dụng của thứ tự bộ nhớ Một ít cácvấn đề trong sách này nghiên cứu tác dụng thứ tự bộ nhớ, nhưng ở một số phần,

sự phân tích trong sách này sẽ không xem xét đến Các mô hình bao gồm thứ tự

bộ nhớ là một bit phức tạp hơn mô hình RAM, để mà chúng có thể rất khó khănkhi làm việc với chúng Ngoài ra, việc phân tích mô hình RAM thường là dụng

cụ dự báo tuyệt vời hiệu suất của các máy tính thực

Phân tích thậm chí một thuật toán đơn giản trong mô hình RAM có thểđược xem là thách thức Các công cụ tính toán yêu cầu bao gồm các phép tính tổhợp, lý thuyết xác suất, phương trình đại số và khả năng nhận dạng hầu hết sốhạng quan trọng trong công thức Bởi vì các giải quyết một thuật toán có lẽ khácnhau ở từng đầu vào, nghĩa là chúng ta cần có biện pháp để tóm lược thành cáccông thức đơn giản, dễ hiểu

Thậm chí chúng ta chọn điển hình chỉ một mô hình máy để phân tích thuậttoán được đưa ra, chúng ta còn quay về nhiều sự lựa chọn trong việc quyết định

Trang 10

giản để viết, tính toán, và trình bày các đặc tính quan trọng của yêu cầu đầu vàocủa thuật toán, và các chi tiết dài dòng bị bỏ.

Phân tích kỹ thuật sắp xếp chèn

Thời gian thực hiện thủ tục ISERTION-SORT phụ thuộc vào đầu vào: tiếntrình sắp xếp hàng ngàn con sẽ số lâu hơn sắp xếp ba con số Hơn nữa,INSERTION-SORT có thể mất vài khoảng thời gian khác nhau để sắp xếp haichuỗi đầu vào của cùng một kích cỡ giống nhau, phụ thuộc vào mức độ sắp xếpsẵn của chúng Nói chung, thời gian thực hiện một thuật toán thường tăng theokích cỡ đầu vào, do đó theo cách truyền thống, để mô tả thời gian thực hiệnchương trình như một hàm kích cỡ đầu vào của chương trình Để làm được nhưvậy, chúng ta cần định nghĩa các thuật ngữ: thời gian thực hiện (running time) vàkích cỡ đầu vào cẩn thận hơn

Quan điểm tốt nhất của kích cỡ đầu vào phục thuộc vào bài toán đangnghiên cứu Đối với một số bài toán, như là sắp xếp hay tính toán rời rạc cácphép biến đổi Fourier, số đo tự nhiên nhất đó là số lượng các mục trong đầu vào

– ví dụ, kích cỡ mảng là n để sắp xếp Trong một số các bài toán khác, như nhân

hai số nguyên, số đo tốt nhất của kích cỡ đầu vào là tổng số các bit để biểu thịcho đầu vào theo ký hiệu nhị phân thông thường Thỉnh thoảng, việc mô tả kích

cỡ đầu vào bằng hai số nguyên thích hợp hơn là một số Ví dụ, nếu đầu vào củathuật toán là một đồ thị, kích cỡ đầu vào có thể được mô tả bằng số các đỉnh vàcạnh của đồ thị Chúng ta có thể nêu rõ kiểu đo kích cỡ đầu vào nào được sửdụng với mỗi bài toán mà chúng ta nghiên cứu

Thời gian thực hiện một thuật toán trên một đầu vào cụ thể chính là sốlượng các phép toán nguyên tố và các bước thực hiện Sẽ tiện dụng hơn nếu tađịnh nghĩa khái niệm “bước” để nó càng độc lập với máy càng tốt Quan trọng,hãy chấp nhận quan điểm sau Cần có một khoảng thời gian cố định để thực thitừng dòng giải mã của chúng ta Một dòng có thể mất một khoảng thời gian khác

so với dòng kia, nhưng chúng ta sẽ thừa nhận rằng mỗi lệnh thực thi của dòng

hình RAM và nó còn phản ánh cách thực thi giải mã trên hầu hết các máy tínhhiện nay.(*)

Trang 11

Trong đoạn mô tả dưới đây, cách diễn tả về thời gian thực hiện củaINSERTION-SORT sẽ suy ra từ công thức mờ mà nó sử dụng trong tất cả các

hao phí ci thành một ký hiệu đơn giản hơn mà ngắn gọn hơn và dễ dàng tính toán

hơn Ký hiệu đơn giản này sẽ giúp ta dễ dàng xác định một thuật toán có hiệu lựchơn thuật toán khác hay không

Chúng ta bắt đầu bằng việc đưa ra thủ tục INSERT-SORT với chi phí thờigian của mỗi câu lệnh và số lượng thời gian thực thi mỗi câu lệnh Với mỗi

j=2,3,…,n mà n = length[A], chúng ta cho t j là số thời gian thực hiện vòng lặp while ở dòng 5 theo giá trị j đó Khi vòng lặp for hoặc while kết thúc theo cách

thông thường (nhờ có kiểm tra vòng lặp đầu trước), kiểm tra được thực thi mộtthời gian hơn phần thân vòng lặp Chúng ta thừa nhận rằng các chú giải khôngphải là các câu lệnh thi hành, và chúng không mất thời gian với nó

INSERTION-SORT(A) cost times

t

2

) 1 (

t

2

1

8 A[i + 1] m c8 n - 1

Thời gian thực hiện của thuật toán là tổng thời gian thực thi mỗi câu lệnh

được thi hành; một câu lệnh mất ci bước để thực hiện và được thực hiện trong n lần sẽ thành ci n tổng thời gian thực hiện (**) Để tính T(n), thời gian thực hiện củaINSERTION-SORT, chúng ta cộng các kết quả các cột chi phí và thời gian, thuđược:

T(n) = c 1 n+c 2 (n – 1) + c 4 (n – 1)+c 5

=

n

j j

t

2

) 1 ( +c 7

=

n

j j

Trang 12

dụ, trong INSERTION-SORT, trường hợp tốt nhất xảy ra nếu mảng được sắp

xếp sẵn rồi Với mỗi j=2,3,….,n, chúng ta thấy A[i]<=key ở dòng 5 khi i có giá trị ban đầu là j-1 Như vậy, tj = 1 với j = 2,3,…,n và thời gian thực thi tốt nhất là:

T(n) = c 1 n + c 2 (n-1)+ c 4 (n-1)+ c 5 (n-1)+ c 8 (n-1)

= (c 1 + c 2 + c 4 + c 5 + c 8 )n - (c 1 + c 2 + c 4 + c 5 + c 8 )

Thời gian thực hiện này có thể được mô tả như an+b với các hằng số a và

b phụ thuộc vào hao phí câu lệnh c i; như vậy, nó là một hàm tuyến tính của n.

Nếu mảng được sắp xếp theo thứ tự đảo ngược – nghĩa là theo thứ tự giảm – trường hợp xấu nhất sẽ xảy ra Chúng ta phải so sánh mỗi giá trị A[j] với mỗi giá trị trong toàn bộ mảng con A[1 j-1] đã sắp xếp và tj = j trong đó j=2,3, ,n.

Lưu ý rằng:

1 2

) 1 (

) 1 ( ) 1 (

(Chương 3 sẽ đề cập lại các phép tổng này), ta thấy rằng trong trường hợpxấu nhất, thời gian thực hiện của INSERTION-SORT là

) 1

(n

n

+c8(n – 1) =  2 + 2 + 2 

7 6

5 c c

c n 2 +  1+ 2+ 4+ 5 − 6− 7+ 8 

2 2

c c c c c

Thời gian thực hiện trong trường hợp xấu nhất này có thể được diễn tả

hiện lệnh cj ; do đó nó là hàm bậc hai của n

Thông thường như trong trường hợp sắp xếp theo kiểu chèn (insertionsort), thời gian thực thi một thuật toán được cố định với một đầu vào được cho,mặc dù trong các chương sau, chúng ta có thể thấy một số thuật toán ngẫu nhiên

mà cách hoạt động của nó có thể biến thiên ngay cả giá trị đầu vào cố định

Trang 13

Phân tích trường hợp xấu nhất và trường hợp trung bình

Trong việc phân tích sắp xếp theo kiểu chèn ở trên, chúng ta xem xét cảcách tốt nhất, mà mảng đầu vào đã được sắp xếp sẵn, và trong trường hợp xấunhất, mà mảng đầu vào đã được sắp xếp đảo ngược Tuy nhiên, phần còn lại củacuốn sách này, chúng ta thường tập trung vào việc chỉ tìm kiếm thời gian thựchiện xấu nhất, nghĩa là, thời gian thực hiện là dài nhất cho bất kỳ một đầu vào

nào có kích cỡ là n Sở dĩ như vậy vì ba lý do như sau:

* Thời gian thực hiện xấu nhất của một thuật toán là cận trên đối với thờigian thực hiện của bất kỳ đầu vào nào Biết rằng nó cho chúng ta sự đảm bảorằng thuật toán sẽ không bao giờ kéo dài lâu hơn nữa Chúng ta không cần phảisuy đoán này nọ về thời gian thực hiện và hy vọng rằng nó không trở nên có thểxấu hơn thế

* Đối với một số thuật toán, trường hợp xấu nhất xuất hiện khá thườngxuyên Chẳng hạn như trong việc tìm kiếm một dữ liệu cụ thể trong một cơ sở dữliệu, trường hợp xấu nhất của thuật toán thường xảy ra khi thông tin đó khônghiện diện trong cơ sở dữ liệu Trong một số ứng dụng tìm kiếm, ta thường gặpcác trường hợp tìm kiếm thông tin không có mặt

* Trường hợp trung bình cũng tệ hại tương tự như trường hợp không xấu

nhất Giả sử là chúng ta chọn ngẫu nhiên n con số và áp dụng kỹ thuật tìm kiếm theo kiểu sắp xếp chèn Sẽ mất bao lâu để xác định vị trí trong mảng con A[1 j-

1] để chèn số hạng A[j]? Tính trung bình, một nửa các số hạng trong A[1 j-1] là

nhỏ hơn A[j] và một nửa số hạng là lớn hơn Như vậy, tính trung bình, chúng ta

quả là trường hợp trung bình thời gian thực thi, nó thành ra trở thành một hàmbậc 2 của kích cỡ đầu vào, hệt như thời gian thực hiện trường hợp xấu nhất

Trong một số trường hợp cụ thể, chúng ta sẽ quan tâm đến thời gian thựchiện mong đợi hoặc trường hợp trung bình của thuật toán, trong Chương 5 chúng

ta sẽ thấy kỹ thuật phân tích theo xác suất, bằng cách này, chúng ta xác định thờigian thực thi được mong đợi Một bài toán có thể tiến hành phân tích trường hợptrung bình, đó là khi không nắm rõ nội dung tạo thành một đầu vào “trung bình”

Trang 14

vào có cùng một kích cỡ nhất định có thể xảy ra như nhau Trong thực tế, giảthiết này có lẽ bị trái ngược, song đôi lúc, chúng ta buộc phải sử dụng thuật toánngẫu nhiên, mà nó xây dựng các lựa chọn ngẫu nhiên, để cho phép các phân tíchtheo xác suất.

Cấp tăng trưởng

Chúng ta đã từng sử dụng một số khái niệm trừu tượng được đơn giản hóa

để tạo thuận lợi cho tiến trình phân tích thủ tục INSERTION-SORT Đầu tiên,

chúng ta đã bỏ qua các chi phí thực tế của mỗi câu lệnh, sử dụng hằng số ci để

đại diện cho chi phí của nó Sau đó, chúng ta quan sát ngay cả các hằng số của

chúng cho ta biết chi tiết hơn ta cần: thời gian thực hiện xấu nhất là an 2 + bn + c

với hằng số a, b và c phụ thuộc vào giá trị câu lệnh ci Vì thế chúng ta bỏ qua không chỉ chi phí câu lệnh thực tế mà còn bỏ qua chi phí trừu tượng ci.

Bây giờ chúng ta sẽ làm nhiều hơn một khái niệm trừu tượng được đơngiản hóa Nó là tỷ lệ phát triển hay cấp tăng trưởng của thời gian thực thi mà tathực sự quan tâm Vì thế chúng ta xem như chỉ giới hạn chủ đạo của công thức

(ví dụ an 2 ) từ giới hạn thứ tự thấp hơn là tương đối tầm thường với n lớn chúng

ta còn bỏ qua hệ số hằng số giới hạn chủ đạo, từ khi thừa số hằng số ít có ý nghĩahơn tỷ lệ phát triển trong việc xác định hiệu suất tính toán với đầu vào lớn Vì

(đọc là têta của n bình phương) Chúng ta sẽ sử dụng luôn ký hiệu Θ trongchương này, nó sẽ được định nghĩa chính xác ở chương 3

Chúng ta thường xem một thuật toán trở nên có hiệu quả hơn thuật toánkhác nếu thời gian thực hiện xấu nhất của nó có một cấp tăng trưởng thấp hơn.Nhờ có các nhân tố hằng số và số hạng thứ tự thấp hơn, việc ước lượng có lẽ bị

chạy nhanh hơn trong trường hợp xấu nhất hơn thuật toán Θ(n3).

(*) : Có một sự khôn khéo ở đây Các bước thực hiện tính toán mà ta nêu ra bằng tiếng Anh thường là các thủ tục được yêu cầu một khoảng thời gian thực hiện thay đổi Ví dụ, về phía cuối của sách này, chúng ta nói: sắp xếp các điểm bằng điều phối biến x như chúng ta đã biết, mất hơn một khoảng thời gian không đổi Ngoài ra, cần chú ý rằng các câu lệnh được gọi là thủ tục con sẽ mất một

Trang 15

khoảng thời gian không đổi để thực thi, thông qua các thủ tục con này, dẫn chứng cho thấy có thể mất thời gian nhiều hơn Vì thế, chúng ta tách rời các tiến trình của cái được gọi là các tham số thủ tục con với nó từ tiến trình của việc thực thi thủ tục con.

(**) : Các đặc tính này không cần thiết để lưu giữ tài nguyên như bộ nhớ Câu lệnh mà nó tham chiếu m ký tự của bộ nhớ và được thực thi trong n lần không thực sự cần thiết phải dùng hết m*n ký tự trong tổng bộ nhớ.

Bài tập 2.2-1

Biễu diễn hàm n3/1000 – 100n2 – 100n + 3 trong số hạng ký hiệu Θ

Bài tập 2.2-2

Xem như việc sắp xếp n số lưu trong mảng A bằng việc tìm kiếm phần tử

nhỏ nhất đầu tiên của A và hoán đổi nó với phẩn tử A[1] Sau đó tìm phần tử nhỏ

nhất thứ hai trong A và hoán đổi nó với A[2] Tiếp tục như thế với n-1 số hạng

đầu tiên trong A Viết giải mã cho thuật toán này với kiểu sắp xếp lựa chọn

Thuật toán này duy trì vòng lặp không đổi nào? Tại sao cần chạy chỉ n-1 phần tử đầu tiên hơn là tất cả n phần tử? Cho thời gian thực thi tốt nhất và xấu nhất của

cách sắp xếp lựa chọn này trong ký hiệu Θ

Bài tập 2.2-4

Chúng ta bổ sung thuật toán như thế nào để có thời gian thực thi tối ưutốt?

Trang 16

2.3 Thiết kế thuật toán

Có nhiều cách để thiết kế một thuật toán: Ví dụ như sắp xếp chèn sử dụng

cách tiếp cận tăng dần (incremental) để sắp xếp mảng A[1 j-1], chúng ta chèn một thành phần A[j] đơn lẻ vào nơi thích hợp, vùng đã sắp xếp mảng A[1 j]

Trong phần này chúng ta nghiên cứu một phương pháp thiết kế khác, đượchiểu như là: “divide-and-conquer” (chia để trị) Chúng ta sử dụng “divide-and-conquer” để thiết kế một thuật toán sắp xếp mà trường hợp xấu nhất của phươngpháp này là thời gian thực hiện là ít hơn so với thuật toán sắp xếp chèn Thuậnlợi của thuật toán “divide-and-conquer” là thời gian thực hiện của nó dễ dàngquyết định việc sử dụng phương pháp và nó được giới thiệu trong Chương 4

2.3.1 Nguyên lý “Divide-and-conquer” (chia để trị)

Những thuật toán hữu ích đó là cấu trúc đệ quy (recursive): để giải quyếtmột bài toán được đưa ra, chúng gọi đệ quy đến chúng một hoặc nhiều lần để xử

lý các bài toán con liên quan với nhau Nguyên lý “divide-and-conquer” đượchiểu như sau: Chúng ngắt những bài toán thành nhiều bài toán con tương tự vớibài toán ban đầu nhưng kích thước nhỏ hơn, xử lý những bài toán con đệ quy vàsau đó kết nối những cách xử lý này để tạo ra một phương pháp giải quyết chobài toán gốc

Nguyên lý “divide-and-conquer” bao gồm 3 bước tại mỗi mức của đệquy:

- Divide: Chia bài toán thành một số bài toán con

- Conquer: Xâm chiếm vào những bài toán con bằng việc giải quyết nó đệ

quy Nếu kích thước bài toán này là đủ nhỏ, giải quyết bài toán này bằng cáchtiến thẳng

- Combine: Kết hợp nhiều phương pháp giải quyết các bài toán con thành

phương pháp giải quyết cho bài toán gốc

* Ứng dụng nguyên lý “divide-and-conquer” vào thuật toán sắp xếp hòanhập:

Divide: chia dãy n đã được sắp xếp thành hai dãy con thành phần n/2

Trang 17

Conquer: Sắp xếp hai dãy con bằng cách đệ quy sử dụng phương pháp

sắp xếp trộn

Combine: Trộn hai dãy con đã được sắp xếp để tạo thành một dãy và sắp

xếp

Đệ quy “bottoms out” (dưới lên) khi sắp xếp một dãy có chiều dài là 1,

trong trường hợp dãy đó không làm gì cả thì cứ mỗi dãy có chiều dài bằng 1 làđược sắp xếp thứ tự

Chìa khoá của việc thực hiện thuật toán sắp xếp hòa nhập là hòa nhập haidãy đã được sắp xếp thành “combine” bước (bước kết hợp) Để thực hiện việc

hòa nhập chúng ta sử dụng một thủ tục con Merge (A,p,q,r) mà ở đó A là mảng

và p,q,r là các chỉ số phần tử của mảng, và pq<r Thủ tục này chỉ ra rằng mảng

con A[p q] và mảng con A[q+1 r] là được sắp xếp thứ tự Nó hòa nhập hai

mảng con này để thiết lập mảng con đã được sắp xếp đơn lẻ thay thế cho mảng

A[p r] hiện thời.

Thủ tục MERGE này tiêu tốn thời gian là Θ(n) mà n=r-p+1số lần hòa

nhập và nó làm việc như sau: Trở lại với bài toán chơi bài, chúng ta có hai lá bàilật ngửa ở trên bàn Mỗi lá bài là đã sắp xếp, lấy những lá bài nhỏ nhất ở trênđỉnh, chúng ta hòa nhập hai lá bài này thành một lá bài đã sắp xếp riêng lẻ xuất

ra mà nó đã được úp lại trên bàn Ta muốn hòa nhập hai lá bài thành một lá bàixuất ra và đã được sắp xếp mà mỗi lá bài là úp lại trên mặt bàn Bước cơ bản của

ta là chọn hai lá bài nhỏ hơn ở trên đỉnh của những lá lật ngửa, di chuyển nó từnhững lá (phơi bày ra một đỉnh bài mới) và vị trí của lá bài này úp trên lá bàixuất ra Chúng ta lặp lại bước này cho đến khi một lá bài nhập vào là trống, tạithời điểm này chúng ta chỉ còn lại lá bài nhập vào và vị trí của lá bài này úp lên

lá bài xuất ra.Theo như ước tính, mỗi bước cơ bản mất một hằng số lần Khi đó

ta chỉ kiểm tra hai lá bài ở trên đỉnh Lúc đó ta chỉ thực hiện n bước cơ bản Việc hòa nhập mất thời gian Θ(n) lần

Theo sau những ý tưởng giả mã, với sự cộng thêm vòng xoắn để tránhkhỏi kiểm tra, mặc dù mỗi lá bài là trống trong mỗi bước cơ bản Ý tưởng này là

Trang 18

trị đặc biệt nào đó để đơn giản cho việc viết mã Ở đây, chúng ta sử dụng ∞ như

là một giá trị đứng canh, vì vậy bất cứ khi nào một con bài ∞ được mở ra, nókhông thể nhỏ hơn trừ khi tất cả các lá bài có con bài đứng canh được mở ra.Nhưng một lần nữa điều này đã xảy ra, tất cả các con bài không đứng gác phải

sẵn sàng ở vị trí trên lá bài xuất ra Từ đó ta thấy là có chính xác r-p+1 lá bài sẽ ở

vị trí trên lá bài xuất ra, chúng ta phải ngừng một lần khi chúng ta thực hiện nótrong nhiều bước cơ bản

Từ thuật toán trên, thủ tục MERGE làm việc như sau: Dòng 1 của máy

tính có chiều dài n1 của mảng con A[p q] và dòng 2 thì có chiều dài n2 của mảng

con A[q+1 r] Chúng ta tạo ra mảng L và R (“left” và “right”) có chiều dài n1 +1

mảng A[p q] cho mảng L[1 n] và vòng lặp for ở dòng 6, 7 gán mảng A[q+1 r]

Dòng 10, 17 được minh hoạ trong Hình 2.3 thực hiện r-p+1 bước cơ bản bằng

việc duy trì theo sau biến vòng lặp Bắt đầu sự lặp lại của vòng lặp for của dòng

Trang 19

12-17, mảng A[p k-1] chứa k-p phần tử nhỏ nhất của mảng L[1 n1+1] và mảng

nhất của mảng mà không được gán cho mảng A

chứa những giá trị mà sẵn sàng đã được gán cho mảng A (a) - (h) Các mảng A,

L và R và những chỉ số k, i, j của chúng và chỉ số j trước sự lặp lại của vòng lặp

từ dòng 12-17.(i) các mảng và chỉ số tại thời điểm kết thúc Tại thời điểm nàymảng con A[9 16] là được sắp xếp và hai con bài đứng canh trong mảng L và R

là chỉ hai phần tử trong mảng này mà không được gán cho mảng A

Chúng ta phải thấy rằng biến vòng lặp này giữ ưu tiên của phép lặp forđầu tiên từ dòng 12-17 Tại mỗi phép lặp của vòng lặp, biến được duy trì và biếnnày cung cấp một thuộc tính hữu ích khi vòng lặp kết thúc

Trang 20

Initialization (Khởi tạo): Trước sự lặp lại của vòng lặp đầu tiên, chúng ta

có k=p vì vậy mảng con A[p k-1] là trống Mảng con trống này chứa k-p=0 phần

tử nhỏ nhất của mảng L và R và khi i=j=1,cả L[i] và R[j] là phần tử nhỏ nhất của

mảng mà không được gán từ mảng A

Hình 2.3

Maintenance (Sự duy trì): Để thấy rằng duy trì sự lặp đi lặp lại của mỗi

nhất chưa được gán từ mảng A, bởi vì mảng A[p k-1] chứa k-p phần tử nhỏ nhất, sau đó dòng 14 gán mảng L[i] cho A[k] Mảng con A[p k] sẽ chứa k-p+1 phần tử nhỏ nhất Tăng biến k (cập nhật lại vòng lặp for) thiết lập lại biến vòng lặp cho

sự lặp lại kế tiếp Nếu L[i]>R[j] thì dòng 16, 17 thực hiện hoạt động duy trì

biến vòng lặp

Termination (Sự kết thúc): Tại thời điểm kết thúc k=r+1.Vì biến vòng

lặp của mảng con A[p k-1], mà mảng A[p r] chứa k-p=r-p+1 phần tử nhỏ nhất

Trang 21

chứa n1+n2+2=r-p+3 phần tử Hai phần tử lớn được sao chép từ mảng A và hai

phần tử lớn này là hai lá bài đứng gác

Ta thấy rằng thủ tục MERGE chạy với thời gian Θ(n) lần mà n=r-p+1,

quan sát điều này ta thấy từ dòng 1-3 và dòng 8-11 mất thời gian là một hằng số

Vòng lặp for của dòng 4-7 mất Θ(n1+n2) = Θ(n) lần(6), từ dòng 12-17 vòng lặp

for lặp n lần, mà mỗi lần mất thời gian là một hằng số lần

Bây giờ chúng ta sử dụng thủ tục MERGE như là một thủ tục con trong

thuật toán sắp xếp trộn Thủ tục MERGE-SORT (A,p,r) sắp xếp những phần tử trong mảng con A[p r] Nếu p>=r thì hầu như các mảng con có một phần tử đã được sắp xếp Mặt khác bước chia đơn giản của một máy tính một chỉ số q mà phân vùng của mảng A[p r] thành hai mảng con: A[p q] chứa n/2 phần tử và A[q+1 r] chứa n/2 phần tử(7)

Hình 2.4 Minh hoạ việc thực hiện thủ tục “bottom-up” khi n là luỹ thừa

của 2 Thuật toán này thực hiện việc trộn những cặp của một khoảng dãy để hìnhthành những dãy đã được sắp xếp có chiều dài 2 Và sau đó trộn những cặp củanhững dãy có chiều dài là 2 thành những dãy đã được sắp xếp có chiều dài 4 Và

tiếp tục như vậy cho đến khi hai dãy đã được sắp xếp có chiều dài n/2 được trộn lại thành một dãy cuối cùng đã được sắp xếp có chiều dài n.

Hình 2.4 Thực hiện việc sắp xếp hòa nhập trên một mảng

A=(5,2,4,7,1,3,2,6) Chiều dài của dãy đã được sắp xếp và hòa nhập lại tăng lênkhi thuật toán tăng dần từ đáy lên đỉnh

Trang 22

Hình 2.4

2.3.2 Phân tích thuật toán divide and conquer

Khi một thuật toán chứa lời gọi đệ quy đến chính nó Thời gian thực hiệncủa nó có thể được mô tả bởi một công thức truy hồi cân bằng hoặc công thứctruy hồi và nó mô tả hầu hết thời gian thực hiện một bài toán có kích cỡ n cùngthời gian thực hiện nhỏ hơn đầu vào Sau đó chúng ta có thể sử dụng các phươngpháp toán học để giải quyết công thức truy hồi và cung cấp những giới hạn trongviệc thực hiện các thuật toán

Thời gian thực hiện công thức truy hồi của nguyên lý divide and conquer

là dựa trên 3 bước cơ bản Trước hết ta cho T(n) là thời gian thực hiện bài toán với kích thước n Nếu kích thước của bài toán là đủ nhỏ, ta nói n<=c (c là hằng

số) Giải pháp tiến thẳng mất thời gian là một hằng số mà ta viết là T(1).Việc

chia bài toán thành các bài toán con mà mỗi bài toán con có kích thước là 1/b bài toán ban đầu (Ví dụ như thuật toán trộn cả a và b đều cùng bằng 2, nhưng ta thấy rằng trong nhiều thuật toán divide-and-conquer thì a và b là khác nhau) Nếu ta mất D(n) lần để chia bài toán thành các bài toán con và C(n) lần để kết hợp các

cách giải quyết bài toán con thành cách giải quyết cho bài toán ban đầu Chúng

ta có công thức truy hồi như sau:

Trang 23

(1)( )

Phân tích thuật toán sắp xếp hòa nhập

Mặc dầu mã giả của thuật toán MERGE SORT viết đúng khi một số phần

tử là không thể xảy ra, thì cơ sở của công thức truy hồi này phân tích thật đơngiản Nếu chúng ta cho rằng kích cỡ của bài toán gốc là luỹ thừa của 2 Cứ mỗi

lần chia thì vùng của hai dãy con có kích thước chính xác là n/2 Trong Chương

4 chúng ta sẽ thấy rằng giả định này không có hiệu quả thứ tự phát triển giảipháp của công thức truy hồi

Ta lý luận để thành lập công thức truy hồi cho T(n) như sau: Trường hợp xấu nhất thời gian chạy của thuật toán MERGE SORT chỉ có n số MERGE SORT chỉ là một phần tử nên nó chỉ mất là hằng số lần Khi chúng ta có n>1

phần tử, chúng ta sẽ chia thời gian thực hiện như sau:

Divide: Chia từng bước để thực hiện phần tử giữa mảng con mà mỗi lần

chia mất thời gian là một hằng số Do đó, D(n)= Θ(1).

Conquer: Đệ quy để giải quyết hai bài toán con, mỗi bài có kích thước là

n/2 và thời gian chạy của nó là 2T(n/2).

Combine: Thủ tục MERGE của một mảng con n phần tử tiêu tốn thời gian

có một công thức truy hồi cho thuật toán MERGE SORT mà trường hợp xấu

nhất là thời gian chạy bằng T(n)

Trang 24

tuyến hàm nào khác Đối với thuật toán hòa nhập, dữ liệu nhập vào lớn và thời

gian thực hiện của nó là Θ(nlgn) và nó làm tốt hơn thuật toán sắp xếp chèn Trong trường hợp xấu nhất, thời gian chạy của nó là Θ(n2)

Ta không cần phải nắm vững nguyên tắc tại sao công thức truy hồi (2.1) là

T(n)= Θ(nlgn) Viết lại công thức truy hồi (2.1) như sau:

Và hằng số c mô tả thời gian yêu cầu giải quyết bài toán có kích thước là 1

tốt như là thời gian cho mỗi phần tử mảng của từng bước chia và kết hợp

Hình 2.5 Mô tả cách giải quyết công thức truy hồi (2.2) Ta thấy rằng n là

luỹ thừa của 2 Phần (a) biểu diễn T(n), mà trong phần (b) đã mở rộng ra thành một cây tương đương biểu diễn công thức truy hồi cn là gốc (giá trị tại mức đỉnh của đệ quy) và hai cây con của nó là hai công thức truy hồi T(n/2) nhỏ hơn Phần (c) chỉ ra quá trình thực hiện từng bước để mở rộng cây T(n/2) Giá trị cho hai nút con tại mức thứ hai của phép đệ quy là cn/2 Ta tiếp tục mở rộng các nút

trên cây bằng cách chẽ nó ra thành những cây thành phần bằng công thức truy

hồi cho đến khi kích cỡ của bài toán giảm xuống 1 mà mỗi nhánh là một giá trị c.

Phần (d) biểu diễn cây kết quả

Trang 25

Hình 2.5 Hình 2.5 Cấu trúc của một cây đệ quy cho ta một công thức truy hồi

Trang 26

trong (b) – (d) để thực hiện cây đệ quy Cây mở rộng đầy đủ trong phần (d) có

lgn +1 mức (i.e có chiều cao lgn đã cho biết) và mỗi mức có giá trị tổng là cn Vì vậy giá trị tổng là cnlgn +cn = Θ(nlgn).

Tiếp theo chúng ta thêm giá trị vào mỗi mức của cây Mức đỉnh có giá trị

tổng là cn, xuống mức tiếp theo có giá trị tổng là c(n/2) +c(n/2)=cn, mức sau có giá trị tổng là c(n/4) +c(n/4)+c(n/4)+c(n/4)=cn và cứ như vậy Nói chung mức i

dưới đỉnh có 2i nút, mỗi nút có một giá trị là c(n/2i) Vì vậy mức i dưới đỉnh có

giá trị tổng là 2i(cn/2i)=cn Tại mức đáy có n nút mà mỗi nút có giá trị c trong giá trị tổng cn.

Tổng số mức của cây đệ quy trong Hình 2.5 là lgn+1 Sự thật này dễ dàng thấy được bằng một lý luận quy nạp bất thường, trường hợp này xảy ra khi n=1

và trong trường hợp này chỉ có một mức Khi lg1=0, chúng ta thấy lgn+1 cho số

nút là lg2i+1=i+1( ứng với mọi giá trị nào của i, ta có lg2i =i).Ta thấy rằng kích

cỡ đầu nhập ban đầu là một luỹ thừa của 2 và kích cỡ đầu nhập tiếp theo là 2i+1

mức là (i+1)+1=lg2i+1+1

Để tính tổng giá trị mô tả của một công thức truy hồi ở Hình 2.2 Đơn giản chúng ta chỉ thêm vào các giá trị của tất cả các mức.Có lgn+1 mức mà mỗi mức chứa giá trị cn trong tổng giá trị của cn(lgn+1)=cnlgn+cn.Thuật ngữ low-oder và hằng số c cho ta kết quả là Θ(nlgn).

Bài tập 2.3.3

Sử dụng công thức toán học để biểu diễn khi n chính xác là một luỹ thừacủa 2 thì giải pháp của công thức truy hồi là

Trang 27

Phương pháp sắp xếp chèn được mô tả như là một thủ tục đệ quy như

sau.Để sắp xếp mảng A[1 n] ta sắp xếp đệ quy mảng A[1 n-1] và sau đó chèn mảng A[n] đến mảng đã được sắp xếp A[1 n-1] Viết ra một công thức truy hồi

cho thời gian chạy trên phiên bản mới sắp xếp chèn phép đệ quy

Bài tập 2.3.5

Trở lại việc tìm hiểu bài toán như bài 2.3.1 quan sát ta thấy rằng nếu mộtdãy A đã được sắp xếp Chúng ta có thể kiểm tra điểm giữa của dãy v và loại bỏnữa dãy từ sự xuy xét xa hơn Tìm kiếm nhị phân là một thuật toán mà lặp đi lặplại thủ tục này Cứ mỗi lần như vậy chúng ta chia đôi phần chia kích cỡ dãy cònlại.Viết giả mã Viết lặp hoặc đệ quy cho tìm kiếm nhị phân Nhận thấy rằngtrường hợp xấu nhất thời gian chạy của tìm kiếm nhị phân là Θ(lgn).

Bài tập 2.3.6

Quan sát ta thấy vòng lặp While của dòng 5-7 của thủ tục sắp xếp chèntrong phần 2.1 sử dụng tuyến tìm kiếm để quét (từ phía sau) của mảng con đã

được sắp xếp A[1 j-1].Chúng ta có thể dùng phương pháp tìm kiếm nhị phân

(nlgn) trong trường hợp xấu nhất.

Bài tập 2.3.7

nguyên và số nguyên x khác, mặc dù không thể tồn tại hai phần tử trong S màtổng của nó chính xác là bằng x

Bài toán 2.1

Trang 28

Mặc dầu thời gian thực hiện trong trường hợp xấu nhất của sắp xếp trộn là

Hệ số hằng trong sắp xếp chèn làm nó nhanh hơn khi n nhỏ Thật vậy điều đó có

nghĩa là sử dụng phương pháp chèn mà không cần phương pháp hòa nhập khinhững bài toán con là đủ nhỏ Xem xét sự thay đổi cho phương pháp sắp xếp hòa

nhập cho n/k dãy con có chiều dài là k đã sắp xếp việc dùng phương pháp sắp xếp chèn, sau đó hòa nhập lại việc dùng kỹ thuật hòa nhập tiêu chuẩn mà ở đó k

là một giá trị tiêu chuẩn được chọn

(nlg(n/k)) lần.

hợp xấu nhất của Θ(nk+nlg(n/k).Tiệm cận lớn nhất là gì (ký pháp Θ) giá trị của

k là một hàm của n cho việc xác định thuật tóan có cùng thời gian thực hiện tiệm

cận là sắp xếp hòa nhập chuẩn

Bài toán 2.2

Sự đúng đắn của phương pháp sắp xếp nổi bọt

Sắp xếp nổi bọt là một thuật toán phổ biến Nó thực hiện công việc đổichỗ cho những phần tử kế liền nhau không được sắp thứ tự

BUBBLESORT(A)

1 for i ← 1 to length[A]

2 do for j ← length[A] downto i + 1

3 do if A[j] < A[j - 1]

4 then exchange A[j] ↔ A[j - 1]

Lấy A’ bao gồm output của Bubblesort(A) Để chứng minh rằngBubblesort là đúng ta cần chứng minh nó là giới hạn cuối cùng và là

(2.3) A’[1] <=A’[2] <=A’[n]

Trang 29

Mà n=length[A] và mặt khác phải chứng minh được rằng BULLESORT

thật sự được sắp xếp

Hai phần tiếp theo sẽ được chứng minh bất đẳng thức(2.3)

Tình huống chính xác một biến vòng lặp cho vòng lặp for từ dòng 2-4 vàchứng minh rằng biến vòng lặp này được giữ lại Cách chứng minh của ta nên sửdụng cấu trúc phương pháp chứng minh biến vòng lặp được mô tả trong chươngnày

Sử dụng giới hạn điều kiện của vòng lặp đã được chứng minh trong phần(b) Tình trạng biến vòng lặp trong vòng lặp for ở dòng 1-4 cho phép bạn chứngminh bất đẳng thức 2.3 Sự chứng minh của bạn nên sử dụng cách chứng minhbiến vòng lặp đã được mô tả trong chương này

Thời gian thực hiện của phương pháp nổi bọt trong tình huống xấu nhất làgì? Nó so sánh với thời gian thực hiện của phương pháp chèn như thế nào?

Bài toán 2.3 Sự đúng đắn của nguyên lý Horner’s

Đoạn mã dưới đây bổ sung nguyên lý Horner’s để đánh giá một đa thức

k n

k

kx a

Đã đưa ra hệ số (a0… an) và một giá trị x

Trang 30

c Chứng minh rằng từ dòng 3- 5 dưới đây là một biến vòng lặp trong vòng lặpwhile.

Bắt đầu sự lặp lại của vòng lặp while từ dòng 3-5

Bài toán 2.4: Phép nghịch đảo

Lấy mảng A[1 n] là một mảng gồm n số khác nhau Nếu i<j và A[i]>A[j], sau đó cặp (i,j) được gọi là một phép nghịch đảo của mảng A.

a Liệt kê 5 phép đảo ngữ của mảng (2,3,8,6,1)

b Mảng gồm những phần tử từ tập hợp (1,2…n) có những phép đảo ngữ gì?

Có bao nhiêu phép đảo ngữ?

c Mối quan hệ giữa thời gian thực hiện thuật toán sắp xếp chèn và số phépđảo ngữ trong mảng nhập vào là gì? Viết ra câu trả lời của bạn?

d Đưa ra một thuật toán để chọn số phép đảo ngữ của bất cứ phép hoán vị nào

trên n phần tử của Θ(nlgn) trong trường hợp xấu nhất (gợi ý: thay đổi thuật toán

Trang 31

con A[p q] và A[q+1 r]có kích cỡ là n/2 và n/2 Thứ tự là để kiểm tra 4 trường hợp xảy ra phụ thuộc vào mỗi giá trị p và r, cho dù p và r là lẽ hay chẵn.

Không giống như cùng một hằng số chính xác minh hoạ thời gian để giảiquyết các bài toán có kích thước là 1 và số lần cho mỗi mảng phần tử của cácbước chia và kết hợp Chúng ta có thể giải quyết bài toán này bằng cách cho c làlớn hơn tại thời điểm này và để hiểu rằng công thức truy hồi của chúng ta đưa rathời gian thực hiện của giới hạn trên, hoặc là tại thời điểm này cho c là nhỏ hơn

và công thức truy hồi đưa ra thời gian thực hiện giới hạn dưới Cả hai giới hạntrên cùng thứ tự nlgn và được lấy cùng nhau cho ta thời gian thực hiện Θ(nlgn)

Ghi chú Chương 2

Vào năm 1968, Knuth đã xuất bản ra một trong ba quyển sách với tựa đề

là “The Art of Computer programming” [182,183,185] Cuốn sách đầu tiên đưa

ra mô hình học các thuật toán máy tính với chủ đề là phân tích thời gian thựchiện và một chuỗi đầy đủ còn lại của sự tham khảo hấp dẫn và đáng giá chonhiều loại chủ đề đã được mô tả ở đây.Theo như Knuth từ “algorithm” có nguồngốc từ tên “al- Khowarizmi”, một nhà toán học Iran thế kỷ 19

Aho, Hoporoft, Ullman đã ủng hộ tiệm cận phân tích thuật toán là mộtphương pháp so sánh mối quan hệ thực thi Ông cũng sử dụng phổ biến mốitương quan của công thức truy hồi để mô tả thời gian thực hiện của thuật toán đệquy

Knuth[185] cung cấp một bộ sách giáo khoa xử lý các thuật toán sắp xếp

Sự so sánh thuật toán sắp xếp của ông (trang 381) bao gồm các bước chính xác

là đếm- phân tích cũng là một mà chúng ta đã thực hiện ở đây cho thuật toán sắpxếp chèn.Ông Knuth’s đã thảo luận sắp chèn và sắp xếp hòa nhập bao gồm hàngloạt sự biến thiên của thuật toán Quan trọng nhất ở đây là sắp xếp Shell’s đượcgiới thiệu bởi D.L.Shell mà đã sử dụng sắp xếp chèn trên một dãy con nhập vàođịnh sẵn cho ta thuật toán sắp xếp nhanh hơn

Trang 32

Sắp xếp hòa nhập được mô tả bởi ông Knuth’s Ông chú ý rằng một cáimáy so sánh khả năng trộn hai cỗ bài của những lá bài dính vào nhau trong một

kỳ thi riêng lẽ đã được phát minh vào năm 1938.J.Von.Neumann, là một trongnhững nhà khoa học máy tính đầu tiên, hình như ông đã viết một chương trìnhcho thuật toán sắp xếp hòa nhập trên máy tính EDVAC vào năm 1945

Lịch sử sớm nhất của việc cải thiện chương trình được mô tả bởi ôngGries[133], người đã ghi nhận P.Naur với tựa đề đầu tiên trong lĩnh vực này.Gries đã xây dựng biến vòng lặp cho R.W.Floyd Tập sách được Mitchell[222]

mô tả tiến trình gần đây trong việc cải thiện chính xác chương trình

Trang 33

CHƯƠNG 3 TĂNG TRƯỞNG CỦA HÀM

Tổng quan

Cấp tăng trưởng thời gian thực hiện của một thuật toán, đã được địnhnghĩa trong Chương 2, cho một đặc tính đơn giản, hiệu quả của thuật toán, cũngcho phép chúng ta so sánh hiệu suất tương đối của các thuật toán thay thế Một

khi kích cỡ đầu vào n đủ lớn, phương pháp sắp xếp trộn (merge sort), thời gian thực hiện trong trường hợp xấu nhất là Θ(n lg n) trội hơn hẳn so với giải thuật

sắp xếp chèn (insertion sort), thời gian thực hiện trong trường hợp xấu nhất là

thuật toán, như trường hợp thuật toán sắp xếp chèn ở Chương 2, song độ chínhxác phụ trội thường không xứng với nỗ lực tính toán của nó Với dữ liệu đầu vào

đủ lớn, các hằng nhân (multiplicative constants) và số hạng cấp thấp của mộtthời gian thực hiện chính xác thường bị chi phối bởi những ảnh hưởng của chínhkích thước dữ liệu vào

Khi xem xét dữ liệu đầu vào đủ lớn để chỉ làm sự tăng trưởng của thờigian thực hiện có liên quan, ta đang nghiên cứu hiệu quả tiệm cận của thuật toán.Nghĩa là ta đề cập đến cách gia tăng thời gian thực hiện của một thuật toán vớikích thước dữ liệu vào trong một giới hạn nào đó, bởi kích cỡ đầu vào gia tăng

mà không có ranh giới Thông thường, một thuật toán hiệu quả hơn về mặt tiệmcận sẽ là sự lựa chọn tốt nhất cho tất cả ngoại trừ đầu vào là rất nhỏ

Chương này cung cấp một số phương pháp chuẩn cho việc đơn giản hóacách phân tích tiệm cận của các thuật toán Phần tiếp theo bắt đầu định nghĩa một

số loại ký hiệu tiệm cận, mà ta đã thấy một ví dụ trong hệ ký hiệu Θ Sau đótrình bày vài quy ước ký hiệu dùng trong suốt quyển sách này, và cuối cùng là ônlại cách ứng xử của các hàm thường nảy sinh trong quá trình phân tích thuậttoán

3.1 Hệ ký hiệu tiệm cận

Các ký hiệu mà ta dùng để mô tả thời gian thực hiện tiệm cận của một

Trang 34

hợp các số tự nhiên N={1,2,3….} Các ký hiệu như vậy tỏ ra thuận lợi cho việc

mô tả thời gian thực hiện của hàm T(n) trong trường hợp xấu nhất, nó thường chỉ

định nghĩa trên kích thước dữ liệu vào là số nguyên (integer) Tuy nhiên, đôi lúc

cũng tỏ ra tiện dụng khi lạm dụng hệ ký hiệu theo nhiều cách khác nhau Ví dụ,

ký hiệu dễ dàng mở rộng trên miền là tập số thực hoặc giới hạn của tập hợp concác số tự nhiên Tuy nhiên, điều quan trọng là hiểu chính xác nghĩa của những

ký hiệu sao cho khi bị lạm dụng, nó không bị dùng sai Chương này sẽ định

nghĩa những ký hiệu tiệm cận cơ bản và giới thiệu một số cách sử dụng phổ biến

Hệ ký hiệu Θ

Trong Chương 2, chúng ta đã tìm thấy trường hợp xấu nhất của thời gian

thực hiện thuật toán sắp xếp chèn là T(n) = Θ(n2) Giờ đây ta định nghĩa ý nghĩa

của hệ ký hiệu này Với một hàm đã cho g(n), qua Θg(n) ta thể hiện tập hợp các

hàm

Θ(g(n)) = {f(n): tồn tại các hằng dương c1, c2 và n0 sao cho 0 ≤ c1g(n) ≤ f(n) ≤ c2g(n),nn0}

Một hàm f(n) thuộc tập hợp Θ(g(n)) nếu tồn tại các hằng c1, c2 mà nó có

thể xen giữa c1 g(n), c2g(n) với n đủ lớn.

Bởi vì Θ(g(n)) là một tập hợp, chúng ta có thể viết " f(n) ∈ Θ (g(n))" để biểu

thị rằng f(n) là con của Θ(g(n)), hoặc "f(n) = Θ(g(n))" để nhấn mạnh cùng ý trên.

Hình 3.1 a) Minh họa trực quan các hàm f(n) và g(n), ở đó f(n) = Θ (g(n)).

Với tất cả các giá trị từ n đến bên phải n0, giá trị của f(n) nằm tại hoặc bên trên

c1g(n) và tại hoặc bên dưới c2g(n) Nói cách khác, với mọi nn0, hàm f(n) bằng

g(n) bên trong một thừa số bất biến Ta nói rằng, g(n) là một tiệm cận ràng buộc chặt chẽ của f(n).

Trang 35

Hình 3.1 Hình 3.1 Đồ thị biễu diễn những ví dụ về các ký hiệu Θ, O, và Ω Trong

mỗi phần, giá trị của n0 đã nêu là giá trị tối thiểu khả dĩ; bất kỳ giá trị lớn hơncũng sẽ cho dạng như thế

(a) Ký hiệu Θ ràng buộc một hàm nằm trong các thừa số bất biến Chúng

ta viết f(n) = Θ(g(n)) nếu tồn tại hằng dương n0, c1, và c2 sao cho ở bên phải của

n0, giá trị của f(n) luôn nằm giữa c1g(n) và c2g(n).

(b) Ký hiệu O cung cấp một cận trên cho một hàm nằm trong một thừa số

bất biến Chúng ta viết f(n) = O(g(n)) nếu tồn tại hằng số dương n0 và c sao cho

ở bên phải của n0, giá trị của f(n) luôn luôn nằm tại hoặc bên dưới cg (n)

(c) Ký hiệu Ω đưa ra một cận dưới cho một hàm nằm trong một thừa số

bất biến Chúng ta viết f(n) = Ω(g(n)) nếu tồn tại hằng dương n0 và c sao cho ở bên phải của n0, giá trị của f(n) luôn luôn nằm tại hoặc bên trên cg(n).

Định nghĩa của Θ(g(n)) yêu cầu mọi phần tử f(n) ∈ Θ (g(n))phải là tiệm

không âm, nghĩa là, f (n) không âm bất cứ trường hợp nào khi n là đủ lớn (Một hàm tích cực là một hàm mà những tiệm cận trong đó là tích cực cho tất cả n đủ lớn) Do vậy, bản thân hàm g(n) phải là tiệm không âm, hoặc ngược lại tập Θ(g(n)) là rỗng Do đó chúng ta sẽ giả định rằng tất cả các hàm được sử dụng

trong vòng ký hiệu Θ là tiệm không âm Giả thiết này cũng áp dụng cho nhữngkhái niệm tiệm cận khác được định nghĩa trong chương này

Chương 2 đã giới thiệu một khái niệm không chính thức của ký hiệu Θ màchung quy là loại bỏ các số hạng bậc thấp và bỏ qua hệ số đầu vào của số hạngbậc cao nhất Ta chỉ cần xác minh ngắn gọn trực giác này bằng cách sử dụng

Trang 36

định nghĩa chính thức để chỉ ra rằng 1 / 2n2 − 3n= Θ (n2 ) Để làm như vậy, chúng ta

phải xác định các hằng số dương c1, c2, và n0 sao cho: c1n2 ≤ 1/2n2 - 3n ≤ c2n2

Với mọi n ≥ n0 Chia biểu thức trên cho n2: c1 ≤ 1/2 - 3/n ≤ c2

Vế phải của bất đẳng thức có thể đúng với bất kỳ giá trị của n ≥ 1 bằng cách chọn c2 ≥ 1/2 Tương tự như vậy, vế trái của bất đẳng thức có thể đúng với

1/14, c2=1/2, và n0 = 7, chúng ta có thể chứng minh rằng 1 / 2n2 − 3n= Θ (n2 ) Tấtnhiên, vẫn có các chọn lựa khác cho các hằng, song điều quan trọng đó là việctồn tại một khả năng lựa chọn nào đó Lưu ý rằng các hằng này phụ thuộc vào

hàm 1/2n2 - 3n; một hàm khác thuộc Θ(n2) thường sẽ yêu cầu các hằng khác

Chúng ta cũng có thể sử dụng định nghĩa chính thức để chứng minh rằng

6n3 ≠ Θ(n2) Giả sử mâu thuẫn rằng tồn tại c2 và n0 sao cho 6n3 ≤ c2 n2 với mọi n ≥

n0 Nhưng sau đó n ≤ c2/6, không thể áp dụng cho n lớn một cách tùy ý, bởi vì c2

là hằng số

Theo trực giác, các số hạng cấp thấp của một tiệm cận hàm tích cực có thể

được bỏ qua trong việc xác định tiệm cận chặt chẽ, vì nó không đáng kể với n

lớn Một phần nhỏ của giới hạn bậc cao nhất là đủ để chiếm ưu thế so với các

giới hạn bậc thấp hơn Do đó, việc ấn định c1 theo một giá trị hơi nhỏ hơn hệ sốcủa số hạng bậc cao nhất và việc ấn định c2 theo một giá trị hơi lớn sẽ cho phépthỏa các bất đẳng thức trong định nghĩa của ký hiệu Θ Cũng vậy, ta có thể bỏ

qua hệ số của số hạng bậc cao nhất, vì nó chỉ thay đổi c1 và c2 theo một thừa sốkhông đổi bằng với hệ số đó

Ví dụ, xét hàm bậc hai bất kỳ f(n) =an2 +bn+c , trong đó a, b, c là hằng số

và a> 0 Bỏ qua những số hạng bậc thấp và bỏ qua các hằng sẽ cho ra f(n) = Θ (n2

) Về hình thức, để hiển thị cùng một nội dung, ta lấy các hằng số c1= a/4, c2 =7a/4, và n0 = 2 ∗ max(b/a, (c /a) Người đọc có thể chứng minh rằng

0

2 2 2

p

0

) (

với ai là hằng số và a d > 0, chúng ta có p(n) = Θ (n d) (xem Bài toán 3-1)

Trang 37

Vì một hằng bất kỳ là đa thức bậc 0, nên ta có thể biễu diễn hàm liên tụcbất kỳ là Θ(n0), hoặc Θ(1) Tuy nhiên, hệ ký hiệu sau là một lạm dụng nhỏ, bởi takhông rõ những biến là hướng đến vô cùng Ta thường xuyên phải sử dụng kýhiệu Θ(1) để chỉ một hằng hay một hàm liên tục đối với một số biến.

Hệ ký hiệu O

Các ký hiệu Θ là ký hiệu tiệm cận giới hạn của một hàm chặn trên và chặndưới Khi chúng ta chỉ có một tiệm cận chặn trên (asymptotic higher bound),

chúng ta sử dụng ký hiệu O Với một hàm đã cho g(n), bằng O(g(n)) (phát âm là

"O lớn của g của n" hoặc đôi khi chỉ cần "O của g của n") là tập hợp các hàm

O(g(n)) = {f(n): tồn tại hằng số dương c và no sao cho 0 ≤ f (n) ≤ cg(n), nn0}.

Ta sử dụng ký hiệu O để cung cấp một cận trên của một hàm, trong phạm

vi một thừa số bất biến Hình 3.1 b) biễu diễn hình ảnh trực quan của ký hiệu O Với mọi giá trị n đến bên phải n0, giá trị của hàm f (n) nằm tại hoặc bên dưới g (n).

Chúng ta viết f(n) = O(g(n)) để cho biết rằng một hàm f (n) là một phần tử của tập O (g(n)) Lưu ý rằng f (n) = Θ(g(n)) suy ra f (n) = O(g(n)), bởi vì ký hiệu

Θ là một khái niệm mạnh hơn ký hiệu O Viết theo lý thuyết tập hợp, chúng ta có

c

bn

an2 + + , trong đó a> 0, nghĩa là trong Θ(n2) cũng cho thấy rằng bất kỳ hàm

trong O(n2), dễ dàng được chứng minh bằng cách cho c = a + | b | và n0 = 1

Một số độc giả đã từng gặp hệ ký hiệu O trước đây có thể thấy lạ khi chúng

ta viết, ví dụ, n = O(n2) Trong tài liệu, ký hiệu O đôi khi được sử dụng để mô tảkhông chính thức tiệm cận chặt chẽ, nghĩa là, nội dung mà ta đã định nghĩa bằng

ký hiệu Θ Tuy nhiên, trong cuốn sách này, khi viết f(n) = O(g(n)), ta chỉ khẳng định rằng một số hằng số của g(n) là một tiệm cận giới hạn trên của f (n), mà

không xác nhận một cận trên sát đến mức nào Việc phân biệt các tiệm cận trên

và tiệm cận ràng buộc chặt chẽ giờ đây đã trở thành chuẩn trong các tài liệu thuậttoán

Ngày đăng: 11/04/2015, 22:08

TỪ KHÓA LIÊN QUAN

TÀI LIỆU CÙNG NGƯỜI DÙNG

TÀI LIỆU LIÊN QUAN

w