You are on page 1of 51

Cấu trúc dữ liệu

Chương 1 - Mở đầu
Nội dung

§ Từ bài toán đến chương trình

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

§ Độ phức tạp của giải thuật

2
Ví dụ “Đèn Hiệu Điều khiển GT”
Một ngã năm như hình vẽ, trong đó C và E là lối đi
một chiều. Hãy thiết kế một bảng đèn hiệu điều khiển
giao thông một cách hợp lý để:
1. Phân chia các lối đi thành các nhóm, mỗi nhóm gồm
các lối đi có thể đồng thời nhưng không xảy ra tai nạn
giao thông.
2. Số lượng nhóm là ít nhất có thể được.
D
C
E

B A
Ta cần làm những gì để giải bài toán này bằng 3
chương trình máy tính?
Từ bài toán đến chương trình

Các bước tiếp cận một bài toán:


1. Mô hình hóa bài toán thực tế bằng một mô hình toán học.
2. Tìm giải thuật (algorithms) trên mô hình này: chỉ nêu
phương hướng giải hoặc các bước giải một cách tổng quát.
3. Hình thức hoá giải thuật bằng cách viết một thủ tục bằng
ngôn ngữ giả, rồi chi tiết hoá dần ("mịn hoá") các bước giải
tổng quát ở trên. Kết hợp việc dùng các kiểu dữ liệu trừu
tượng và các cấu trúc điều khiển trong ngôn ngữ lập trình
để mô tả giải thuật.
4. Cài đặt giải thuật trong một ngôn ngữ lập trình (NNLT) cụ
thể. Các cấu trúc dữ liệu được cung cấp trong NNLT được
dùng để thể hiện các kiểu dữ liệu trừu tượng, các lệnh và
cấu trúc điều khiển trong NNLT được dùng để cài đặt các
4
bước của giải thuật.
Từ bài toán đến chương trình

§ Giải thuật là một chuỗi hữu hạn các thao tác để


giải một bài toán nào đó. (cách để giải một bài
toán nào đó)
§ Các tính chất quan trọng của giải thuật là:
- Hữu hạn (finiteness): giải thuật phải luôn luôn kết
thúc sau một số hữu hạn bước.
- Xác định (definiteness): mỗi bước của giải thuật phải
được xác định rõ ràng và phải được thực hiện chính
xác, nhất quán.
- Hiệu quả (effectiveness): các thao tác trong giải
thuật phải được thực hiện trong một lượng thời gian
hữu hạn.
5
Ví dụ “Đèn Hiệu Điều khiển GT”
D
C
E

B A

Một ngã năm như hình vẽ, trong đó C và E là


lối đi một chiều. Hãy thiết kế một bảng đèn
hiệu điều khiển giao thông một cách hợp lý
để:
1. Phân chia các lối đi thành các nhóm, mỗi nhóm
gồm các lối đi có thể đồng thời nhưng không xảy
ra tai nạn giao thông.
2. Số lượng nhóm là ít nhất có thể được 6
Ví dụ “Đèn Hiệu Điều khiển GT”
AB AC AD
D
C BA BC BD
E
DA DB DC

B A
EA EB EC ED

1. Mô AB AC AD
hình
Mô hình hóa bài toán
hóa BA BC BD theo mô hình toán là
bài đồ thị (Graph).

toán DA DB DC
thực
tế EA EB EC ED 7
Ví dụ “Đèn Hiệu Điều khiển GT”

Mô hình hóa bài toán thực tế: phải giải quyết bài
toán “Tô màu cho đồ thị” sao cho:
- Các lối đi được phép đi đồng thời sẽ được tô
cùng một màu => 2 đỉnh có cạnh nối nhau sẽ
không được tô cùng màu.
- Số nhóm là ít nhất <=> ta phải tính toán để
dùng số màu ít nhất.

8
Ví dụ “Đèn Hiệu Điều khiển GT”

2. Tìm giải thuật


