59
1 Môn học: Phân tích và Thiết kế Giải thuật Số tín chỉ: 2 BÀI GIẢNG ĐIỆN TỬ Biên soạn bởi: TS. Dương Tuấn Anh Khoa Công nghệ Thông tin Trường Đ.H. Bách Khoa ĐạI học Quốc Gia Tp Hồ Chí Minh

Chapter1 Cac khai niem can ban

Embed Size (px)

Citation preview

Page 1: Chapter1 Cac khai niem can ban

1

Môn học:

Phân tích và Thiết kế Giải thuậtSố tín chỉ: 2

BÀI GIẢNG ĐIỆN TỬBiên soạn bởi: TS. Dương Tuấn Anh

Khoa Công nghệ Thông tin

Trường Đ.H. Bách Khoa

ĐạI học Quốc Gia Tp Hồ Chí Minh

Page 2: Chapter1 Cac khai niem can ban

2

Tài liệu tham khảo

[1] Cormen, T. H., Leiserson, C. E, and Rivest, R. L., Introduction to Algorithms, The MIT Press, 1997.

[2] Levitin, A., Introduction to the Design and Analysis of Algorithms, Addison Wesley, 2003

[3] Sedgewick, R., Algorithms in C++, Addison-Wesley, 1998

[4] Weiss, M.A., Data Structures and Algorithm Analysis in C, TheBenjamin/Cummings Publishing, 1993

Page 3: Chapter1 Cac khai niem can ban

3

Đề cương Môn học

1. Các khái niệm căn bản

2. Phân tích độ phức tạp một số giải thuật sắp thứ tự và tìm kiếm

3. Phân tích độ phức tạp một số giải thuật làm việc trên cấu trúc dữ liệu

4. Phân tích độ phức tạp một số giải thuật làm việc trên đồ thị

5. Các chiến lược thiết kế giải thuật

6. Vấn đề NP-đầy đủ

Page 4: Chapter1 Cac khai niem can ban

5

Nội dung

1. Kiểu dữ liệu trừu tượng

2. Đệ quy

3. Phân tích giải thuật

Page 5: Chapter1 Cac khai niem can ban

6

1.Kiểu dữ liệu trừu tượng

• Mô tả một cấu trúc dữ liệu theo các tác vụ (operations) làm việc trên cấu trúc dữ liệu thì tiện lợi hơn là diễn tả nó theo những chi tiết thi công (implementation details).

• Chúng ta nên tách những khái niệm về cấu trúc dữ liệu ra khỏi những chi tiết thi công.

• Khi một cấu trúc dữ liệu được định nghĩa theo cách như vậy ta sẽ có một kiểu dữ liệu trừu tượng (abstract data type) hay ADT.

Một kiểu dữ liệu trừu tượng là một mô hình toán học đi cùng với những tác vụ được định nghĩa trên mô hình này.

Page 6: Chapter1 Cac khai niem can ban

7

Vài thí dụ về Kiểu dữ liệu trừu tượng

• Một tập (set) là một tập hợp gồm zero hay nhiều phần tử. Một phần tử không được phép xuất hiện nhiều hơn một lần trong tập. Một tập gồm n phần tử được ký hiệu là {a1, a2,…,an}, nhưng vị trí của một phần tử trong một tập là không quan trọng.

• Một đa tập (multiset) là một tập mà trong đó một phần tử được phép xuất hiện nhiều hơn một lần. Thí dụ, {5,7,5,2} là một đa tập. Một đa tập có thể có những tác vụ sau:

initialize (khởi tạo)insert (thêm vào)is_empty (thử đa tập có rỗng)delete (xoá)findmin (tìm phần tử bé nhất)

Page 7: Chapter1 Cac khai niem can ban

8

Vài thí dụ về Kiểu dữ liệu trừu tượng (tt)

• Một chuỗi (sequence) là một tập hợp có thứ tự của zero hay nhiều phần tử; được ký hiệu là <a1, a2,…, an>. Vị trí của một phần tử trong một chuỗi là có ý nghiã. Một chuỗi có thể có những tác vụ sau:

initialize (khởi tạo)length (chiều dài)head (phần tử đầu)tail (phần đuôi)concatenate (ghép kề hai chuỗi)

Page 8: Chapter1 Cac khai niem can ban

9

Giải một bài toán bằng ADT

