Sự cần thiết phải 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ố giải thuật khác nhau, vấn đề là cần phải đánh giá các giải thuật đó để lựa chọn một giải thuật tốt (nhất). Thông thường thì ta sẽ căn cứ vào các tiêu chuẩn sau: 1. Giải thuật đúng đắn. 2. Giải thuật đơn giản. 3. Giải thuật thực hiện nhanh.
Trang 1Bài Giảng Môn Học Phân Tích Và Thiết
Kế Thuật Toán
Biên tập bởi:
Đại Học Phương Đông
Trang 2Bài Giảng Môn Học Phân Tích Và Thiết
Trang 3MỤC LỤC
1 Độ phức tạp tính toán và tính hiệu quả của thuật toán
2 Mở đầu về thiết kế, đánh giá thuật toán và kiến thức bổ trợ
3 Phương pháp tham lam
4 Phương pháp “chia để trị”
5 Quy hoạch động
6 Thuật toán đồ thị cơ bản
Tham gia đóng góp
Trang 4Độ phức tạp tính toán và tính hiệu quả của thuật toán
Sự cần thiết phải 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ố giải thuật khác nhau, vấn đề làcần phải đánh giá các giải thuật đó để lựa chọn một giải thuật tốt (nhất) Thông thườngthì ta sẽ căn cứ vào các tiêu chuẩn sau:
1 Giải thuật đúng đắn
2 Giải thuật đơn giản
3 Giải thuật thực hiện nhanh
Với yêu cầu (1), để kiểm tra tính đúng đắn của giải thuật chúng ta có thể cài đặt giảithuật đó 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ể giải thuật đú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 giải thuật sai chứ chưa chứng minhđược là nó đúng Tính đúng đắn của giải thuật cần phải được chứng minh bằng toán học.Tất nhiên điều này không đơn giản và do vậy chúng ta sẽ không đề cập đến ở đây
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 (2) là quan trọngnhất 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ũngchỉ 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ì thì yêu cầu tiết kiệm thờigian 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 (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 giải thuật
Thời gian thực hiện của chương trình
Một phương pháp để xác định hiệu quả thời gian thực hiện của một giải thuật 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
Trang 5đố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 giải thuật mà còn phụ thuộc vào tậpcá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 đượcchọ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ứctạ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 giải thuật.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
Thời gian thực hiện chương trình.
Thời gian thực hiện m ộ t chương t r ì n h là một hàm của kích thước dữ liệu vào, kýhiệu T(n) trong đó n là kích thước (độ lớn) của dữ liệu vào
Chương trình tính tổng của n số có thời gian thực hiện là T(n) = cn trong đó c là mộthằng số
Thời gian thực hiện chương trình là một hàm không âm, tức là T(n) ≥ 0 với mọi n ≥ 0
Ðơn vị đo thời gian thực hiện.
Ðơn vị của T(n) không phải là đơn vị đo thời gian bình thường như giờ, phút giây
mà thường được xác định bởi số các lệnh được thực hiện trong một máy tính lý tưởng
Khi ta nói thời gian thực hiện của một chương trình là T(n) = Cn thì có nghĩa là chươngtrình ấy cần Cn chỉ thị thực thi
Thời gian thực hiện trong trường hợp xấu nhất.
Nói chung thì thời gian thực hiện chương trình 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ướcnhưng thời gian thực hiện chương trình có thể khác nhau Chẳng hạn chương trình sắpxế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ácvới khi ta cho vào dãy chưa có thứ tự, hoặc khi ta cho vào một dã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ấttrê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ươngtrình đối với mọi dữ liệu vào có cùng kích thước n
Trang 6Tỷ suất tăng và Ðộ phức tạp của giải thuật
Tỷ suất tăng
Ta nói rằng hàm không âm T(n) có tỷ suất tăng (growth rate) f(n) nếu tồn tại các hằng
số C và N0 sao cho T(n) ≤ Cf(n) với mọi n ≥ N0
Ta có thể c h ứ ng minh đư ợ c rằng “Cho m ộ t hàm không âm T(n) b ấ t kỳ, ta luôn tìm
đư ợ c t ỷ s u ất tăng f (n) c ủa nó”.
Giả sử T(0) = 1, T(1) = 4 và tổng quát T(n) = (n+1)2 Ðặt N0 = 1 và C = 4 thì với mọi n
≥1 chúng ta dễ dàng chứng minh được rằng T(n) = (n+1)2≤ 4n2với mọi n ≥ 1, tức là tỷsuất tăng của T(n) là n2
Tỷ suất tăng của hàm T(n) = 3n3+ 2n2là n3 Thực vậy, cho N0 = 0 và C = 5 ta dễ dàngchứng minh rằng với mọi n ≥ 0 thì 3n2+ 2n2≤ 5n3
Khái niệm độ phức tạp của giải thuật
Giả sử ta có hai giải thuật P1 và P2 với thời gian thực hiện tương ứng là T1(n) = 100n2
(với tỷ suất tăng là n2) và T2(n) = 5n3 (với tỷ suất tăng là n3) Giải thuật nào sẽ thựchiện nhanh hơn? Câu trả lời phụ thuộc vào kích thước dữ liệu vào Với n < 20 thì P2 sẽnhanh hơn P1 (T2<T1), do hệ số của 5n3nhỏ hơn hệ số của 100n2 (5<100) Nhưng khi
n > 20 thì ngươc lại do số mũ của 100n2 nhỏ hơn số mũ của 5n3(2<3) Ở đây chúng tachỉ nên quan tâm đến trường hợp n>20 vì khi n<20 thì thời gian thực hiện của cả P1 vàP2 đều không lớn và sự khác biệt giữa T1 và T2 là không đáng kể
Như vậy một cách hợp lý là ta xét tỷ suất tăng của hàm thời gian thực hiện chương trìnhthay vì xét chính bản thân thời gian thực hiện
Cho mộ t hàm T(n), T(n) g ọ i là có độ phức t ạ p f(n) n ế u t ồn t ạ i các hằng C, N 0 sao cho T(n) ≤ Cf(n) v ớ i m ọ i n ≥ N 0 (tức là T(n) có t ỷ suấ t t ăng là f(n)) và kí h i ệu T(n)
là O(f(n)) ( đọc là “ô c ủ a f(n)”)
T(n)= (n+1)2có tỷ suất tăng là n2nên T(n)= (n+1)2là O(n2)
Chú ý: O(C.f(n))=O(f(n)) với C là hằng số Ðặc biệt O(C) = O(1)
Nói cách khác độ phức tạp tính toán của giải thuật là một hàm chặn trên của hàm thờigian Vì hằng nhân tử C trong hàm chặn trên không có ý nghĩa nên ta có thể bỏ qua vìvậy hàm thể hiện độ phức tạp có các dạng thường gặp sau: log2n, n, nlog2n, n2, n3, 2n,n!, nn Ba hàm cuối cùng ta gọi là dạng hàm mũ, các hàm khác gọi là hàm đa thức Mộtgiải thuật 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
Trang 7tức là có thể cài đặt để thực hiện, còn các giải thuật có độ phức tạp hàm mũ thì phải tìmcách cải tiến giải thuật.
Vì ký hiệu log2n thường có mặt trong độ phức tạp nên trong khuôn khổ tài liệu này, ta
sẽ dùng logn thay thế cho log2n với mục đích duy nhất là để cho gọn trong cách viết
Khi nói đến độ phức tạp của giải thuật là ta muốn nói đến hiệu quả của thời gian thựchiện của chương trình nên ta có thể xem việc xác định thời gian thực hiên của chươngtrình chính là xác định độ phức tạp của giải thuật
nối tiếpnhaulà T(n)=O(max(f(n),g(n)))
Lệnh gán x:=15 tốn một hằng thời gian hay O(1), Lệnh đọc dữ liệu
READ(x) tốn một hằng thời gian hay O(1) Vậy thời gian thực hiện cả hai lệnh trên nốitiếp nhau là O(max(1,1))=O(1)
Qui tắc nhân
Nếu T1(n) và T2(n) là thời gian thực hiện của hai đoạn chương trình P1và P2 và T1(n)
= O(f(n)), T2(n) = O(g(n)) thì thời gian thực hiện của đoạn hai đoạn chương trình đó
lồngnhaulà T(n) = O(f(n).g(n))
Qui tắc tổng quát để phân tích một chương trình
Thời gian thực hiện của mỗi lệnh gán, READ, WRITE là O(1)
Thời gian thực hiện của một chuỗi tuần tự các lệnh được xác định bằng qui tắc cộng.Như vậy thời gian này là thời gian thi hành một lệnh nào đó lâu nhất trong chuỗi lệnh.Thời gian thực hiện cấu trúc IF là thời gian lớn nhất thực hiện lệnh sau THEN hoặc sauELSE và thời gian kiểm tra điều kiện Thường thời gian kiểm tra điều kiện là O(1)
Trang 8Thời gian thực hiện vòng lặp là tổng (trên tất cả các lần lặp) thời gian thực hiện thânvòng lặp Nếu thời gian thực hiện thân vòng lặp không đổi thì thời gian thực hiện vònglặp là tích của số lần lặp với thời gian thực hiện thân vòng lặp.
Tính thời gian thực hiện của thủ tục sắp xếp “nổi bọt”
PROCEDURE Bubble(VAR a: ARRAY[1 n] OF integer); VAR
i,j,temp: Integer; BEGIN {1} FOR i:=1 TO n-1 DO {2} FORj:=n DOWNTO i+1 DO {3} IF a[j-1]>a[j]THEN BEGIN{hoán vịa[i], a[j]} {4} temp := a[j-1]; {5} a[j-1] := a[j]; {6}a[j] := temp; END; END;
Về giải thuật sắp xếp nổi bọt, chúng ta sẽ bàn kĩ hơn trong chương 2 Ở đây, chúng tachỉ quan tâm đến độ phức tạp của giải thuật
Ta thấy toàn bộ chương trình chỉ gồm một lệnh lặp {1}, lồng trong lệnh {1} là lệnh{2}, lồng trong lệnh {2} là lệnh {3} và lồng trong lệnh {3} là 3 lệnh nối tiếp nhau{4}, {5} và {6} Chúng ta sẽ tiến hành tính độ phức tạp theo thứ tự từ trong ra
Trước hết, cả ba lệnh gán {4}, {5} và {6} đều tốn O(1) thời gian, việc so sánh a[j-1] >a[j] cũng tốn O(1) thời gian, do đó lệnh {3} tốn O(1) thời gian
Vòng lặp {2} thực hiện (n-i) lần, mỗi lần O(1) do đó vòng lặp {2} tốn O((n-i).1) = i).Vòng lặp {1} lặp có I chạy từ 1 đến n-1nên thời gian thực hiện của vòng lặp
O(n-{1} và cũng là độ phức tạp của giải thuật là:
Chú ý: Trong trường hợp vòng lặp không xác định được số lần lặp thì chúng ta phải lấy
số lần lặp trong trường hợp xấu nhất
Tìm kiếm tuần tự Hàm tìm kiếm Search nhận vào một mảng a có n số nguyên và một
số nguyên x, hàm sẽ trả về giá trị logic TRUE nếu tồn tại một phần tử a[i] = x, ngược lạihàm trả về FALSE
Trang 9Giải thuật tìm kiếm tuần tự là lần lượt so sánh x với các phần tử của mảng a, bắt đầu từa[1], nếu tồn tại a[i] = x thì dừng và trả về TRUE, ngược lại nếu tất cả các phần tử của ađều khác X thì trả về FALSE.
FUNCTION Search(a:ARRAY[1 n] OF
Integer;x:Integer):Boolean; VAR i:Integer; Found:Boolean;BEGIN {1} i:=1; {2} Found:=FALSE; {3} WHILE(i<=n)AND (notFound) DO {4} IF A[i]=X THEN Found:=TRUE ELSE i:=i+1; {5}Search:=Found; END;
Ta thấy các lệnh {1}, {2}, {3} và {5} nối tiếp nhau, do đó độ phức tạp của hàm Searchchính là độ phức tạp lớn nhất trong 4 lệnh này Dễ dàng thấy rằng ba lệnh {1}, {2} và{5} đều có độ phức tạp O(1) do đó độ phức tạp của hàm Search chính là độ phức tạp củalệnh {3} Lồng trong lệnh {3} là lệnh {4} Lệnh {4} có độ phức tạp O(1) Trong trườnghợp xấu nhất (tất cả các phần tử của mảng a đều khác x) thì vòng lặp {3} thực hiện nlần, vậy ta có T(n) = O(n)
Ðộ phức tạp của chương trình có gọi chương trình con không đệ quy
Nế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ờigian 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ácchươ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 gianthự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ệncủ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ỗichươ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:
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ìnhcon là B1 và B2, chương trình B1 gọi hai chương trình con là B11 và B12
Ðể tính thời gian thực hiện của A, ta tính theo các bước sau:
Trang 101 Tính thời gian thực hiện của C, B2, B11 và B12 Vì các chương trình con này khônggọi chương trình con nào cả.
2 Tính thời gian thực hiện của B1 Vì B1 gọi B11 và B12 mà thời gian thực hiện củaB11 và B12 đã được tính ở bước 1
3 Tính thời gian thực hiện của B Vì B gọi B1 và B2 mà thời gian thực hiện của B1 đãđược tính ở bước 2 và thời gian thực hiện của B2 đã được tính ở bước 1
4 Tính thời gian thực hiện của A Vì A gọi B và C mà thời gian thực hiện của B
đã được tính ở bước 3 và thời gian thực hiện của C đã được tính ở bước 1
Ta có thể viết lại chương trình sắp xếp bubble như sau: Trước hết chúng ta viết thủ tụcSwap để thực hiện việc hoàn đổi hai phần tử cho nhau, sau đó trong thủ
tục Bubble, khi cần ta sẽ gọi đến thủ tục Swap này
PROCEDURE Swap (VAR x, y: Integer); VAR temp: Integer;
BEGIN END; temp := x; x := y; y := temp; PROCEDURE Bubble(VAR a: ARRAY[1 n] OF integer); VAR i,j :Integer; BEGIN{1} FOR i:=1 TO n-1 DO {2} FOR j:=n DOWNTO i+1 DO {3} IFa[j-1]>a[j] THEN Swap(a[j-1], a[j]); END;
Trong cách viết trên, chương trình Bubble gọi chương trình con Swap, do đó để tínhthời gian thực hiện của Bubble, trước hết ta cần tính thời gian thực hiện của Swap
Dễ thấy thời gian thực hiện của Swap là O(1) vì nó chỉ bao gồm 3 lệnh gán TrongBubble, lệnh {3} gọi Swap nên chỉ tốn O(1), lệnh {2} thực hiện n-i lần, mỗi lần tốnO(1) nên tốn O(n-i) Lệnh {1} thực hiện n-1 lần nên:
Phân tích các chương trình Ðệ quy
Với các chương trình có gọi các chương trình con đệ quy, ta không thể áp dụng cáchtính như vừa trình bày trong mục 1.5.4 bởi vì một chương trình đệ quy sẽ gọi chính bảnthân nó Có thể thấy hình ảnh chương trình đệ quy A như sau:
Trang 11Với phương pháp tính độ phức tạp đã trình bày trong mục 1.5.4 thì không thể thực hiệnđược Bởi vì nếu theo phương pháp đó thì, để tính thời gian thực hiên của chương trình
A, ta phải tính thời gian thực hiện của chương trình A và cái vòng luẩn quẩn ấy khôngthể kết thúc được
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 đệ quy, sau đógiải phương trình đệ quy, nghiệm của phương trình đệ quy sẽ là thời gian thực hiện củachương trình đệ quy
Thành lập phương trình đệ quy
Phương trình đệ quy 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ờigian 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 đệ quy, 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ộttrườ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 đệ quy, 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 đệ quy dừng, ta phải xem xétkhi đó 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àitoá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 đệ quy sẽ là:
Trang 12Trong đó 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ợpcác kết quả.
Xét hàm tính giai thừa viết bằng giải thuật đệ quy như sau:
FUNCTION Giai_thua(n:Integer): Integer; BEGIN END; IF n=0then Giai_thua :=1 ELSE Giai_thua := n* Giai_thua(n-1);
Gọi T(n) là thời gian thực hiện việc tính n giai thừa, thì T(n-1) là thời gian thực hiệnviệc tính n-1 giai thừa Trong trường hợp n = 0 thì chương trình chỉ thực hiện một lệnhgán Giai_thua:=1, nên tốn O(1), do đó ta có T(0) = C1 Trong trường hợp n>0 chươngtrì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 Giai_thua Thờigian để 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 đệ quy để tính thời gian thực hiện của chương trình đệ quyGiai_thua
Chúng ta xét thủ tục MergeSort một cách phác thảo như sau:
FUNCTION MergeSort (L:List; n:Integer):List; VAR
L1,L2:List; BEGIN IF n=1 THEN RETURN(L) ELSE BEGIN Chiađôi L thành L1 và L2, với độ dài n/2;
RETURN(Merge(MergeSort(L1,n/2),MergeSort(L2,n/2))); END;END;
Trang 13Chẳng hạn để sắpxếp danh sách L gồm 8 phần tử 7, 4, 8, 9, 3, 1, 6, 2 ta có mô hình minh họa củaMergeSort như sau:
Hàm MergeSort nhận một danh sách có độ dài n và trả về một danh sách đã được sắpxếp Thủ tục Merge nhận hai danh sách đã được sắp L1 và L2 mỗi danh sách có độ dàin/2 trộn chúng lại với nhau để được một danh sách gồm n phần tử có thứ tự Giải thuậtchi tiết của Merge ta sẽ bàn sau, chúng ta chỉ để ý rằng thời gian để Merge các danh sách
có độ dài n/2 là O(n) Gọi T(n) là thời gian thực hiện MergeSort một danh sách n phần
tử thì T(n/2) là thời gian thực hiện MergeSort một danh sách n/2 phần tử Khi L có độdài 1 (n = 1) thì chương trình chỉ làm một việc duy nhất là return(L), việc này tốn O(1)
= C1 thời gian Trong trường hợp n > 1, chương trình phải thực hiện gọi đệ quy MerSorthai lần cho L1 và L2 với độ dài n/2 do đó thời gian để gọi hai lần đệ quy này là 2T(n/2).Ngoài ra còn phải tốn thời gian cho việc chia danh sách L thành hai nửa bằng nhau vàtrộn hai danh sách kết quả (Merge) Người ta xác đinh được thời gian để chia danh sách
và Merge là O(n) = C2n Vậy ta có phương trình đệ quy như sau:
Trang 14Các hàm tiến triển khác
Trong trường hợp hàm tiến triển không phải là một hàm nhân thì chúng ta không
thể áp dụng các công thức ứng với ba trường hợp nói trên mà chúng ta phải tính trựctiếp nghiệm riêng, sau đó so sánh với nghiệm thuần nhất để lấy nghiệm lớn nhất tronghai nghiệm đó làm nghiệm của phương trình
Vídụ2-17:Giải phương trình đệ quy sau: T(1) = 1
T(n) = 2T(n/2) + nlogn
Phương trình đã cho thuộc dạng phương trình tổng quát nhưng d(n) = nlogn không phải
là một hàm nhân
b Ta có nghiệm thuần nhất = nlog a = nlog2 = n
Do d(n) = nlogn không phải là hàm nhân nên ta phải tính nghiệm riêng bằng cách xéttrực tiếp
Theo giả thiết trong phương trình tổng quát thì n = bk nên k = logbn, ở đây do b=2nên 2k=n và k=logn, chúng ta có nghiệm riêng là O(nlog2n), nghiệm này lớn hơn
nghiệm thuần nhất do đó T(n) = O(nlog2n)
Bài tập chương
Tính thời gian thực hiện của các đoạn chương trình sau:
a) Tính tổng của các số
{1} Sum := 0;
Trang 15{2} for i:=1 to n do begin
Trang 16Cho một mảng n số nguyên được sắp thứ tự tăng Viết hàm tìm một số nguyên trong
mảng đó theo phương pháp tìmkiếmnhị phân, nếu tìm thấy thì trả về TRUE, ngược lại
trả về FALSE Sử dụng hai kĩ thuật là đệ quy và vòng lặp Với mỗi kĩ thuật hãy viết mộthàm tìm và tính thời gian thực hiện của hàm đó
Dành cho độc giả
Tính thời gian thực hiện của giải thuật đệ quy giải bài toán Tháp Hà nội với n tầng?Dành cho độc giả
Xét công thức truy toán để tính số tổ hợp chập k của n như sau:
a) Viết một hàm đệ quy để tính số tổ hợp chập k của n
b) Tính thời gian thực hiện của giải thuật nói trên
Dành cho độc giả
Trang 17Mở đầu về thiết kế, đánh giá thuật toán và kiến thức bổ trợ
Khái niệm thuật toán
Khái niệm về thuật toán
Thuật toán (algorithm) là một trong những khái niệm quan trọng trong lĩnh vực tin học.Thuật ngữ thuật toán được xuất phát từ nhà toán học Arập Abu Ja’far MohammedibnMusa al Khowarizmi (khoảng năm 825)
Tuy nhiên lúc bấy giờ và trong nhiều thế kỷ sau, nó không mang nội dung như ngày naychúng ta quan niệm Thuật toán nổi tiếng nhất có từ thời cổ Hy lạp là thuật toán Euclid,thuật toán tìm ước chung lớn nhất của hai số nguyên Có thể mô tả thuật toán đó nhưsau:
ThuậttoánEuclid.
Input: m, n nguyên dương
Output: g (ước chung lớn nhất của m và n)
Phương pháp:
Bước 1: Tìm r, phần dư của m cho n
Bước 2: Nếu r = 0, thì g:=n (gán giá trị của n cho g),và dừng lại Trong trường
hợp ngược lại (r≠0), thì m:=n; n:=r và quay lại bước 1
Chúng ta có thể quan niệm các bước cần thực hiện để làm một món ăn, được mô tả trongcác sách dạy chế biến món ăn, là một thuật toán Cũng có thể xem các bước cần tiếnhành để gấp đồ chơi bằng giấy ,được trình bày trong sách dạy gấp đồ chơi bằng giấy làmột thuật toán Phương pháp cộng nhân các số nguuyên, chúng ta đã được học
ở cấp I cũng là các thuật toán
Vì vậy ta có định nghĩa không hình thức về thuật toán như sau:
Thuật toán là một dãy hữu hạn các bước, mỗi bước mô tả chính xác các phép toán,
Trang 18hoặc hành động cần thực hiện để cho ta lời giải của bài toán.
Các yêu cầu về thuật toán
Định nghĩa trên về thuật toán tất nhiên còn chứa nhiều điều chưa rõ ràng Để hiểu đầy
đủ ý nghĩa của khái niệm thuật toán, chúng ta đưa ra 5 đặc trưng sau đây của thuật toán
Input
Mỗi thuật toán đều có một số (có thể bằng không) các dữ liệu vào (input) Đó là các giátrị cần đưa vào khi thuật toán bắt đầu làm việc Các dữ liệu này cần được lấy từ các tậphợp giá trị cụ thể nào đó Chẳng hạn, trong thuật toán Euclid ở trên, các số m và n là các
dữ liệu lấy từ tập các số nguyên dương
Output
Mỗi thuật toán cần có một hoặc nhiều dữ liệu ra (output) Đó là các giá trị có quan hệhoàn toàn xác định với các dữ liệu vào, và là kết quả của sự thực hiện thuật toán Trongthuật toán Euclid, có một dữ liệu ra đó là ƯSCLN g, khi thuật toán dừng lại (trường hợpr=0) thì giá trị của g là ước chung lớn nhất của m và n
Tính xác định
Ở mỗi bước, các bước thao tác phải hết sức rõ ràng, không gây nên sự nhập nhằng Nói
rõ hơn là trong cùng một điều kiện hai bộ xử lý cùng thực hiện một thuật toán phải chocùng một kết quả như nhau Nếu biểu diễn thuật toán bằng phương pháp thông thườngkhông có gì đảm bảo được người đọc hiểu đúng ý của người viết thuật toán Để đảm bảođòi hỏi này, thuật toán cần được mô tả trong các ngôn ngữ lập trình (ngôn ngữ máy, hợpngữ hoặc ngôn ngữ bậc cao như Pascal ) Trong các ngôn ngữ này các mệnh đề đượctạo theo các qui tắc cú pháp nghiêm ngặt và chỉ có một nghĩa duy nhất
Tính khả thi/đa năng
Tất cả các phép toán có mặt trong thuật toán phải đủ đơn giản Điều đó có nghĩa là, cácphép toán phải sao cho, ít nhất về nguyên tắc có thể thực hiện bởi con người chỉ bằnggiấy trắng và bút chì trong một khoảng thời gian hữu hạn Chẳng hạn, trong thuật toánEuclid ta chỉ cần thực hiện các phép chia các số nguyên, các phép gán và các phép sosánh r=0 hay r ≠ 0 Điều quan trọng nữa là thuật toán phải có tính đa năng làm việc đượcvới tất cả các tập hợp dữ liệu có thể của đầu vào
Tính dừng
Với mọi bộ dữ liệu vào thoả mãn các điều kiện của dữ liệu vào (tức là được lấy ra từ cáctập của dữ liệu vào), thuật toán phải dừng lại sau một số hữu hạn bước thực hiện
Trang 19Thuật toán Euclid thoả mãn điều kiện này Bởi vì giá trị của r luôn nhỏ hơn n (khi thựchiện bước 1), nếu r <>0 thì giá trị của n ở bước i (ký hiệu là ni) sẽ là giá trị của ri-1 ởbước i-1, ta phải có bất đẳng thức n>r =n1>r1 = n2 > r2 Dãy số nguyên dương này giảmdần và cần phải kết thúc ở 0, do đó sau một số hữu hạn bước nào đó giá trị của r phải =
0 và thuật toán phải dừng lại
Với một vấn đề đặt ra, có thể có một hoặc nhiều thuật toán giải Một vấn đề có thuật toángiải gọi là vấn đề giải được (bằng thuật toán) Chẳng hạn, tìm nghiệm của hệ phươngtrình tuyến tính là vấn đề giải được Một vấn đề không tồn tại thuật toán gọi là vấn đềkhông giải được (bằng thuật toán) Một trong những thành tựu suất xắc nhất của toánhọc thế kỷ 20 là đã tìm ra những vấn đề không giải được bằng thuật toán Chẳng hạnthuật toán chắc thắng cho người thứ hai của cờ ca rô hoặc thuật toán xác định xem mộtmáy Turing có dừng lại sau n bước không, đềulà những vấn đề không tồn tại thuật toángiải được
Thiết kế thuật toán
Để giải một bài toán trên máy tính điện tử (MTĐT), điều trước tiên là chúng ta phải cóthuật toán Một câu hỏi đặt ra là làm thế nào để tìm ra được thuật toán cho một bài toán
đã đặt ra- Lớp các bài toán được đặt ra từ các ngành khoa học kỹ thuật, từ các lĩnh vựchoạt động của con người là hết sức phong phú và đa dạng Các thuật toán giải các lớpbài toán khác nhau cũng rất khác nhau Tuy nhiên, có một số kỹ thuật thiết kế thuật toánchung như: Chia để trị (divide-and-conque), phương pháp tham ăn (greedy method), quihoạch động (dynamic programming) Việc nắm được các chiến lược thiết kế thuật toánnày là hết sức quan trọng và cần thiết vì nó giúp cho ta dễ tìm ra các thuật toán mới chocác bài toán mới được đưa ra
Tính đúng đắn của thuật toán
Khi một thuật toán được làm ra, ta cần phải chứng minh rằng, thuật toán khi được thựchiện sẽ cho ta kết quả đúng với mọi dữ liệu vào hợp lệ Điều này gọi là chứng minh tínhđúng đắn của thuật toán Việc chứng minh tính đúng đắn của thuật toán là một công việckhông dễ dàng Trong nhiều trường hợp, nó đòi hỏi ta phải có trình độ và khả năng tưduy toán học tốt
Sau đây ta sẽ chỉ ra rằng, khi thực hiện thuật toán Euclid, g sẽ là ước chung lớn nhấtcủa hai số nguyên dương bất kỳ m, n Thật vậy, khi thực hiện bước 1, ta có m = qn + r,trong đó q là số nguyên nào đó Nếu r = 0 thì n là ước của m và hiển nhiên n (do đó g)
là ước chung lớn nhất của m và n Nếu r 0 thì một ước chung bất kỳ của m và n cũng làước chung của n và r (vì r=m-qn) Ngược lại một ước chung bất kỳ của n và r cũng làước chung của m và n (vì m = qn + r) Do đó ước chung lớn nhất của n và r cũng là ướcchung lớn nhất của ma và n Vì vậy, khi thực hiện lặp lại bước 1, với sự thay đổi giá trị
Trang 20của m bởi n, và sự thay đổi giá trị của n bởi r, cho tới khi r=0 ta nhận được giá trị của g
là ước chung lớn nhất của các giá trị m và n ban đầu
Phân tích thuật toán
Giả sử, với một số bài toán nào đó chúng ta có một số thuật toán giải Một câu hỏi mớixuất hiện là, chúng ta cần chọn thuật toán nào trong số các thuật toán đó để áp dụng.Việc phân tích thuật toán, đánh giá độ phức tạp của thuật toán là nội dung của phần dướiđây sẽ giải quyết vấn đề này
Đánh giá hiệu quả của thuật toán
Khi giải một vấn đề, chúng ta cần chọn trong số các thuật toán, một thuật toán mà chúng
ta cho là “tốt” nhất Vậy ta cần lựa chọn thuật toán dựa trên cơ sở nào- Thông thường tadựa trên hai tiêu chuẩn sau đây:
• Thuật toán đơn giản, dễ hiểu, dễ cài đặt (dễ viết chương trình)
• Thuật toán sử dụng tiết kiệm nhất các nguồn tài nguyên của máy tính, và đặcbiệt chạy nhanh nhất có thể được
Khi ta viết một chương trình chỉ để sử dụng một số ít lần, và cái giá của thời gian viếtchương trình vượt xa cái giá của chạy chương trình thì tiêu chuẩn (1) là quan trọng nhất.Nhưng có trường hợp ta cần viết các chương trình (hoặc thủ tục, hàm) để sử dụng nhiềulần, cho nhiều người sử dụng, khi đó giá của thời gian chạy chương trình sẽ vượt xa giáviết nó Chẳng hạn, các thủ tục sắp xếp, tìm kiếm được sử dụng rất nhiều lần, bởi rấtnhiều người trong các bài toán khác nhau Trong trường hợp này ta cần dựa trên tiêuchuẩn 2 Ta sẽ cài đặt thuật táon có thể sẽ rất phức tạp, miễn là chương trình nhận đượcchạy nhanh hơn so với các chương trình khác
Tiêu chuẩn 2 được xem là tínhhiệuquảcủa thuật toán Tính hiệu quả của thuật toán bao
gồm hai nhân tố cơ bản:
Dung lượng không gian nhớ cần thiết để lưu giữ các giữ liệu vào, các kết quả tính toántrung gian và các kết quả của thuật toán
Thời gian cần thiết để thực hiện thuật toán (ta gọi là thời gian chạy) Chúng ta chỉ quantâm đến thời gian thực hiện thuậ toán, có nghĩa là ta nói đến đánh giá thời gian thựchiện Một thuật toán có hiệu quả được xem là thuật toán có thời gian chạy ít hơn so vớicác thuật toán khác
Trang 21Các phương pháp biểu diễn thuật toán
Có nhiều phương pháp biểu diễn thuật toán Có thể biểu diễn thuật toán bằng danh sáchcác bước, các bước được diễn đạt bằng ngôn ngữ thông thường và các ký hiệu toán học
Có thể biểu diễn thuật toán bằng sơ đồ khối Tuy nhiên, để đảm bảo tính xác định củathuật toán như đã trình bày trên, thuật toán cần được viết trên các ngôn ngữ lập trình.Một chương trình là sự biểu diễn của một thuật toán trong ngôn ngữ lập trình đã chọn.Thông thường ta dùng ngôn ngữ lập trình Pascal, một ngôn ngữ thường được chọn đểtrình bày các thuật toán trong sách báo
Ngôn ngữ thuật toán là ngôn ngữ dùng để miêu tả thuật toán Thông thường ngôn ngữthuật toán bao gồm ba loại:
+ Ngôn ngữ liệt kê từng bước;
+ Sơ đồ khối;
+ Ngôn ngữ lập trình;
Phương pháp liệt kê từng bước
Ngôn ngữ liệt kê từng bước nội dung như sau:
Thuật toán: Tên thuật toán và chức năng
Vào: Dữ liệu vào với tên kiểu
Ra: Các dữ liệu ra với tên kiểu
Biến phụ (nếu có) gồm tên kiểu
Hành động là các thao tác với các lệnh có nhãn là các số tự nhiên
Để giải phương trình bậc hai ax2 + bx +c = 0, ta có thể mô tả thuật toán bằng
ngôn ngữ liệt kê như sau:
Trang 22Bước 4: Nếu Δ <0 thông báo phương trình vô nghiệm và chuyển sang bước 8 Bước 5:Nếu Δ=0, tính x1=x2= 2− b ∗ a và chuyển sang bước 7.
Bước 6: Tính x1= − b +2a√Δ , x2= − b −2a√Δ và chuyển sang bước 7 Bước 7: Thông báo cácnghiệm x1, x2
Bước 8: Kết thúc thuật toán
Phương pháp sơ đồ
Phương pháp dùng sơ đồ khối mô tả thuật toán là dùng mô tả theo sơ đồ trên mặt
phẳng các bước của thuật toán Sơ đồ khối có ưu điểm là rất trực giác dễ bao quát
Để mô tả thuật toán bằng sơ đồ khối ta cần dựa vào các nút sau đây:
Nútthaotác:Biểu diễn bằng hình chữ nhật,
Nútđiềukhiển:Được biểu diễn bằng hình thoi, trong đó ghi điều kiện cần kiểm tra trong
quá trình tính toán
Nútkhởiđầu,kếtthúc:Thường được biểu diễn bằng hình
tròn thể hiện sự bắt đầu hay kết thúc quá trình
Cung:Đoạnnối từ nút này đến nút khác và có mũi tên chỉ hướng.
Trang 23Một số cấu trúc dữ liệu cơ bản
Danh sách
Danh sách là một tập sắp thứ tự các phần tử cùng một kiểu Đối với danh sách, người ta
có một số thao tác: Tìm một phần tử trong danh sách, chèn một phần tử vào danh sách,xoá một phần tử khỏi danh sách, sắp xếp lại các phần tử trong danh sách theo một trật
Trang 24- Danh sách nối kép
- Danh sách nối vòng một hướng
- Danh sách nối vòng hai hướng
Các phép toán cơ bản trên danh sách
Để thiết lập kiểu dữ liệu trừu tượng danh sách (hay ngắn gọn là danh sách) ta phải địnhnghĩa các phép toán trên danh sách Và như chúng ta sẽ thấy trong toàn bộ giáo trình,khôngc ó một tập hợp các phép toán nào thích hợp cho mọi ứng dụng (application) Vìvậy ở đây ta sẽ định nghĩa một số phép toán cơ bản nhất trên danh sách Để thuận tiệncho việc định nghĩa ta giả sử rằng danh sách gồm các phần tử có kiểu là kiểu phần tử(elementType); vị trí của các phần tử trong danh sách có kiểu là kiểu vị trí và vị trí sauphần tử cuối cùng trong danh sách L là ENDLIST(L) Cần nhấn mạnh rằng khái niệm
vị trí (position) là do ta định nghĩa, nó không phải là giá trị của các phần tử trong danhsách Vị trí có thể là đồng nhất với vị trí lưu trữ phần tử hoặc không
Các phép toán được định nghĩa trên danh sách là:
INSERT_LIST(x,p,L):xen phần tử x ( kiểu ElementType ) tại vị trí p (kiểu
position) trong danh sách L Tức là nếu danh sách là a1, a2, , ap-1, ap, , an thì sau khixen ta có kết quả a1, a2 ap-1, x, ap, , an Nếu vị trí p không tồn tại trong danhsách thì phép toán không được xác định
LOCATE(x,L):thực hiện việc định vị phần tử có nội dung x đầu tiên trong danh sách
L Locate trả kết quả là vị trí (kiểu position) của phần tử x trong danh sách Nếu x không
có trong danh sách thì vị trí sau phần tử cuối cùng của danh sách được trả về, tức làENDLIST(L)
- RETRIEVE(p,L):lấy giá trị của phần tử ở vị trí p (kiểu position) của danh sách L; nếu
vị trí p không có trong danh sách thì kết quả không xác định (có thể thông báo lỗi)
- DELETE_LIST(p,L):chương trình con thực hiện việc xoá phần tử ở vị trí p (kiểu
position) của danh sách Nếu vị trí p không có trong danh sách thì phép toán không đượcđịnh nghĩa và danh sách L sẽ không thay đổi
- NEXT(p,L):cho kết quả là vị trí của phần tử (kiểu position) đi sau phần tử p; nếu p là
phần tử cuối cùng trong danh sách L thì NEXT(p,L) cho kết quả là
ENDLIST(L):Next không xác định nếu p không phải là vị trí của một phần tử trong
danh sách
Trang 25- PREVIOUS(p,L):cho kết quả là vị trí của phần tử đứng trước phần tử p trong danh
sách Nếu p là phần tử đầu tiên trong danh sách thì Previous(p,L) không xác định.Previous cũng không xác định trong trường hợp p không phải là vị trí của phần tử nàotrong danh sách
- FIRST(L):cho kết quả là vị trí của phần tử đầu tiên trong danh sách Nếu danh sách
rỗng thì ENDLIST(L) được trả về
- EMPTY_LIST(L):cho kết quả TRUE nếu danh sách có rỗng, ngược lại nó cho giá trị
FALSE
- MAKENULL_LIST(L):khởi tạo một danh sách L rỗng.
- Trong thiết kế các giải thuật sau này chúng ta dùng các phép toán trừu tượng đã đượcđịnh nghĩa ở đây như là các phép toán nguyên thủy
Đồ thị
Các định nghĩa
Một đồ thị G bao gồm một tập hợp V các đỉnh và một tập hợp E các cung, ký hiệuG=(V,E) Các đỉnh còn được gọi là nút (node) hay điểm (point) Các cung nối giữahai đỉnh, hai đỉnh này có thể trùng nhau Hai đỉnh có cung nối nhau gọi là hai đỉnh kề(adjacency) Một cung nối giữa hai đỉnh v, w có thể coi như là một cặp điểm (v,w) Nếucặp này có thứ tự thì ta có cung có thứ tự, ngược lại thì cung không có thứ tự Nếu cáccung trong đồ thị G có thứ tự thì G gọi là đồ thị có hướng (directed graph) Nếu các cungtrong đồ thị G không có thứ tự thì đồ thị G là đồ thị vô hướng (undirected graph)
Biểu diễn đồ thị
- Biểu diễn đồ thị bằng ma trận kề
- Biểu diễn đồ thị bằng danh sách các đỉnh kề:
Các phép duyệt đồ thị
- Duyệt theo chiều sâu (depth-first search)
- Duyệt theo chiều rộng (breadth-first search)
Trang 26Các thuật ngữ cơ bản trên cây
Cây là một tập hợp các phần tử gọi là nút (nodes) trong đó có một nút được phân biệtgọi là nút gốc (root) Trên tập hợp các nút này có một quan hệ, gọi là mối quan hệ
cha-con(parenthood), để xác định hệ thống cấu trúc trên các nút Mỗi nút, trừ nút
gốc, có duy nhất một nút cha Một nút có thể có nhiều nút con hoặc không có nútcon nào Mỗi nút biểu diễn một phần tử trong tập hợp đang xét và nó có thể có mộtkiểu nào đó bất kỳ, thường ta biểu diễn nút bằng một kí tự, một chuỗi hoặc một
số ghi trong vòng tròn Mối quanhệchaconđược biểu diễn theo qui ước nútchaởdòng
trênnútconởdòngdướivàđượcnốibởimộtđoạnthẳng Một cách hình thức ta có thể định
nghĩa cây một cách đệ qui như sau:
Định nghĩa
Một nút đơn độc là một cây Nút này cũng chính là nút gốc của cây
Giả sử ta có n là một nút đơn độc và k cây T1, , Tk với các nút gốc tương ứng là n1, ,
nk thì có thể xây dựng một cây mới bằng cách cho nút n là cha của các nút n1, , nk Câymới này có nút gốc là nút n và các cây T1, , Tk được gọi là các cây con Tập rỗng cũngđược coi là một cây và gọi là cây rỗng kí hiệu
Xét mục lục của một quyển sách Mục lục này có thể xem là một cây
Cây m ụ c l ụ c m ột qu y ể n sách
Nút gốc là sách, nó có ba cây con có gốc là C1, C2, C3 Cây con thứ 3 có gốc C3 là mộtnút đơn độc trong khi đó hai cây con kia (gốc C1 và C2) có các nút con
Nếu n1, , nk là một chuỗi các nút trên cây sao cho ni là nút cha của nút ni+1, với
i=1 k-1, thì chuỗi này gọi là một đườngđitrêncây(hay ngắn gọn là đường đi ) từ n1 đến nk Độdàiđườngđiđược định nghĩa bằng số nút trên đường đi trừ 1 Như vậy độ dài
đường đi từ một nút đến chính nó bằng không
Nếu có đường đi từ nút a đến nút b thì ta nói a là tiềnbối (ancestor) của b, còn b gọi là
hậuduệ(descendant) của nút a Rõ ràng một nútvừalàtiềnbối vừalàhậu duệcủachínhnó.
Tiền bối hoặc hậu duệ của một nút khác với chính nó gọi là tiền bối hoặc hậu duệ thực
sự Trên cây nútgốckhông có tiền bối thực sự Một nút không có hậu duệ thực sự gọi là
nútlá(leaf) Nút không phải là lá ta còn gọi là núttrung gian(interior) Cây con của một
cây là một nút cùng với tất cả các hậu duệ của nó
Trang 27Chiềucaocủamộtnútlà độ dài đường đi lớn nhất từ nút đó tới lá Chiềucaocủa câylà chiều
cao của nút gốc Độsâucủamộtnútlà độ dài đường đi từ nút gốc đến nút đó Các nút có
cùng một độ sâu i ta gọi là các nút có cùng một mức i Theo định nghĩa này thì nút gốc
ở mức 0, các nút con của nút gốc ở mức 1
Các thứ tự duyệt cây quan trọng
Duyệt cây là một qui tắc cho phép đi qua lần lượt tất cả các nút của cây mỗi nút đúngmột lần, danh sách liệt kê các nút (tên nút hoặc giá trị chứa bên trong nút) theo thứ
tự đi qua gọi là danh sách duyệt cây Có ba cách duyệt cây quan trọng: Duyệt tiềntự (preorder), duyệttrungtự(inorder), duyệt hậutự(posorder) Có thể định nghĩa các phép
duyệt cây tổng quát một cách đệ qui như sau:
- Cây rỗng thì danh sách duyệt cây là rỗng và nó được coi là biểu thức duyệt tiền tự,trung tự, hậu tự của cây
- Cây chỉ có một nút thì danh sách duyệt cây gồm chỉ một nút đó và nó được coi là biểuthức duyệt tiền tự, trung tự, hậu tự của cây
- Ngược lại: giả sử cây T có nút gốc là n và có các cây con là T1, ,Tn thì:
+ Biểu thức duyệt tiền tự của cây T là liệt kê nút n kế tiếp là biểu thức duyệt tiền tự củacác cây T1, T2, , Tn theo thứ tự đó
+ Biểu thức duyệt trung tự của cây T là biểu thức duyệt trung tự của cây T1 kế tiếp lànút n rồi đến biểu thức duyệt trung tự của các cây T2, , Tn theo thứ tự đó
+ Biểu thức duyệt hậu tự của cây T là biểu thức duyệt hậu tự của các cây T1, T2, , Tntheo thứ tự đó rồi đến nút n
Cho cây như trong hình
Biểu thức duyệt tiền tự: A B C D E F H K L Trung tự: C B E D F A K H L
Hậu tự: C E F D B K L H A
Trang 28Các phép toán trên cây
- Hàm PARENT(n,T)cho nút cha của nút n trên cây T, nếu n là nút gốc thì hàm cho giá
trị NULL Trong cài đặt cụ thể thì NULL là một giá trị nào đó do ta chọn, nó phụ thuộcvào cấu trúc dữ liệu mà ta dùng để cài đặt cây
- Hàm LEFTMOST_CHILD(n,T)cho nút con trái nhất của nút n trên cây T, nếu n là lá
thì hàm cho giá trị NULL
- Hàm RIGHT_SIBLING(n,T)cho nút anh em ruột phải nút n trên cây T, nếu n không
có anh em ruột phải thì hàm cho giá trị NULL
- Hàm LABEL_NODE(n,T)cho nhãn tại nút n của cây T.
- Hàm ROOT(T)trả ra nút gốc của cây T Nếu Cây T rỗng thì hàm trả về NULL.
- Hàm CREATEi(v,T1,T2, ,Ti),với i=0 n, thủ tục tạo cây mới có nút gốc là n được
gán nhãn v và có i cây con T1, ,Ti Nếu n= 0 thì thủ tục tạo cây mới chỉ gồm có 1 nútđơn độc là n có nhãn v Chẳng hạn, giả sử ta có hai cây con T1 và T2, ta muốn thiết lậpcây mới với nút gốc có nhãn là v thì lời gọi thủ tục sẽ là CREATE2(v,T1,T2)
Cài đặt cây
- Cài đặt cây bằng mảng
- Biểu diễn cây bằng danh sách các con
- Biểu diễn theo con trái nhất và anh em ruột phải:
Cây nhị phân (Binary Trees)
Cây nhị phân là cây rỗng hoặc là cây mà mỗi nút có tối đa hai nút con Hơn nữa các nútcon của cây được phân biệt thứ tự rõ ràng, một nút con gọi là nút con trái và một nút congọi là nút con phải Ta qui ước vẽ nút con trái bên trái nút cha và nút con phải bên phảinút cha, mỗi nút con được nối với nút cha của nó bởi một đoạn thẳng
Cây tìm kiếm nhị phân (Binary Search Trees)
Cây tìm kiếm nhị phân (TKNP) là cây nhị phân mà khoá tại mỗi nút cây lớn hơn khoácủa tất cả các nút thuộc cây con bên trái và nhỏ hơn khoá của tất cả các nút thuộc câycon bên phải Lưu ý: dữ liệu lưu trữ tại mỗi nút có thể rất phức tạp như là một recordchẳng hạn, trong trường hợp này khoá của nút được tính dựa trên một trường nào đó,
ta gọi là trường khoá Trường khoá phải chứa các giá trị có thể so sánh được, tức là nóphải lấy giá trị từ một tập hợp có thứ tự
Trang 29Tập hợp
Định nghĩa
Tập hợp là một khái niệm cơ bản trong toán học Tập hợp được dùng để mô hình hoáhay biểu diễn một nhóm bất kỳ các đối tượng trong thế giới thực cho nên nó đóng vaitrò rất quan trọng trong mô hình hoá cũng như trong thiết kế các giải thuật
Khái niệm tập hợp cũng như trong toán học , đó là sự tập hợp các thành viên ( members) hoặc phần tử (elements) Tất cả các phần tử của tập hợp là khác nhau Tập hợp có thể
có thứ tự hoặc không có thứ tự, tức là , có thể có quan hệ thứ tự xác định trên các phần
tử của tập hợp hoặc không Tuy nhiên, trong chương này, chúng ta giả sử rằng các phần
tử của tập hợp có thứ tự tuyến tính, tức là , trên tập hợp S có quan hệ < và = thoả mảnhai tính chấ t:
- Với mọi a,b S thì a<b hoặc b<a hoặc a =b
- Với mọi a,b ,c S, a <b và b<c thì a <c
Các phép toán cơ bản trên kiểu dữ liệu tập hợp
Cũng như các kiểu dữ liệu trừu tượng khác, các phép toán kết hợp với mô hình tập hợp
sẽ tạo thành một kiểu dữ liệu trừu tượng là rất đa dạng Tùy theo nhu cầu của các ứngdụng mà các phép toán khác nhau sẽ được định nghĩa trên tập hợp Ở đây ta đề cập đếnmột số phép toán thường gặp nhất như sau :
- Thủ tục UNION(A,B,C)nhận vào 3 tham số là A,B,C; Thực hiện phép toán lấy hợp
của hai tập A và B và trả ra kết quả là tập hợp C = A ?B
- Thủ tục INTERSECTION(A,B,C)nhận vào 3 tham số là A,B,C; Thực hiện phép
toánlấy giao của hai tập A và B và trả ra kết quả là tập hợp C = A ? B
- Thủ tục DIFFERENCE(A,B,C)nhận vào 3 tham số là A,B,C; Thực hiện phép toán
lấy hợp của hai tập A và B và trả ra kết quả là tập hợp C = A\B
- Hàm MEMBER(x,A)cho kết quả kiểu logic (đúng/sai) tùy theo x có thuộc
A hay không Nếu x ∈ A thì hàm cho kết quả là 1 (đúng), ngược lại cho kết quả 0 (sai)
- Thủ tục MAKENULLSET(A)tạo tập hợp A tập rỗng
- Thủ tục INSERTSET(x,A)thêm x vào tập hợp A
- Thủ tục DELETESET(x,A)xoá x khỏi tập hợp A
Trang 30- Thủ tục ASSIGN(A,B)gán A cho B ( tức là B:=A )
- Hàm MIN(A)cho phần tử bé nhất trong tập A
- Hàm EQUAL(A,B)cho kết quả TRUE nếu A=B ngược lại cho kết quả FALSE
Cài đặt tập hợp
- Cài đặt tập hợp bằng vector Bit
- Cài đặt bằng danh sách liên kết
Ngôn ngữ tựa Pascal
Bảng chữ cái và ký tự chủ yếu
Ngôn ngữ tựa Pascal được sử dụng dùng để mô tả các bước của thuật toán Nó có đặcđiểm là giúp mô tả thuật toán gần gũi với một chương trình máy tính và làm mô tả thuậttoán trở nên chính xác hơn Dưới đây là liệt kê một số câu lệnh chính được sử dụng để
mô tả thuật toán dùng ngôn ngữ lập trình Pascal:
Ký tự và biểu thức
Các ký tự la tinh: A, a Z, z Chữ số: 0…9 Các phép toán số học: +, - , *, /
Các phép toán quan hệ : < , >, =, Giá trị logic: T (true), F (false) Phép toán logic: and,
or, not
Hằng đó là các giá trị cụ thể nào đó
Tên biến: Là một dãy kí tự mà kí tự đầu phải là chữ cái
Có hai loại biến chính:
Loại integer (biến nguyên)
Var bien: integer; Loại Real (biến thực)
var bien real;
Biến chỉ số Ở đây i là các biến nguyên
Biểu thức là kết hợp các hằng, biến và các phép toán
Trang 31Function max(a,b,c);
Cho biết tên của thuật toán là max và các biến là a, b, c
Procedure move(n,A,B,C);
Cho biết tên thuật toán được mô tả là move với các biến là n, A, B, C;
Các bước của thuật toán được mô tả trong thân thủ tục (hàm) được bắt đầu bởi
Begin và kết thúc bởi end
Function max(a,b,c); Begin
Trang 32Variable := exp; Max := a;
Các câu lệnh trong khối được thực hiện tuần tự Dưới đây thuật ngữ câu lệnh được dùng
để chỉ chung một câu lệnh cũng như một khối câu lệnh
(*a là số phần tử lớn nhất trong danh sách L*)
k là số phần tử của danh sách L
Câu lệnh điều kiện
Câu lệnh đơn giản là
If điều kiện then câu lệnh;
Khi thực hiện câu lệnh, điều kiện sẽ được kiểm tra, nếu nó được thoả mãn thì câu lệnh
sẽ được thực hiện
Nhiều khi ta cần thực hiện một thao tác nào đó khi điều kiện được thực hiện còn nếungược lại ta phải thực hiện một thao tác khác Khi đó ta có thể thực hiện câu lệnh phứctạp hơn sau đây:
If điều kiện then câu lệnh 1
else câu lệnh 2;
Các câu lệnh lặp
Trang 33Các câu lệnh sau đây sẽ được sử dụng
For biến := giá trị đầu to giá trị cuối do câu lệnh;
Tại đầu vòng lặp, biến được sẽ gán cho giá trị đầu, nếu giá trị đầu nhỏ hơn hơn hoặcbằng giá trị cuối và câu lệnh được thực hiện với giá trị này của biến Tiếp đến giá trịcủa biến sẽ tăng lên 1và câu lệnh sẽ được thực hiện với giá trị mới của biến Quá trình
sẽ được tiếp tục cho đến khi biến bằng giá trị cuối Sau khi thực hiện câu lệnh với biếnbằng giá trị cuối sẽ chuyển sang thực hiện câu lệnh tiếp theo Nếu giá trị đầu lớn hơn giátrị cuối thì không có câu lệnh nào được thực hiện
Câu lệnh lặp thứ hai được sử dụng là câu lệnh “while” While điều kiện do câu lệnh;
Khi câu lệnh này được sử dụng, điều kiện sẽ được kiểm tra, nếu nó là đúng thì câu lệnhđược thực hiện Điều đó sẽ tiếp tục cho đến khi điều kiện sai
Câu lệnh tiếp theo được sử dụng là câu lệnh “repeat”
Repeat câu lệnh
until điều kiện;
Khi câu lệnh này được sử dụng thì câu lệnh trong vòng lặp được thực hiện, điều đó cóthể sẽ dẫn đến sự thay đổi giá trị của các biến trong điều kiện Nếu điều kiện vẫn làđúng, thì câu lệnh lại được thực hiện Điều đó sẽ tiếp diễn cho đến khi điều kiện là đúng
Để giải phương trình bậc hai ax2 + bx + c = 0 ta mô tả thuật toán bằng một chương trìnhviết trên Pascal như sau:
Program giai_PT; Var
a,b,c, denta, x1, x2 : real; Begin
Clrscr;
Write(‘Nhập hệ số:’); Repeat
Write(‘a:=’);readln(a); Write(‘b:=’);readln(b); Write(‘c:=’);readln(c);
Until a<>0; Denta:=sqr(b)-4*a*c; If denta<0 then
Begin
Trang 34Write(‘phương trình vô nghiệm’);
Trang 35Xác định số bước thực hiện nhiều nhất có thể trong thuật toán của bạn nhằm xác địnhđược nhiều nhất có thể các số liên tiếp nhau có tổng dương trong một dãy n số hạng chotrước.
Dành cho độc giả
Mô tả các thuật toán của bạn bằng sơ đồ khối:
a) Thuật toán tìm phần tử lớn nhất của một dãy hữu hạn số thực
b) Thuật toán tìm phần tử bé nhất của một tập con của tập hợp
c) Thuật toán xếp lại một dãy theo thứ tự tăng dần
d) Thuật toán tìm một dãy các số liên tiếp nhau (dài nhất có thể) có tổng dương trongmột dãy số thực cho trước
Dành cho độc giả
Viết các thuật toán của bạn bằng ngôn ngữ tựa Pascal:
a) Thuật toán tìm phần tử lớn nhất của một dãy hữu hạn số thực
b) Thuật toán tìm phần tử bé nhất của một tập hợp con của tập hợp
c) Thuật toán xếp lại một dãy theo thứ tự tăng dần
d) Thuật toán tìm một dãy các số liên tiếp nhau (dài nhất có thể) có tổng dương trongmột dãy số thực cho trước
Dành cho độc giả
Trang 36Phương pháp tham lam
Đặc trưng của chiến lược tham lam
Bài toán tối ưu tổ hợp
• Là một dạng của bài toán tối ưu, nó có dạng tổng quát như sau:
• Cho hàm f(X) = xác định trên một tập hữu hạn các phần tử D Hàm f(X) đượcgọi là hàm mục tiêu
• Mỗi phần tử X Є D có dạng X = (x1, x2 xn) được gọi là một phương án
• Cần tìm một phương án X Є D sao cho hàm f(X) đạt min (max) Phương án Xnhư thế được gọi là phương án tối ưu
Ta có thể tìm thấy phương án tối ưu bằng phương pháp “vét cạn” nghĩa là xét tất cả cácphương án trong tập D (hữu hạn) để xác đinh phương án tốt nhất Mặc dù tập hợp D
là hữu hạn nhưng để tìm phương án tối ưu cho một bài toán kích thước n bằng phươngpháp “vét cạn” ta có thể cần một thời gian mũ Các phần tiếp theo của chương này sẽtrình bày một số kĩ thuật giải bài toán tối ưu tổ hợp mà thời gian có thể chấp nhận được
Nội dung kĩ thuật tham ăn
Tham ăn hiểu một cách dân gian là: trong một mâm có nhiều món ăn, món nào ngonnhất ta sẽ ăn trước và ăn cho hết món đó thì chuyển sang món ngon thứ hai, lại ăn hếtmón ngon thứ hai này và chuyển sang món ngon thứ ba… Kĩ thuật tham ăn thường đượcvận dụng để giải bài toán tối ưu tổ hợp bằng cách xây dựng một phương án X Phương
án X được xây dựng bằng cách lựa chọn từng thành phần Xi của X cho đến khi hoànchỉnh (đủ n thành phần) Với mỗi Xi, ta sẽ chọn Xi tối ưu Với cách này thì có thể ởbước cuối cùng ta không còn gì để chọn mà phải chấp nhận một giá trị cuối cùng cònlại
Áp dụng kĩ thuật tham ăn sẽ cho một giải thuật thời gian đa thức, tuy nhiên nói chungchúng ta chỉ đạt được một phương án tốt chứ chưa hẳn là tối ưu Có rất nhiều bài toán
mà ta có thể giải bằng kĩ thuật này
Đặc tính lựa chọn tham lam
Toàn bộ phương pháp tối ưu có thể đạt được từ việc chọn tối ưu trong từng bước chọn
Về khía cạnh này giải thuật tham lam khác với giải thuật quy hoạch động ở chỗ: Trongqui hoạch động chúng ta thực hiện chọn cho từng bước, nhưng việc lựa chọn này phụthuộc vào cách giải quyết các bài toán con Với giải thuật tham lam, tại mỗi bước chúng
Trang 37ta chọn bất cứ cái gì là tốt nhất vào thời điểm hiện tại, và sau đó giải quyết các vấn đềphát sinh từ việc chọn này Vấn đề chọn thực hiện bởi giải thuật tham lam không phụthuộc vào việc lựa chọn trong tương lai hay cách giải quyết các bài toán con Vì vậykhác với quy hoạch động, giải quyết các bài toán con theo kiểu bottom up (từ dưới lên),giải thuật tham lam thường sử dụng giải pháp top-down (từ trên xuống) Chúng ta phảichứng minh rằng với giải thuật tham lam, toàn bộ bài toán được giải quyết một cách tối
ưu nếu mỗi bước việc chọn được thực hiện tối ưu Các bước chọn tiếp theo được thựchiện tương tự như bước đầu tiên, nhưng với bài toán nhỏ hơn Ph- ương pháp qui nạpđược ứng dụng trong giải thuật tham lam có thể được sử dụng cho tất cả các bước chọn
Cấu trúc con tối ưu
Một bài toán thực hiện optimal substructure nếu cách giải quyết tối ưu của bài toán chứađựng cách giải quyết tối ưu những bài toán con của nó Tính chất này được đánh giá làmột thành phần có thể áp dụng được của thuật toán quy hoạch động tốt như thuật toántham lam Một ví dụ của optimal substructure, nếu A là đáp án tối ưu của bài toán vớihành động chọn đầu tiên là 1, thì tập hợp A’= A- {1} là đáp án tối ưu cho bài toánS’= {i Є S: si≥ f1}
Sơ đồ chung của phương pháp
Đặc điểm chung của thuật toán tham lam
Mục đích xây dựng bài toán giải nhiều lớp bài toán khác nhau, đưa ra quyết định dựangay vào thuật toán đang có, và trong tương lai sẽ không xem xét lại quyết định trongquá khứ Vì vậy thuật toán dễ đề xuất, thời gian tính nhanh nhưng thường không cho kếtquả đúng
• Lời giải cần tìm có thể mô tả như là bộ gồm hữu hạn các thành phần thoả mãnđiều kiện nhất định, ta phải giải quyết bài toán một cách tối ưu -> hàm mục tiêu
• Để xây dựng lời giải ta có một tập các ứng cử viên
• Xuất phát từ lời giải rỗng, thực hiện việc xây dựng lời giải từng bước, mỗibước sẽ lựa chọn trong tập ứng cử viên để bổ xung vào lời giải hiện có
• Xây dựng một hàm nhận biết tính chấp nhận được của lời giải hiện có -> HàmSolution(S) -> Kiểm tra thoả mãn điều kiện chưa
Một hàm quan trọng nữa: Select(C) cho phép tại mỗi bước của thuật toán lựa chọn ứng
cử viên có triển vọng nhất để bổ xung vào lời giải hiện có -> dựa trên căn cứ vào ảnhhưởng của nó vào hàm mục tiêu, thực tế là ứng cử viên đó phải giúp chúng ta phát triểntiếp tục bài toán
Trang 38Xây dựng hàm nhận biết tính chấp nhận được của ứng cử viên được lựa chọn, để có thểquyết định bổ xung ứng cử viên được lựa chọn bởi hàm Select vào lời giải -> Feasible(Sx).
Sơ đồ thuật toán
• Để chỉ ra thuật toán không cho lời giải đúng chỉ cần đưa ra một phần ví dụ
• Việc chứng minh thuật toán đúng khó hơn nhiều và ta sẽ nghiên cứu cụ thểtrong phần sau:
Lập luận biến đổi (Exchange A r gument)
Giả sử cần chứng minh thuật toán A cho lời giải đúng A(I) là lời giải tìm được bởi thuậttoán A đối với bộ dữ liệu I Còn O là lời giải tối ưu của bài toán với bộ dữ liệu này
Ta cần tìm cách xây dựng phép biến đổi φ để biến đổi O thành O’ sao cho:
Trang 391 O’ cũng tốt không kém gì O (Nghĩa là O’ vẫn tối ưu)
2 O’ giống với A(I) nhiều hơn O
Giả sử đã xây dựng được phép biến đổi vừa nêu Để chứng minh tính đúng đắn dựa vàohai sơ đồ chứng minh sau
- CMbằngphảnchứng:Giả sử A không đúng đắn, hãy tìm bộ dữ liệu I sao cho A(I) khác
với lời giải tối ưu của bài toán Gọi O là lời giải tối ưu giống với A(I) nhất => A(I) khác
O Dùng phép biến đổi φ chúng ta có thể biến đổi O → O’ sao cho O’ vẫn tối ưu và O’giống với A(I) hơn => mâu thuẫn giả thiết O là lời giải tối ưu giống với A(I) nhất
-CMtrựctiếp:O là lời giải tối u Biến đổi O → O’ giống với A(I) hơn là O Nếu O’ =A(I) thì A(I) chính là phương án tối u ngược lại biến đổi O’→ O’’ giống với A(I) hơn
Cứ thế ta thu được dãy O’, O’’, O’’’… ngày càng giống hơn, và chỉ có một số hữu hạnđiều kiện để so sánh nên chỉ sau một số hữu hạn lần phép biến đổi
sẽ kết thúc và đó là tại A(I)
Bài toán trả tiền của máy rút tiền tự động ATM
Trong máy rút tiền tự động ATM, ngân hàng đã chuẩn bị sẵn các loại tiền có mệnh giá100.000 đồng, 50.000 đồng, 20.000 đồng và 10.000 đồng Giả sử mỗi loại tiền đều có
số lượng không hạn chế Khi có một khách hàng cần rút một số tiền n đồng (tính chẵnđến 10.000 đồng, tức là n chia hết cho 10000) Hãy tìm một phương án trả tiền sao chotrả đủ n đồng và số tờ giấy bạc phải trả là ít nhất
Gọi X = (X1, X2, X3, X4) là một phương án trả tiền, trong đó X1 là số tờ giấy bạc mệnhgiá 100.000 đồng, X2 là số tờ giấy bạc mệnh giá 50.000 đồng, X3 là số tờ giấy bạc mệnhgiá 20.000 đồng và X4 là số tờ giấy bạc mệnh giá 10.000 đồng Theo yêu cầu ta phải cóX1 + X2 + X3 + X4 nhỏ nhất và X1 * 100.000 + X2 * 50.000 + X3 *
20.000 + X4 * 10.000 = n
Áp dụng kĩ thuật tham ăn để giải bài toán này là: để có số tờ giấy bạc phải trả (X1 +
X2 + X3 + X4) nhỏ nhất thì các tờ giấy bạc mệnh giá lớn phải được chọn nhiều nhất.Trước hết ta chọn tối đa các tờ giấy bạc mệnh giá 100.000 đồng, nghĩa là X1 là sốnguyên lớn nhất sao cho X1 * 100.000 ≤ n Tức là X1 = n DIV 100.000
Xác định số tiền cần rút còn lại là hiệu n – X1 * 100000 và chuyển sang chọn loại giấybạc 50.000 đồng…
Trang 40Khách hàng cần rút 1.290.000 đồng (n = 1290000), phương án trả tiền như sau: X1 =
1290000 DIV 100000 = 12
Số tiền cần rút còn lại là 1290000 – 12 * 100000 = 90000 X2 = 90000 DIV 50000 = 1
Số tiền cần rút còn lại là 90000 – 1 * 50000 = 40000 X3 = 40000 DIV 20000 = 2
Số tiền cần rút còn lại là 40000 – 2 * 20000 = 0 X4 = 0 DIV 10000 = 0
Ta có X = (12, 1, 2, 0), tức là máy ATM sẽ trả cho khách hàng 12 tờ 100.000 đồng, 1 tờ50.000 đồng và 2 tờ 20.000 đồng
Bài toán về các đoạn thẳng không giao nhau
Bài toán
Đầu vào : Cho họ các đoạn thẳng mởĐầu ra : Tập các đoạn thẳng không giao nhau có lực lượng lớn nhất
Ứngdụngthựctế: Bài toán xếp thời gian biểu cho các hội thảo, bài toán phục vụ khách
hành trên một máy, bài toán lựa chọn hành động (Ví dụ có nlời mời dự tiệc bắt đầu bởi
aikết thúc bởi bi, hãy lựa chọn sao cho đi được nhiều tiệc nhất).
Đề xuất các thuật to á n :
Greedy 1: Sắp xếp các đoạn thẳng theo thứ tự tăng dần của đầu mút trái, bắt đầu từ tập
S là tập rỗng ta lần lượt xếp các đoạn thẳng trong danh sách theo thứ tự đã xếp và bổsung đoạn thẳng đang xét vào S nếu nó không có điểm chung với bất cứ đoạn nào trongS