Xây dựng một giải thuật “Gready” để tô màu đồ thị
- Chọn một đỉnh chưa tô màu và tô nó bằng một
màu mới C nào đó.
- Duyệt danh sách các đỉnh chưa tô màu. Đối với
một đỉnh chưa tô màu, xác định xem nó có kề với
một đỉnh nào được tô bằng màu C đó không. Nếu
không có, tô nó bằng màu C đó.
9
Ví dụ “Đèn Hiệu Điều khiển GT”

Một lời giải cho


bài toán “Đèn
Hiệu Điều khiển
GT” khi áp dụng
giải thuật
“Gready”
F Dùng 4 màu tô, mỗi màu tượng
trưng cho một màu đèn
F Các lối đi có cùng một màu sẽ
được đi đồng thời 10
Áp dụng Greedy cho bài toán«
đèn giao thông »
• Tô màu xanh cho các
đỉnh: AB AC AD

AB,AC,AD,BA,DC,ED BC
BA BD
• Tô màu đỏ cho các
DA DB DC
đỉnh: BC,BD,EA
EA EB EC ED
• Tô màu tím cho các
đỉnh: DA,DB
• Tô màu vàng cho các
đỉnh: EB,EC

11
Greedy có cho lời giải tối ưu?

• Ta có thể trở lại mô hình của


bài toán và dùng tính chất
của đồ thị để kiểm tra kết
quả. Nhận xét rằng:
– Một đồ thị có k đỉnh và mỗi
cặp đỉnh bất kỳ đều được nối
nhau thì phải dùng k màu để
tô.
– Một đồ thị chứa k đỉnh và mỗi
cặp đỉnh bất kỳ đều được nối
nhau thì phải dùng ít nhất k
màu để tô.

12
Greedy có cho lời giải tối ưu?

Tô theo GREEDY Tối ưu


(xét lần lượt theo số thứ tự các (thử tất cả các khả năng)
đỉnh)
1: đỏ; 2: đỏ 1,3,4 : đỏ
3: xanh;4: xanh 2,5 : xanh
5: vàng

13
Ngôn ngữ giả và tinh chế
từng bước
• Mô hình hóa à mô hình thích hợp cho bài toán
• Hình thức hoá một giải thuật trong thuật ngữ của mô
hình đó.
– Khởi đầu là viết những mệnh đề tổng quát
– tinh chế dần thành những chuỗi mệnh đề cụ thể hơn
– Cuối cùng là các chỉ thị thích hợp trong một ngôn ngữ lập trình.

• Ví dụ: Heuristic GREEDY, giả sử đồ thị là G, heuristic sẽ


xác định một tập hợp Newclr các đỉnh của G được tô
cùng một màu, mà ta gọi là màu mới C ở trên. Để tiến
hành tô màu hoàn tất cho đồ thị G thì Heuristic này phải
được gọi lặp lại cho đến khi toàn thể các đỉnh đều được
tô màu.

14
Thủ tục GREEDY với ngôn ngữ giả
PASCAL
PROCEDURE GREEDY ( var G: GRAPH ; var Newclr: SET );
begin
{1}Newclr := Æ;
{2}for (mỗi đỉnh v chưa tô màu của G) do
{3} if (v không được nối với một đỉnh nào trong Newclr) then
begin
{4} đánh dấu v đã được tô màu;
{5} thêm v vào Newclr;
end;
end;
Trong thủ tục bằng ngôn ngữ giả:
•từ khoá của ngôn ngữ PASCAL
•mệnh đề tiếng Việt.
•"kiểu dữ liệu trừu tượng" GRAPH, SET
15
Tinh chế từng bước

• Mệnh đề if ở {3} có thể chi tiết hoá hơn nữa như sau:
PROCEDURE GREEDY ( var G: GRAPH ; var Newclr: SET );
begin
{1} Newclr:= Æ;
{2} for (mỗi đỉnh v chưa tô màu của G) do begin
{3.1} found:=false;
{3.2} for (mỗi đỉnh w trong Newclr) do
{3.3} if (có cạnh nối giữa v và w) then
{3.4} found:=true;
{3.5} if found=false then begin
{4} đánh dấu v đã được tô màu;
{5} thêm v vào Newclr;
end;
end;
end;

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