Để thấy ích lợi của kiểu dữ liệu trừu tượng, thử xét bài toán sau:Cho một mảng (array) gồm n số, A[1..n], hãy xác định k phần tử lớn nhất trong mảng, với k n. Thí dụ, nếu A là {5, 3, 1, 9, 6}, và k = 3, thì kết quả là {5, 9, 6}.

Không dễ để xây dựng một giải thuật để giải bài toán trên.Ta thử dùng kiểu dữ liệu trừu tượng đa tập (multiset) với cáctác vụ:

initialize(M), insert(x, M), deleteMin(M), findMin(M)

Page 9: Chapter1 Cac khai niem can ban

10

Suy nghĩ trên đa tập M, ta có thể viết một giải thuật như sau:

Initialize(M);for i:= 1 to k do Insert(A[i], M);for i:= k + 1 to n do if A[i] > FindMin(M) then begin DeleteMin(M); Insert(A[i],M) end;

Trong thí dụ trên, ta thấy kiểu dữ liệu trừu tượng đã làm đơn giản hóa việc xây dựng giải thuật bằng cách không bận tâm đến những chi tiết thi công hay hiện thực hóa.

Page 10: Chapter1 Cac khai niem can ban

11

Thi công một kiểu dữ liệu trừu tượng

Quá trình dùng một cấu trúc dữ liệu cụ thể để hiện thực hóa một ADT được gọi là thi công kiểu dữ liệu trừu tượng. Trong sự thi công này, phần dữ liệu trừu tượng được hiện thực hóa bằng một cấu trúc dữ liệu cụ thể và phần các tác vụ trừu tượng được hiện thực hóa bằng các tác vụ cụ thể hơn.

AbstractData

Operations

DataStructure

Concrete operations

Page 11: Chapter1 Cac khai niem can ban

12

Thi công một kiểu dữ liệu trừu tượng (tt.)

Có thể dùng mảng (array) hay danh sách liên kết (linked list) để thi công tập hợp (set).

Có thể dùng mảng (array) hay danh sách liên kết (linked list) để thi công chuỗi.

Với kiểu dữ liệu trừu tượng đa tập như trong thí dụ trước, ta có thể dùng hàng đợi có độ ưu tiên (priority queue) để thi công. Và sau đó ta có thể dùng cấu trúc dữ liệu heap để thi công hàng đợi có độ ưu tiên.

Page 12: Chapter1 Cac khai niem can ban

13

2. Đệ quy

Hệ thức truy hồi

Thí dụ 1: Hàm tính giai thừaN! = N.(N-1)! với N 1

0! = 1Những định nghĩa hàm đệ quy mà chứa những đối số nguyên được gọi là những hệ thức truy hồi (recurrence relation).

function factorial (N: integer): integer;begin if N = 0 then factorial: = 1 else factorial: = N*factorial (N-1);end;

Page 13: Chapter1 Cac khai niem can ban

14

Hệ thức truy hồi

Thí dụ 2: Số Fibonacci

Hệ thức truy hồi: FN = FN-1 + FN-2 for N 2 F0 = F1 = 1 1, 1, 2, 3, 5, 8, 13, 21, …

function fibonacci (N: integer): integer;begin if N <= 1 then fibonacci: = 1 else fibonacci: = fibonacci(N-1) + fibonacci(N-2);end;

Page 14: Chapter1 Cac khai niem can ban

15

Một cách khác: Ta có thể dùng một mảng để chứa những trị số đi trước trong khi tính hàm fibonacci. Ta có một giải thuật không đệ quy.

procedure fibonacci;const max = 25;var i: integer;F: array [0..max] of integer;begin F[0]: = 1; F[1]: = 1; for i: = 2 to max do F[i]: = F[i-1] + F[i-2]end;

Page 15: Chapter1 Cac khai niem can ban

16

Chiến lược Chia-để-trị

Nhiều giải thuật hay có cấu trúc đệ quy: để giải một vấn đề giải thuật gọi chính nó một hay nhiều lần để đối phó với những vấn đề con (subproblem) có quan hệ chặt chẽ với nhau.

Những giải thuật như vậy tuân theo cách tiếp cận chia-để-trị (divide and conquer): Giải thuật phân rã vấn đề thành những vấn đề con, giải những vấn đề con này và kết hợp những lời giải của những vấn đề con thành lời giải cho vấn đề nguyên thủy.

Chiến lược này bao gồm 3 bước sau đây ở mỗi cấp đệ quy:phân chia (divide)trị (conquer)kết hợp (combine)

Page 16: Chapter1 Cac khai niem can ban

17