• GRAPH và SET ta coi như tập hợp


– Có nhiều cách để biểu diễn tập hợp trong ngôn
ngữ lập trình: xem các tập hợp như là một danh
sách (LIST) các số nguyên biểu diễn chỉ số của
các đỉnh và kết thúc bằng một giá trị đặc biệt
NULL.

17
PROCEDURE GREEDY ( var G: GRAPH ; var Newclr: LIST );
var found:boolean;
v,w :integer;
begin
Newclr:= Æ;
v:= đỉnh đầu tiên chưa được tô màu trong G;
while v<>null do begin
found:=false;
w:=đỉnh đầu tiên trong newclr;
while( w<>null) and (not found) do begin
if có cạnh nối giữa v và w then
found:=true;
else w:= đỉnh kế tiếp trong newclr;
end;
if found=false then begin
đánh dấu v đã được tô màu;
thêm v vào Newclr;
end;
v:= đỉnh chưa tô màu kế tiếp trong G;
end;
end;

18
Chú ý việc dùng ngôn ngữ giả

• Mục đích:
– phát họa ý tưởng của giải thuật
– tránh sa đà vào cú pháp của ngôn ngữ.

• Các bước tinh chế về sau: thủ tục ngôn ngữ giả càng
gần giống với chương trình trong một ngôn ngữ lập
trình.

• Việc chọn ngôn ngữ giả tựa PASCAL hay tựa C hay tựa
một một ngữ lập trình nào khác là tùy thuộc vào thói
quen của người sử dụng, vào sự quen thuộc với ngôn
ngữ lập trình.

19
Nếu người dùng quen thuộc với ngôn ngữ C
có thể viết thủ tục với ngôn ngữ giả tựa C như
sau :

void GREEDY ( GRAPH& G, SET& Newclr ){


/*1*/ Newclr = Æ;
/*2*/ for (mỗi đỉnh v chưa tô màu của G)
/*3*/ if (v không được nối với một đỉnh nào trong Newclr){
/*4*/ đánh dấu v đã được tô màu;
/*5*/ thêm v vào Newclr;
}
}

20
Thủ tục tinh chế được viết tựa C như sau:

void GREEDY ( GRAPH& G, SET& Newclr )


{
/*1*/ Newclr= Æ;
/*2*/ for (mỗi đỉnh v chưa tô màu của G) {
/*3.1*/ int found=0;
/*3.2*/ for (mỗi đỉnh w trong Newclr)
/*3.3*/ if (có cạnh nối giữa v và w)
/*3.4*/ found=1;
/*3.5*/ if (!found) {
/*4*/ đánh dấu v đã được tô màu;
/*5*/ thêm v vào Newclr;
}
}
}

21
Tinh chế thêm một bước nữa:

void GREEDY ( GRAPH& G, LIST& Newclr ){


Newclr= Æ;
int v= đỉnh đầu tiên chưa được tô màu trong G;
while (v<>null) {
int found=0;
int w=đỉnh đầu tiên trong newclr;
while( w<>null) && (!found)
If (có cạnh nối giữa v và w) found=1;
else w= đỉnh kế tiếp trong newclr;
if (!found) {
Đánh dấu v đã được tô màu;
Thêm v vào Newclr;
}
v= đỉnh chưa tô màu kế tiếp trong G;
}
}
22
Ví dụ “Đèn Hiệu Điều khiển GT”
3. Hình thức hóa giải thuật
Giải thuật mức 0: Giải thuật mức 1:
void GREEDY (GRAPH *G, void GREEDY (GRAPH *G,
SET *Newclr ) SET *Newclr )
{ {
/*1*/ Newclr = Æ; /*1*/ Newclr= Æ;
/*2*/ while (còn đỉnh v chưa /*2*/ while (còn đỉnh v chưa tô màu của G){
tô màu của G) /*3.1*/ found=0;
/*3*/ if (v không được nối với một /*3.2*/ while (mỗi đỉnh w trong Newclr)
đỉnh nào trong Newclr){ /*3.3*/ if (có cạnh nối giữa v và w)
/*4*/ đánh dấu v đã được tô màu; /*3.4*/ found=1;
/*5*/ thêm v vào Newclr; /*3.5*/ if (found==0) {
} /*4*/ đánh dấu v đã được tô màu;
} /*5*/ thêm v vào Newclr;
Thủ tục GREEDY với }//if
ngôn ngữ giả C }//while Chi tiết hóa bước 3 của
} giải thuật mức 0 23
Ví dụ “Đèn Hiệu Điều khiển GT”
Giải thuật mức 2:
void GREEDY ( GRAPH *G, LIST *Newclr )
{ int found; int v, w ;
Newclr= Æ;
v= đỉnh đầu tiên chưa được tô màu trong G;
3. while (v<>null) {
found=0;
Hình w=đỉnh đầu tiên trong Newclr; Chi tiết hóa
thức while( w<>null) && (found=0) { giải thuật
if (có cạnh nối giữa v và w) mức 1
hóa found=1;
giải else w= đỉnh kế tiếp trong Newclr;
} //while
thuật if (found==0) {
Đánh dấu v đã được tô màu;
Thêm v vào Newclr;
}//if
v= đỉnh chưa tô màu kế tiếp trong G;
}//while
} 24
Tóm tắt các bước tiếp cận
với một bài toán
1. Mô hình hoá bài toán bằng một mô hình toán học thích
hợp.
2. Tìm giải thuật trên mô hình này.
Giải thuật có thể mô tả một cách không hình thức, tức là nó chỉ
nêu phương hướng giải hoặc các bước giải một cách tổng
quát.
3. Hình thức hoá giải thuật bằng cách viết một thủ tục
bằng ngôn ngữ giả, rồi chi tiết hoá dần ("mịn hoá") , kết
hợp với việc dùng các kiểu dữ liệu trừu tượng và các
cấu trúc điều khiển trong ngôn ngữ lập trình để mô tả
giải thuật.
4. Cài đặt giải thuật trong một ngôn ngữ lập trình cụ thể
(Pascal,C,...). Ở bước này ta dùng các cấu trúc dữ liệu
được cung cấp trong ngôn ngữ, ví dụ Array, Record,...
để thể hiện các kiểu dữ liệu trừu tượng,
25
KIỂU DỮ LIỆU TRỪU TƯỢNG

• Khái niệm trừu tượng hóa • Trừu tượng hóa dữ liệu


– Trừu tượng hóa chương – Một kiểu dữ liệu trừu tượng
trình (ADT): một mô hình toán
void Main() { học cùng với một tập hợp
Input_Matrix(A); các phép toán (operator)
Input_Matrix(B); trừu tượng được định
Matrix_mult(A,B,C); nghĩa trên mô hình đó.
Print_Matrix(C); – Ví dụ tập hợp số nguyên
} cùng với các phép toán
hợp, giao, hiệu là một kiểu
dữ liệu trừu tượng.
– ADT là sự tổng quát hoá
của các kiểu dữ liệu
nguyên thuỷ.

26
ADT - tổng quát hoá các kiểu dữ liệu
nguyên thuỷ
• Danh sách (LIST) các số nguyên và các phép toán trên danh sách
– Tạo một danh sách rỗng.
– Lấy phần tử đầu tiên trong danh sách và trả về giá trị null nếu danh sách rỗng.
– Lấy phần tử kế tiếp trong danh sách và trả về giá trị null nếu không còn phần tử
kế tiếp.
– Thêm một số nguyên vào danh sách.
• Nếu chúng ta viết các chương trình con thực hiện các phép toán này, thì ta
dễ dàng thay các mệnh đề hình thức trong giải thuật bằng các câu lệnh đơn
giản