Thí dụ: Xét việc vạch những vạch cách nhau 1 inch trên 1 cây thước: có một nét vạch tại điển ½ inch, nét vạch ngắn hơn ở những đoạn ¼ inch, nét vạch ngắn hơn nữa ở những đoạn 1/8 inch, v.v...

procedure rule(l, r, h: integer);/* l: left position of the ruler; r: right position of the ruler */var m: integer;begin if h > 0 then begin m: = (1 + r) div 2; mark(m, h); rule(l, m, h-1); rule(m, r , h-1) end;end;

Giả sử thủ tục mark(x, h) vẽ một vạch h đơn vị tại vị trí x.

Page 17: Chapter1 Cac khai niem can ban

18

Khử đệ quy

Vấn đề: Giải thuật không đệ quy thường làm việc hữu hiệu và dễ kiểm soát hơn 1 giải thuật đệ quy. Làm cách nào để chuyển đổi một chương trình đệ quy thành một chương trình không-đệ-quy tương đương.

Phương pháp: Cho một thủ tục đệ quy P, mỗi lần có một lệnh gọi đệ quy đến P, giá trị hiện hành của các tham số và các biến cục bộ được cất vào các ngăn xếp (stack) để xử lý sau.

Mỗi lần có một sự quay về đệ quy về P, giá trị của các tham số và các biến cục bộ sẽ được khôi phục lại từ các ngăn xếp.

Page 18: Chapter1 Cac khai niem can ban

19

Khử đệ quy (tt.)

Việc đối phó với địa chỉ khứ hồi (return address) được thực hiện như sau: Giả sử thủ tục P chứa một lệnh gọi đệ quy tại bước lệnh K, địa chỉ khứ hồi K+1 sẽ được lưu tại một ngăn xếp và sẽ được dùng để quay về mức thực thi thủ tục P hiện hành.

procedure hanoi(n, beg, aux, end); begin if n = 1 then writeln(beg, end) else begin hanoi(n-1, beg, end, aux) ; writeln(beg, end); hanoi(n-1, aux, beg, end); endend;

Thí dụ: Thủ tục hanoi là một thủ tục đệ quy giải bài toán Tháp Hà Nội

Page 19: Chapter1 Cac khai niem can ban

20

procedure hanoi(n, beg, aux, end: integer);/* Stacks STN, STBEG, STAUX, STEND, and STADD correspond, respectively, to variables N, BEG, AUX, END and ADD */label 1, 3, 5;var t: integer;begin top: = 0; /* preparation for stacks */1: if n = 1 then begin writeln(beg, end); goto 5 end; top: = top + 1; /* first recursive call */ STN[top]: = n; STBEG[top]: = beg; STAUX [top]:= aux; STEND [top]: = end; STADD [top]: = 3; /* saving return address */ n: = n-1; t:= aux; aux: = end; end: = t; goto 1;

3: writeln(beg, end); top: = top + 1; /* second recursive call */ STN[top]: = n; STBEG[top]: = beg; STAUX[top]: aux; STEND[top]: = end; STADD[top]: = 5; /* saving return address */ n: = n-1; t:= beg; beg: = aux; aux: = t; goto 1;5. /* translation of return point */ if top <> 0 then begin n: = STN[top]; beg: = STBEG [top]; aux: = STAUX [top]; end: = STEND [top]; add: = STADD [top]; top: = top – 1; goto add endend;

Page 20: Chapter1 Cac khai niem can ban

21

Sự duyệt cây đệ quy

Cách đơn giản nhất để duyệt các nút trong một cây nhị phân theo thứ tự nội là nhờ một thủ tục đệ quy như sau: // duyệt thứ tự nội (Inorder traversal)type link = node; node = record info: …….; l, r: link end;procedure traverse(t: link);begin if t <> z then /* z is dummy node */ begin traverse(t.l); visit(t); traverse(t.r) endend;

Page 21: Chapter1 Cac khai niem can ban

22

Thủ tục duyệt cây tiền thứ tự (Pre-order)

procedure traverse (t: link)begin if t <> z then begin visit(t); traverse(t.1); traverse(t.r) endend;

Trước khi khử đệ quy thủ tục này, ta có thể loại bỏ lệnh gọi đệ quy thứ hai dễ dàng vì không có mã chương trình đi sau nó.

Lệnh gọi đệ quy thứ hai có thể được chuyển thành một lệnh goto như sau:

Page 22: Chapter1 Cac khai niem can ban

23

Khử đệ quy đuôi

procedure traverse (t: link);label 0,1;begin0: if t = z then goto 1; visit(t); traverse(t. l); t: = t.r; goto 0;1: end;

Kỹ thuật này được gọi là khử đệ quy đuôi (tail-recursion removal).

Page 23: Chapter1 Cac khai niem can ban

24

procedure traverse(t: link);label 0, 1, 2, 3;begin0: if t = z then goto 1; visit(t); push(t); t: = t.l; goto 0;3: t: = t.r; goto 0;1: if stack_empty then goto 2; t: = pop; goto 3;2: end;

Chú ý: Do chỉ có một địa chỉ khứ hồi, 3, mà là cố định, nên ta không phải nhồi nó vào stack.

Bây giờ áp dụng phương pháp tổng quát, ta có thể khử lệnh gọi đệ quy còn lại trong chương trình.

Page 24: Chapter1 Cac khai niem can ban

25

procedure traverse(t: link);label 0,2;begin0: while t <> z do begin visit(t); push(t.r); t: = t.1; end; if stack_empty then goto 2; t: = pop; goto 0;2: end;

Ta có thể loại bỏ vài phát biểu goto bằng cách dùng vòng lặp while như sau.

Page 25: Chapter1 Cac khai niem can ban

26

procedure traverse(t: link);begin push(t); repeat t: = pop; while t <> z do begin visit(t); push(t.r); t: = t.l; end; until stack_empty;end;

Một lần nữa ta có thể loại bỏ hai lệnh goto còn lại bằng cách dùng vòng lặp repeat .

Page 26: Chapter1 Cac khai niem can ban

27

procedure traverse(t: link);begin push(t); repeat t: = pop; if t <> z then begin visit(t); push(t.r); push(t.1); end; until stack_empty;end;

Hai vòng lặp lồng nhau có thể được đơn giản hóa như sau:

Page 27: Chapter1 Cac khai niem can ban

28

Để tránh đưa những cây con rỗng vào stack, ta có thể sửa chương trình trên thành:

procedure traverse(t: link);begin push(t); repeat t: = pop; visit(t); if t.r < > z then push(t.r); if t.l < > z then push(t.l); until stack_empty;end;Đây là chương trình không đệ quy hoàn chỉnh để duyệt cây nhị phân.

Page 28: Chapter1 Cac khai niem can ban

29

Khử hàm đệ quy

Nếu thủ tục đệ quy P là một hàm, ta cần bổ sung vào qui tắc chuyển đổi tổng quát một số điểm sau đây:

Thí dụ: Hàm tính tổ hợpfunction comb(m, n: int):intIf (n=m or m = 0) then return 1elsebegin a:= comb(n -1, m); b:= comb(n-1, m -1); return (a+b);end

1. Tại câu lệnh có gắn địa chỉ khứ hồi, ta phải khôi phục trị hàm từ stack; nếu cần thì thêm lệnh sử dụng trị hàm này.

2. Tại câu lệnh return, cần đưa vào phát biểu định trị biểu thức đi liền sau từ khóa return và cất trị hàm trả về này vào stack.

Page 29: Chapter1 Cac khai niem can ban

30