Câu lệnh Mệnh đề hình thức

MAKENULL(newclr) newclr= Æ

w=FIRST(newclr) w=phần tử đầu tiên trong newclr

w=NEXT(w,newclr) w=phần tử kế tiếp trong newclr

INSERT( v,newclr) Thêm v vào newclr


27
KIỂU DỮ LIỆU - CẤU TRÚC DỮ
LIỆU VÀ KIỂU DỮ LIỆU TRỪU
TƯỢNG
• Kiểu dữ liệu là một tập hợp – Kiểu dữ liệu trừu tượng là một
các giá trị và một tập hợp các kiểu dữ liệu do chúng ta định
phép toán trên các giá trị đó. nghĩa ở mức khái niệm
(conceptual), chưa được cài
– kiểu dữ liệu sơ cấp: int, char đặt cụ thể bằng một ngôn ngữ
– kiểu dữ liệu có cấu trúc; array, lập trình.
struct. – Khi cài đặt một kiểu dữ liệu
• Cấu trúc dữ liệu: trừu tượng trong một ngôn
– Các kiểu dữ liệu có cấu trúc ngữ lập trình cụ thể: :
cơ bản (cung cấp bởi NNLT) • Biểu diễn kiểu dữ liệu trừu
– các cấu trúc phức hợp (được tượng (ở mức khái niệm)
bằng một cấu trúc dữ liệu
tạo ra từ các kiểu dữ liệu cơ hoặc một kiểu dữ liệu trừu
bản) tượng khác đã được cài đặt.
• Một kiểu dữ liệu trừu tượng • Viết các chương trình con
là một mô hình toán học cùng thực hiện các phép toán trên
với một tập hợp các phép toán kiểu dữ liệu trừu tượng.
trên nó.

28
Kiểu dữ liệu trừu tượng
(Abstract Data Types - ADT)

§ Trừu tượng hóa trong tin học là đơn giản hóa:


che đi những chi tiết, làm nổi bật cái tổng thể.
- Trừu tượng hóa chương trình: phân chia
chương trình thành các chương trình con =>
che dấu tất cả các lệnh cài đặt chi tiết trong các
chương trình con ở cấp độ chương trình chính.
- Trừu tượng hóa dữ liệu là định nghĩa các kiểu
dữ liệu trừu tượng.
Kiểu dữ liệu trừu tượng
(Abstract Data Types - ADT)

§ Kiểu dữ liệu (Data type): là một tập hợp các


giá trị và một tập hợp các phép toán trên các
giá trị đó.
§ Có 2 loại kiểu dữ liệu:
- Kiểu dữ liệu sơ cấp
• Giá trị dữ liệu là đơn nhất.
• Ví dụ: char, int, …
- Kiểu dữ liệu có cấu trúc (cấu trúc dữ liệu)
• Giá trị dữ liệu là sự kết hợp của các giá trị khác.
• Ví dụ: mảng.
Kiểu dữ liệu trừu tượng
(Abstract Data Types - ADT)

§ Kiểu dữ liệu trừu tượng (ADT): mô hình toán


học cùng với một tập hợp các phép toán trừu
tượng được định nghĩa trên mô hình đó.
§ Cài đặt kiểu dữ liệu trừu tượng:
– Tổ chức lưu trữ: một cấu trúc dữ liệu.
– Viết chương trình con thực hiện các phép toán.
Từ bài toán đến chương trình

Các bước tiếp cận một bài toán:


1. Mô hình hóa bài toán thực tế bằng một mô hình toán học.
2. Tìm giải thuật (algorithms) trên mô hình này: chỉ nêu
phương hướng giải hoặc các bước giải một cách tổng quát.
3. Hình thức hoá giải thuật bằng cách viết một thủ tục bằng
ngôn ngữ giả, rồi chi tiết hoá dần ("mịn hoá") các bước giải
tổng quát ở trên. Kết hợp việc dùng các kiểu dữ liệu trừu
tượng và các cấu trúc điều khiển trong ngôn ngữ lập trình
để mô tả giải thuật.
4. Cài đặt giải thuật trong một ngôn ngữ lập trình (NNLT) cụ
thể. Các cấu trúc dữ liệu được cung cấp trong NNLT được
dùng để thể hiện các kiểu dữ liệu trừu tượng, các lệnh và
cấu trúc điều khiển trong NNLT được dùng để cài đặt các
32
bước của giải thuật.
Độ phức tạp của giải thuật