int comb(int n, int m){ int top, topF; int stackN[100]; int stackM[100]; int stackADD[100]; int stackF[100]; int a, b, add, c; top = topF = 0; //stack initializeL0: if (m == 0 || n ==m) { c = 1, goto L3;} else { // 1st recursive call top = top +1 ; stackN[top] = n; stackM[top] = m; stackADD[top] =1; n = n-1; goto L0;L1:// 2nd recursive call top = top +1 ; stackN[top] = n; stackM[top] = m; stackADD[top] =2;

n = n-1; m = m-1; go to L0;L2: a= stackF[topF], topF = topF –1; b = stackF[topF], topF = topF –1; c = a + b;L3: if (top >0) { n = stackN[top]; m = stackM[top]; add = stackADD[top]; top = top –1; // push the result topF = topF + 1; stackF[top] = c; if (add == 1) goto L1; else if (add == 2) goto L2; } return c; }}

Page 30: Chapter1 Cac khai niem can ban

31

3. Phân tích độ phức tạp giải thuật

Với phần lớn các bài toán, thường có nhiều giải thuật khác nhau để giải một bài toán.Làm cách nào để chọn giải thuật tốt nhất để giải một bài toán?Làm cách nào để so sánh các giải thuật cùng giải được một bài toán?

Phân tích độ phức tạp của một giải thuật: dự đoán các tài nguyên mà giải thuật đó cần.

Tài nguyên: Chỗ bộ nhớ Thời gian tính toán

Thời gian tính toán là tài nguyên quan trọng nhất.

Page 31: Chapter1 Cac khai niem can ban

32

Hai cách phân tích

Thời gian tính toán của một giải thuật thường là một hàm của kích thước dữ liệu nhập.

Chúng ta quan tâm đến:

Trường hợp trung bình (average case): thời gian tính toán mà một giải thuật cần đối với một “dữ liệu nhâp thông thường” (typical input data).

Trường hợp xấu nhất (worst case): thời gian tính toán mà một giải thuật cần đối với một “dữ liệu nhâp xấu nhất”

Page 32: Chapter1 Cac khai niem can ban

33

Khung thức của sự phân tích

Bước 1: Đặc trưng hóa dữ liệu nhập và quyết định kiểu phân tích thích hợp. Thông thường, ta tập trung vào việc - chứng minh rằng thời gian tính toán luôn nhỏ hơn một “cận trên” (upper bound), hay - dẫn xuất ra thời gian chạy trung bình đối với một dữ liệu nhập ngẫu nhiên.

Bước 2: nhận dạng thao tác trừu tượng (abstract operation) mà giải thuật dựa vào đó làm việc.

Thí dụ: thao tác so sánh trong giải thuật sắp thứ tự. Tổng số thao tác trừu tượng thường tùy thuộc vào một vài đại lượng.

Bước 3: thực hiện phân tích toán học để tìm ra các giá trị trung bình và giá trị xấu nhất của các đại lượng quan trọng.

Page 33: Chapter1 Cac khai niem can ban

34

Hai trường hợp phân tích

• Thường thì không khó để tìm ra cận trên của thời gian tính toán của một giải thuật.

• Nhưng phân tích trường hợp trung bình thường đòi hỏi một sự phân tích toán học cầu kỳ, phức tạp.

• Về nguyên tắc, một giải thuật có thể được phân tích đến một mức độ chính xác rất chi li. Nhưng trong thực tế, chúng ta thường chỉ tính ước lượng (estimating) mà thôi

• Tóm lại, chúng ta tìm kiếm một ước lượng thô về thời gian tính toán của một giải thuật (nhằm mục đích phân lớp độ phức tạp).

Page 34: Chapter1 Cac khai niem can ban

35

Phân lớp độ phức tạp

Hầu hết các giải thuật thường có một thông số chính, N, số mẩu dữ liệu nhập mà được xử lý.

Thí dụ:Kích thước của mảng (array) được sắp thứ tự hoặc tìm kiếm. Số nút của một đồ thị.

Giải thuật có thể có thời gian tính toán tỉ lệ với

1. Nếu tác vụ chính được thực thi một vài lần. thời gian tính toán là hằng số.

2. lgN (logarithmic) log2N lgNGiải thuật tăng chậm hơn sự tăng của N.

Page 35: Chapter1 Cac khai niem can ban

36

3. N (linear)

4. NlgN

5. N2 (quadratic) khi giải thuật là vòng lặp lồng hai

6. N3 (cubic) khi giải thuật là vòng lặp lồng ba

7. 2N một số giải thuật có thời gian chạy luỹ thừa.

Một vài giải thuật khác có thể có thời gian chạy N3/2, N1/2 , (lgN)2 …

Page 36: Chapter1 Cac khai niem can ban

37

Độ phức tạp tính toán

Chúng ta tập trung vào phân tích trường hợp xấu nhất. Khi phân tích, bỏ qua những thừa số hằng số để xác định sự phụ thuộc hàm của thời gian tính toán đối với kích thước dữ liệu nhập.

Thí dụ: Thời gian tính toán của sắp thứ tự bằng phương pháp trộn (mergesort ) là tỉ lệ với NlgN.

Khái niệm “tỉ lệ với” (proportional to) Công cụ toán học để làm chính xác khái niệm này là ký hiệu – O (O-notation).

Định nghĩa: Một hàm g(N) được gọi là O(f(N)) nếu tồn tại hai hằng số c0 và N0 sao cho g(N) nhỏ hơn c0f(N) với mọi N > N0.

Page 37: Chapter1 Cac khai niem can ban

38

Ký hiệu O

Ký hiệu O là một cách hữu ích để phát biểu cận trên về thời gian tính toán mà độc lập đối với đặc tính dữ liệu nhập và chi tiết hiện thực hóa.

Chúng ta cố gắng tìm cả “cận trên” lẫn “cận dưới” của thời gian tính toán trong phân tích trường hợp xấu nhất.

Nhưng cận dưới (lower-bound ) thì thường khó xác định.

Page 38: Chapter1 Cac khai niem can ban

39

Phân tích trường hợp trung bình

Với kiểu phân tích này, ta phải - đặc trưng hóa dữ liệu nhập của giải thuật - tính giá trị trung bình của số lần một phát biểu được thực thi. - tính thời gian tính toán trung bình của toàn giải thuật.

Nhưng thường thì khó - xác định thời gian chạy của mỗi phát biểu. - đặc trưng hóa chính xác dữ liệu nhập trong thực tế.

Page 39: Chapter1 Cac khai niem can ban

40

Các kết quả tiệm cận và xấp xỉ

Kết quả của một sự phân tích toán học thường mang tính xấp xỉ (approximate): nó có thể là một biểu thức gồm một chuỗi những số hạng giảm dần tầm quan trọng.

Ta thường để ý đến các số hạng dẫn đầu trong biểu thức toán học.

Thí dụ: Thời gian tính toán trung bình của một chương trình là:

a0NlgN + a1N + a2

Ta có thể viết lại là: a0NlgN + O(N)

Với N lớn, ta không cần tìm giá trị của a1 hay a2.

Page 40: Chapter1 Cac khai niem can ban

41

Các kết quả xấp xỉ

Ký hiệu O cho ta một cách tìm ra kết quả xấp xỉ khi N lớn.

Do đó, thông thường chúng ta có thể bỏ qua một số đại lượng khi có tồn tại một số hạng dẫn đầu trong biểu thức.

Example: nếu biểu thức là N(N-1)/2, chúng ta có thể bảo rằng nó khoảng chừng N2/2.

Page 41: Chapter1 Cac khai niem can ban

42

Phân tích một giải thuật lặp

Thí dụ 1 Cho một giải thuật tìm phần tử lớn nhất trong một mảng 1 chiều.

procedure MAX(A, n, max)/* Set max to the maximum of A(1:n) */begin integer i, n; max := A[1]; for i:= 2 to n do if A[i] > max then max := A[i] end

Nếu C(n) là độ phức tạp tính toán của giải thuật được tính theo thao tác so sánh (A[i]> max). Hãy xác định C(n) trong trường hợp xấu nhất.

Page 42: Chapter1 Cac khai niem can ban

43

Phân tích một giải thuật lặp (tt.)

Thao tác căn bản của thủ tục MAX là thao tác so sánh.

Tổng số thao tác so sánh của thủ tục MAX chính là số lần thân vòng lặp được thực thi: (n-1).

Vậy độ phức tạp tính toán của giải thuật là O(n).

Đây là độ phức tạp của cả hai trường hợp trung bình và xấu nhất.

Ghi chú: Nếu thao tác căn bản là phát biểu gán (max := A[i]) thì O(n) là độ phức tạp trong trường hợp xấu nhất.

Page 43: Chapter1 Cac khai niem can ban

44

Phân tích một giải thuật lặp (tt.)

Thí dụ 2: Giải thuật kiểm tra xem có phải mọi phần tử trong mảng 1 chiều là khác biệt nhau.

function UniqueElements(A, n)begin for i:= 1 to n –1 do for j:= i + 1 to n do if A[i] = A[j] return false return trueend

Trong trường hợp xấu nhất, mảng không hề có hai phần tử nào bằng nhau hoặc mảng có hai phần tử cuối cùng bằng nhau. Lúc đó một sự so sánh diễn ra mỗi khi thân vòng lặp trong được thực hiện.

Page 44: Chapter1 Cac khai niem can ban

45

i = 1 j chạy từ 2 cho đến n tức n – 1 lần so sánhi = 2 j chạy từ 3 cho đến n tức n – 2 lần so sánh

.

.i = n -2 j chạy từ n-1 cho đến n tức 2 lần so sánhi = n -1 j chạy từ n cho đến n tức 1 lần so sánh

Tóm lại, tổng số lần so sánh là:

1 + 2 + 3 + … + (n-2) + (n-1) = n(n-1)/2

Vậy độ phức tạp tính toán của giải thuật trong trường hợp xấu nhất là O(n2).

Page 45: Chapter1 Cac khai niem can ban

46

Phân tích một giải thuật lặp (tt.)

Thí dụ 3 (So trùng dòng ký tự - string matching): Tìm tất cả những sự xuất hiện của một khuôn mẫu (pattern) trong một văn bản (text).

Văn bản là một mảng T[1..n] gồm n ký tự và kiểu mẫu là một mảng P[1..m] gồm m ký tự.

Kiểu mẫu P xuất hiện với độ dịch chuyển (shift) s trong văn bản T (tức là, P xuất hiện bắt đầu từ vị trí s+1 trong văn bản T) nếu 1 s n – m và T[s+1..s+m] = P[1..m].

Page 46: Chapter1 Cac khai niem can ban

47

Một giải thuật đơn giản nhất để tìm tất cả những sự xuất hiện của P trong T sẽ dùng một vòng lặp mà kiểm tra điều kiện P[1..m] = T[s+1..s+m] với mỗi trị trong n – m + 1 trị có thể có của s.

procedure NATIVE-STRING-MATCHING(T,P); begin n: = |T|; m: = |P|; for s:= 0 to n – m do if P[1..m] = T[s+1,..,s+m] then print “Pattern occurs with shift” s;end

Page 47: Chapter1 Cac khai niem can ban

48

procedure NATIVE-STRING-MATCHING(T,P); begin n: = |T|; m: = |P|; for s:= 0 to n – m do begin exit:= false; k:=1; while k m and not exit do if P[k] T[s+k] then exit := true else k:= k+1; if not exit then print “Pattern occurs with shift” s; endend

Page 48: Chapter1 Cac khai niem can ban

49

Giải thuật NAIVE STRING MATCHER có hai vòng lặp lồng nhau:- vòng lặp ngoài lặp n – m + 1 lần.- vòng lặp trong lặp tối đa m lần.Do đó, độ phức tạp của giải thuật trong trường hợp xấu nhất là: O((n – m + 1)m).

Page 49: Chapter1 Cac khai niem can ban

50

Phân tích giải thuật đệ quy: các công thức truy hồi căn bản

Có một phương pháp căn bản để phân tích độ phức tạp của các giải thuật đệ quy.

Tính chất của một giải thuật đệ quy thời gian chạy đối với bộ dữ liệu nhập kích thước N tùy thuộc vào thời gian chạy của những bộ dữ liệu nhập nhỏ hơn.

Tính chất này được mô tả bằng một công thức toán học được gọi là hệ thức truy hồi (recurrence relation).

Để dẫn xuất ra độ phức tạp của một giải thuật đệ quy, chúng ta phải giải hệ thức truy hồi này.

Page 50: Chapter1 Cac khai niem can ban

51

Phân tích giải thuật đệ quy bằng phương pháp lặp

Công thức 1: Một chương trình đệ quy mà lặp qua bộ dữ liệu nhập để loại đi một phần tử. Hệ thức truy hồi của nó như sau: CN = CN-1 + N N 2 C1 = 1

CN = CN-1 + N = CN-2 + (N – 1) + N = CN-3 + (N – 2) + (N – 1) + N... = C1 + 2 + … + (N – 2) + (N – 1) + N = 1 + 2 + … + (N – 1) + N = N(N-1)/2 = N2/2

Cách suy ra độ phức tạp bằng phương pháp lặp:

Page 51: Chapter1 Cac khai niem can ban

52

Thí dụ 2

Công thức 2: Một chương trình đệ quy mà tách đôi bộ dữ liệu nhập trong một bước làm việc. Hệ thức truy hồi là: CN = CN/2 + 1 N 2

C1 = 0Cách suy ra độ phức tạp:

Giả sử N = 2n

C(2n) = C(2n-1) + 1 = C(2n-2 )+ 1 + 1 = C(2n-3 )+ 3 . . .

= C(20 ) + n = C1 + n = nCN = n = lgNCN lgN

Page 52: Chapter1 Cac khai niem can ban

53

Thí dụ 3

Công thức 3. Một chương trình đệ quy mà tách đôi bộ dữ liệu nhập trong một bước làm việc nhưng phải xem xét từng phần tử trong dữ liệu nhập. Hệ thức truy hồi là CN = 2CN/2 + N for N 2 C1 = 0

Assume N = 2n

C(2n) = 2C(2n-1) + 2n

C(2n)/2n = C(2n-1)/ 2n-1 + 1 = C(2n-2)/ 2n-2 + 1 +1 . . = n C(2n ) = n.2n

CN = NlgN CN NlgN

Cách suy ra độ phức tạp:

Page 53: Chapter1 Cac khai niem can ban

54

Thí dụ 4

Công thức 4. Một chương trình đệ quy mà tách đôi dữ liệu nhập thành hai nửa trong một bước làm việc . Hệ thức truy hồi làC(N) = 2C(N/2) + 1 for N 2 C(1) = 0

Cách suy ra độ phức tạp:Giả sử N = 2n. C(2n) = 2C(2n-1) + 1C(2n)/ 2n = 2C(2n-1)/ 2n + 1/2n

=C(2n-1)/ 2n-1 + 1/2n

=[C(2n-2)/ 2n-2 + 1/2n-1 ]+ 1/2n

. . . =C(2n-i)/ 2n -i + 1/2n – i +1 + … + 1/2n

Page 54: Chapter1 Cac khai niem can ban

55

Cuối cùng, khi i = n -1, ta được:

C(2n)/2n = C(2)/2 + ¼ + 1/8 + …+ 1/2n = ½ + ¼ + ….+1/2n

1 C(2n) = 2n

C(N) N

Một số hệ thức truy hồi có vẻ giống nhau nhưng mức độ khó khi giải chúng để tìm độ phức tạp thì có thể rất khác nhau.

Page 55: Chapter1 Cac khai niem can ban

56

Nguyên tắc phân tích độ phức tạp trung bình

Để tính độ phức tạp trung bình của một giải thuật A, ta phải làm một số bước:

1. Quyết định một không gian lấy mẫu (sampling space) để diễn tả những dữ liệu đầu vào (kích thước n) có thể có. Giả sử không gian lấy mẫu là S = { I1, I2,…, Ik}

2. Ta phải định nghĩa một phân bố xác xuất p trên S mà biểu diễn mức độ chắc chắn mà dữ liệu đầu vào đó có thể xảy ra.

3. Ta phải tính tổng số tác vụ căn bản được giải thuật A thực hiện để xử lý một trường hợp mẫu. Ta dùng v(Ik) ký hiệu tổng số tác vụ được thực hiện bởi A khi dữ liệu đầu vào thuộc trường hợp Ik.

Page 56: Chapter1 Cac khai niem can ban

57

Phân tích độ phức tạp trung bình (tt.)

4. Ta tính trị trung bình của số tác vụ căn bản bằng cách tính kỳ vọng sau:

Cavg(n) = v(I1).p(I1) + v(I2).p(I2) + …+v(Ik).p(Ik).

Thí dụ: Cho một mảng A có n phần tử. Tìm kiếm vị trí mà trị X xuất hiện trong mảng A.

begin i := 1; while i <= n and X <> A[i] do i := i+1;end

Page 57: Chapter1 Cac khai niem can ban

58

Thí dụ: Tìm kiếm tuần tự

Giả sử X có xuất hiện trong mảng và giả định rằng xác xuất để nó xuất hiện tại một vị trí bất kỳ trong mảng là đều nhau và xác xuất để mỗi trường hợp xảy ra là p = 1/n.

Số lần so sánh để tìm thấy X nếu nó xuất hiện tại vị trí 1 là 1Số lần so sánh để tìm thấy X nếu nó xuất hiện tại vị trí 2 là 2…Số lần so sánh để tìm thấy X nếu nó xuất hiện tại vị trí n là n Tổng số tác vụ so sánh trung bình là: C(n) = 1.(1/n) + 2.(1/n) + …+ N.(1/n) = (1 + 2 + …+ n).(1/n) = (1+2+…+n)/n = n(n+1)/2.(1/n) = (n+1)/2.

Page 58: Chapter1 Cac khai niem can ban

59

Vài chuỗi số thông dụng

Có một vài chuỗi số thông dụng trong việc phân tích độ phức tạp giải thuật.

Chuỗi số cọngS1 = 1 + 2 + 3 + … + nS1 = n(n+1)/2 n2/2S2 = 1 + 22 + 32 + …+ n2

S2 = n(n+1)(2n+1)/6 n3/3

Chuỗi số nhânS = 1 + a + a2 + a3 + … + an

S = (an+1 -1)/(a-1)If 0< a < 1, thenS 1/(1-a)Và khi n , S tiến về 1/(1-a).

Page 59: Chapter1 Cac khai niem can ban

60

Vài chuỗi số thông dụng (tt.)

Tổng chuỗi số điều hoà (Harmonic sum)

Hn = 1 + ½ + 1/3 + ¼ +…+1/nHn = loge n + 0.577215665 được gọi là hằng số Euler.

Một chuỗi số khác cũng rất thông dụng khi phân tích các thao tác làm việc trên cây nhị phân:

1 + 2 + 4 +…+ 2m-1 = 2m -1