§ Một bài toán có thể có nhiều cách giải khác nhau.


§ Ta cần phải phân tích, đánh giá giải thuật để:
– Lựa chọn một giải thuật tốt nhất trong các giải
thuật để cài đặt chương trình giải quyết bài toán
đặt ra.
– Cải tiến giải thuật hiện có để được một giải thuật
tốt hơn.
§ Độ phức tạp của giải thuật (algorithm complexity)
liên quan đến mức độ nhanh hay chậm thực hiện
một giải thuật.
Độ phức tạp của giải thuật

Ví dụ: Hãy sắp xếp mảng có n phần tử theo thứ tự


tăng dần.
§ Giải thuật sắp xếp thông dụng dùng 2 vòng lặp i:1->n và
j:1->n để đổi chỗ. void Sort(int a[], int n){
for (int i = 0; i < n; i++){
for (int j = 0; j < n; j++){
if (a[j] > a[i]) {
int tmp = a[i];
a[i] = a[j];
a[j] = tmp; }
}
}
}

có độ phức tạp là O(n2)


§ Giải thuật sắp xếp Quicksort có độ phức tạp O(n*log(n)).
Độ phức tạp của giải thuật

§ Thời gian thực hiện một giải thuật 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.
- Ví dụ : 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ột hằng
số.
§ Thời gian thực hiện giải thuật là một hàm
không âm, tức là T(n) ³ 0 " n ³ 0.
Độ phức tạp của giải thuật

§ Thời gian thực hiện giải thuật 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.
§ Vì vậy thường ta coi T(n) là thời gian thực
hiện chương trình trong trường hợp xấu nhất
trên dữ liệu vào có kích thước n, tức là: T(n)
là thời gian lớn nhất để thực hiện chương
trình đối với mọi dữ liệu vào có cùng kích
thước n.
Độ phức tạp của giải thuật

§ Trong những ứng dụng thực tiễn, chúng ta không


cần biết chính xác hàm T(n) mà chỉ cần biết một
ước lượng đủ tốt của nó.
§ Ước lượng thường dùng nhất là ước lượng cận
trên O-lớ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, N0 sao cho T(n) ≤ Cf(n)
với mọi n ≥ N0 (tức là T(n) có tỷ suất tăng là f(n)) và
kí hiệu T(n)=O(f(n)).
§ Chú ý: O(Cf(n))=O(f(n)) với C là hằng số. Ðặc
biệt O(C)=O(1).
Độ phức tạp của giải thuật

§ Các dạng phức tạp thường gặp


Hàm thể hiện
Dạng phức tạp Thời gian thực hiện
độ phức tạp
Hằng O(1)
Lôgarit log(n) O(log(n))
Tuyến tính n Dạng
O(n)
n*log(n) đa O(n*log(n))
Bậc hai n2 thức O(n2)
Khối n3 O(n3)
Dạng
Mũ 2n,n!,nk mũ
O(2n), O(n!), O(nk)

§ Một giải thuật có độ phức tạp hàm mũ thì phải tìm


cách cải tiến giải thuật.
Ví dụ: nếu chúng ta lấy một máy tính trung bình từ năm
2008 với giả định rằng nó có thể thực hiện khoảng
50,000,000 phép toán cơ bản mỗi giây.

Algorithm n=10 n=20 n=50 n=100 1,000 10,000 100,000


O(1) < 1 sec. < 1 sec. < 1 sec. < 1 sec. < 1 sec. < 1 sec. < 1 sec.
O(log(n)) < 1 sec. < 1 sec. < 1 sec. < 1 sec. < 1 sec. < 1 sec. < 1 sec.
O(n) < 1 sec. < 1 sec. < 1 sec. < 1 sec. < 1 sec. < 1 sec. < 1 sec.
O(n*log(n)) < 1 sec. < 1 sec. < 1 sec. < 1 sec. < 1 sec. < 1 sec. < 1 sec.
O(n2) < 1 sec. < 1 sec. < 1 sec. < 1 sec. < 1 sec. 2 sec. 3-4 min.
10,0003/(60phut*60giay) = 5.55 231.5
O(n3) < 1 sec. < 1 sec. < 1 sec. < 1 sec. 20 sec.
5.55 giờ hours days
260
O(2n) < 1 sec. < 1 sec. treo treo treo treo
days
O(n!) < 1 sec. treo treo treo treo treo treo
O(nn) 3-4 min. treo treo treo treo treo treo
https://introprogramming.info/english-intro-csharp-book/read-online/chapter-19-
data-structures-and-algorithm-complexity/
Tính độ phức tạp của giải thuật

Bài tập: giả sử máy tính có thể thực hiện 1 hoạt


động chính của giải thuật trong 1 micro giây.
§ Xác định thời gian (số năm) thực hiện giải thuật
có độ phức tạp O(2n), O(3n) và O(n!) khi n=10,
n=40 và n=100?
§ Nếu tuổi thọ của một hành tinh khoảng 20 tỷ
năm thì hành tinh đó vẫn còn tồn tại khi giải
thuật có độ phức tạp O(n!) với n=40 thực hiện
xong?
Tính độ phức tạp của giải thuật

§ Quy tắc cộng: Nếu T1(n) và T2(n) là thời gian


thực hiện của hai đoạn chương trình P1 và 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 chương trình đó nối tiếp
nhau là T(n)=O(max(f(n),g(n))).
§ Quy 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ồng nhau là T(n) = O(f(n).g(n)).
Tính độ phức tạp của giải thuật

§ Thời gian thực hiện của mỗi lệnh gán, nhập, xuất 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.
§ 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 IF hoặc sau ELSE 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).
§ Thờ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ân vò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òng lặ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 độ phức tạp của giải thuật

Ví dụ: tính độ phức tạp của hàm tìm phần tử lớn nhất
trong một mảng có n số nguyên?
int FindMaxElement(int[] a, int n)
{
int max = int.MinValue; O(1)
O(max(1, n, 1)) for (int i = 0; i < n; i++){
=O(n)
if (a[i] > max){
O(1)
O(n*1) max = a[i]; O(1) O(max(1,1))=O(1)
=O(n) }
}
return max; O(1)
https://introprogramming.info/english-intro-csharp-book/read-
} online/chapter-19-data-structures-and-algorithm-complexity/
Tính độ phức tạp của giải thuật

Ví dụ: tính độ phức tạp của đoạn chương trình sau:


/*1*/ Sum=0;
/*2*/ for(i=1;i<=n;i++) {
/*3*/ scanf(“%d”,&x);
/*4*/ Sum=Sum+x;
}
- Dòng 3,4 là các thao tác nhập, gán nên độ phức tạp là
O(1). Áp dụng quy tắc cộng, độ phức tạp cho cả 3 và
4 là O(1)
- Vòng lặp 2 thực hiện n lần mỗi lần O(1) => theo quy
tắc nhân độ phức tạp của 2 là O(n)
- Dòng 1 là phép gán => O(1)
- Áp dụng quy tắc cộng cho 1 và 2 -> T(n) = O(n)
Tính độ phức tạp của giải thuật

Ví dụ: tính độ phức tạp cho đoạn chương trình sau:


/*1*/ Sum=0;
/*2*/ for(i=1;i<=n;i++) {
/*3*/ if (i%2==0)
/*4*/ Sum=Sum+i;
}
- Dòng 3 rẽ nhánh với kiểm tra điều kiện mất O(1), công
việc thực hiện khi điều kiện đúng mất O(1) => theo
quy tắc cộng độ phức tạp của 3 là O(1)
- Vòng lặp 2 thực hiện n lần mỗi lần O(1) => theo quy
tắc nhân độ phức tạp của 2 là O(n)
- Dòng 1 là phép gán => O(1)
- Áp dụng quy tắc cộng cho 1 và 2 => T(n) = O(n)
Tính độ phức tạp của giải thuật

Ví dụ: tính độ phức tạp của đoạn chương trình sau:


/*1*/ Sum=0;
/*2*/ for(i=1;i<=n;i=i*2) {
/*3*/ scanf(“%d”,&x);
/*4*/ Sum=Sum+x;
}
- Cách tính tương tự như ví dụ trang 29, tuy nhiên ở đây do vòng
lặp 2 thực hiện ~ logn lần, do đó T(n) = O(logn)

Bài tập: tính độ phức tạp của đoạn chương trình sau:
/*1*/ Sum1=0;
/*2*/ k=1;
/*3*/ while (k<=n) {
/*4*/ for(j=1;j<=n;j++)
/*5*/ Sum1=Sum1+1;
/*6*/ k=k*2;
}
Tính độ phức tạp của giải thuật

Bài tập: tính độ phức tạp của hàm Sum3 sau?


long Sum3(int n)
{
long sum = 0;
for (int a = 1; a < n; a++) {
for (int b = 1; b < n; b++){
for (int c = 1; c < n; c++){
sum += a * b * c;
}
}
}
return sum;
}
Tính độ phức tạp của giải thuật

Ví dụ: tính độ phức tạp của đoạn chương trình sau?


int count = 0;
for (int i = 0; i < n; i++)
for (int j = 0; j < i; j++)
count++;
Khi i=0, đoạn chương trình chạy 0 lần lệnh count++.
Khi i=1, đoạn chương trình chạy 1 lần lệnh count++.
Khi i=2, đoạn chương trình chạy 2 lần lệnh count++.

Khi i=n-1, đoạn chương trình chạy n-1 lần lệnh count++.
Tổng số lần chạy lệnh count++ là: 0 + 1 + 2 + ⋯ + n − 1 =
!∗(!$%)
=> Độ phức tạp của đoạn chương trình O(n2)
'
https://www.hackerearth.com/practice/basic-programming/complexity-analysis/time-and-space-
complexity/tutorial/#:~:text=Time%20complexity%20of%20an%20algorithm,the%20length%20of
%20the%20input.&text=Let%20each%20operation%20takes%20time.
Tính độ phức tạp của giải thuật

Bài tập: tính độ phức tạp đoạn chương trình sau?


int count = 0;
for (int i = n; i > 0; i/=2)
for (int j = 0; j < i; j++)
count++;
Tính độ phức tạp n
1
A
0.1s
B
0.005s
của giải thuật 2
3
0.4s
0.9s
0.04s
0.135s
4 1.6s 0.32s
5 2.5s 0.625s
6 3.6s 1.08s
Ví dụ: để giải một bài toán 7 4.9s 1.715s
thực tế, ta có thể sử dụng 8 6.4s 2.56s
một trong hai giải thuật A 9 8.1s 3.645s
10 10s 5s
hoặc B. Giải thuật A có
11 12.1s 6.655s
TA(n)=100*n*n ms = O(n2); 12 14.4s 8.64s
giải thuật B có TB(n)=5*n*n*n 13 16.9s 10.985s
ms = O(n3). Ta nên chọn giải 14 19.6s 13.72s
thuật A hay giải thuật B? 15 22.5s 16.875s
16 25.6s 20.48s
17 28.9s 24.565s
18 32.4s 29.16s
19 36.1s 34.295s
20 40s 40s
21 44.1s 46.305s
… … …
http://www.dcs.gla.ac.uk/~pat/52233/complexity.html 30 90s 135s
40 160s 320s
Q&A?

You might also like