Tải bản đầy đủ (.docx) (16 trang)

Phân tích và đánh giá thuật toán thuật toán tìm kiếm

Bạn đang xem bản rút gọn của tài liệu. Xem và tải ngay bản đầy đủ của tài liệu tại đây (153.88 KB, 16 trang )

KHOA CÔNG NGHỆ THÔNG TIN

Bài tập lớn
PHÂN TÍCH ĐÁNH GIÁ THUẬT TOÁN

ĐỀ SỐ 26: Thuật toán tìm kiếm. Cho mảng X có n số nguyên. Hãy xây dựng thuật
toán tìm số lớn thứ hai trong mảng X. Đánh giá độ phức tạp của nó trong trường
hợp xấu nhất.

Giảng viên hướng dẫn
Học viên thực hiện
Mã Sinh viên
Lớp

Hà Nội, 05/2014

: PGS. TS. Đào Thanh Tĩnh
: Dư Quang Trung
: 13870829
: HTTT25B


Mục lục

1. Đặt vấn đề.
Bài toán tìm kiếm là một trong những bài toán phổ biến và có ý nghĩa quan trọng trong cả
cơ sở lý thuyết lẫn ứng dụng thực tiễn của công nghệ thông tin và truyền thông. Quá trình
phát triển của công nghệ khiến ngày càng có nhiều người tiếp cận và sử dụng các thiết bị,
giải pháp công nghệ thông tin. Đi cùng đó là một khối lượng dữ liệu khổng lồ được lưu
trữ, sinh ra hàng ngày. Việc quản lý các cơ sở dữ liệu lớn với các tác vụ cơ bản như: sắp
xếp, truy xuất dữ liệu trở nên hết sức phức tạp. Nhiệm vụ nghiên cứu và triển khai các


giải thuật tìm kiếm dữ liệu một cách hiệu quả được đặt ra để tối ưu hóa hệ thống về nhiều
mặt: chi phí triển khai, duy trì, năng lượng tiêu thụ, độ tin cậy, an toàn… Đề tài nhỏ dưới
đây tập trung vào nghiên cứu trình bày các vấn đề sau



Cơ sở lý thuyết về các bài toán tìm kiếm và giải thuật tìm kiếm.
Ứng dụng vào bài toán tìm kiếm phần tử lớn thứ nhì của một mảng số nguyên cho

trước
• Phân tích các tình huống phức tạp trong quá trình giải quyết bài toán.
2. Tổng quan lý thuyết bài toán tìm kiếm và các thuật toán tìm kiếm.
Thuật toán tìm kiếm nhận đầu vào (input) là một mô hình dữ liệu và một yêu cầu đề bài,
cho ra kết quả đầu ra (output) sau một số bước tính toán hữu hạn, xác định. Đầu vào và
đầu ra của thuật toán tìm kiếm như sau:
2




Đầu vào (input): một cơ sở dữ liệu được lưu trữ dưới dạng một cấu trúc dữ liệu

như mảng, danh sách liên kết, cây, đồ thị… và một yêu cầu tìm kiếm, thường là
một phần tử trong cơ sở dữ liệu thỏa mãn một tính chất mong muốn nào đó.
• Đầu ra (output): phần tử có tính chất mong muốn theo yêu cầu tìm kiếm đầu vào.
Đầu ra của thuật toán tìm kiếm phải đảm bảo: phần tử đầu ra phải là phần tử thỏa
mãn các tính chất mong muốn theo yêu cầu tìm kiếm, đồng thời không bỏ sót bất
cứ phần tử nào cũng thỏa mãn các tính chất mong muốn theo yêu cầu tìm kiếm.
Do dữ liệu đầu vào của một thuật toán tìm kiếm được lưu trữ dưới nhiều dạng cấu trúc
khác nhau, các thuật toán tìm kiếm có thể được phân loại dựa trên mô hình dữ liệu đầu

vào của chúng. Có ba mô hình dữ liệu đầu vào cơ bản cho một thuật toán tìm kiếm: mảng
(danh sách), cây và đồ thị.
2.1. Tìm kiếm trên mảng (danh sách)
Thuật toán tìm kiếm trên mảng là loại giải thuật tìm kiếm cơ bản nhất. Thuật toán này tìm
kiếm trong một tập hợp một phần tử chứa một khóa nào đó. Thuật toán tìm kiếm trên
mảng đơn giản nhất là tìm kiếm tuyến tính. Thuật toán tìm kiếm tuyến tính kiểm tra từng
phần tử trong danh sách theo thứ tự của danh sách đó. Thuật toán này có độ phức tạp tính
toánO(n), trong đó n là số phần tử trong danh sách. Thuật toán tìm kiếm tuyến tính trên
danh sách có độ phức tạp tính toán lớnnhưng có thể sử dụng trực tiếp cho một danh sách
bất kỳ mà không cần tiền xử lý. Tìm kiếm nhị phân là một thuật toán cao cấp hơn với độ
phức tạp tính toánO(log n). Đối với các danh sách lớn, thuật toán này tốt hơn hẳn tìm
kiếm tuyến tính, nhưng nó đòi hỏi danh sách phải được sắp xếp từ trước và đòi hỏi khả
năng truy nhập ngẫu nhiên vào bất cứ phần tử nào nằm trong mảng. Một thuật toán tìm
kiếm trên mảng hiệu quả khác là thuật toán tìm kiếm nội suy. Độ phức tạp của thuật toán
tìm kiếm nội suy với mảng n phần tử làO(n). Tuy nhiên, trong trường hợp các phần tử của
mảng là số ngẫu nhiên phân bố đều (trường hợp phổ biến nhất), độ phức tạp của thuật
toán có thể đạt chỉ còn O(loglogN).
Bảng băm (hash table) cũng được dùng cho tìm kiếm trên danh sách. Thuật toán này đòi
hỏi thời gian hằng số trong trường hợp trung bình, nhưng lại cần nhiều không gian bộ
nhớ và độ phức tạp thuật toánO(n) cho trường hợp xấu nhất. Một phương pháp tìm kiếm
khác dựa trên các cấu trúc dữ liệu chuyên biệt sử dụng cây tìm kiếm nhị phân cân bằng

3


và độ phức tạp thuật toánO(log n); các giải thuật loại này có thể coi là mở rộng của thuật
toán tìm kiếm nhị phân để cho phép chèn và xóa nhanh.
Đa số các giải thuật tìm kiếm trên mảng như tìm kiếm tuyến tính, tìm kiếm nhị phân, và
cây tìm kiếm nhị phân cân bằng đều có thể được mở rộng để tìm tất cả các giá trị nhỏ hơn
hoặc lớn hơn một khóa cho trước.

2.2. Tìm kiếm trên cây.
Cây là một cấu trúc dữ liệu phổ biến và thường dùng trong tin học, nhất là trong việc xây
dựng các mô hình dữ liệu và cấu trúc dữ liệu. Cây là khái niệm quan trọng trong lý thuyết
đồ thị, cấu trúc dữ liệu và giải thuật. Cây là một đồ thị mà trong đó hai đỉnh bất kì đều
được nối với nhau bằng đúng một đường đi, hay nói cách khác đồ thị liên thông bất kỳ
không có chu trình là một cây. Cấu trúc cây được sử dụng rộng rãi trong các cấu trúc dữ
liệu của ngành khoa học máy tính như cây nhị phân, đống, trie, cây Huffman cho nén dữ
liệu, v.v...
Tìm kiếm trên cây là một trong những thuật toán tìm kiếm phổ biến. Các thuật toán này
tìm kiếm trên các cây gồm các nút, cây này có thể là cây tường minh hoặc được xây dựng
dần trong quá trình tìm kiếm. Nguyên lý cơ bản là: một nút được lấy ra từ một cấu trúc
dữ liệu, các nút con của nó được xem xét và bổ sung vào cấu trúc dữ liệu đó. Bằng cách
thao tác trên cấu trúc dữ liệu này, cây tìm kiếm được duyệt theo các thứ tự khác nhau,
chẳng hạn theo từng mức (tìm kiếm theo chiều rộng) hoặc đi tới một nút lá trước rồi quay
lui (tìm kiếm theo chiều sâu). Các ví dụ khác về tìm kiếm trên cây bao gồm: tìm kiếm lặp
sâu dần, tìm kiếm chiều sâu giới hạn, tìm kiếm hai chiều và tìm kiếm chi phí đều.
2.3. Tìm kiếm trên đồ thị.
Thuật toán tìm kiếm trên đồ thị là trường hợp mở rộng tổng quát của thuật toán tìm kiếm
trên cây. Đồ thị là một tập các đỉnh (hoặc nút) nối với nhau bởi các cạnh. Cạnh có thể có
hướng hoặc vô hướng. Đồ thị thường được vẽ dưới dạng một tập các điểm (các đỉnh nối
với nhau bằng các đoạn thẳng (các cạnh).Đồ thị biểu diễn được rất nhiều cấu trúc, nhiều
bài toán thực tế có thể được biểu diễn bằng đồ thị. Cấu trúc đồ thị có thể được mở rộng
bằng cách gán trọng số cho mỗi cạnh. Có thể sử dụng đồ thị có trọng số để biểu diễn

4


nhiều khái niệm khác nhau. Loại đồ thị này được gọi là đồ thị có hướng. Một đồ thị có
hướng với các cạnh có trọng số được gọi là một lưới.
Trong lý thuyết đồ thị các bài toán tìm kiếm phổ biến nhất có thể kể đến như bài toán tìm

cây khung nhỏ nhất, bài toán tìm đường đi ngắn nhất giữa hai đỉnh. Các thuật toán phổ
biến được sử dụng như thuật toán Prim, thuật toán Kruskal tìm cây khung nhỏ nhất, thuật
toán Dijktra tìm đường đi ngắn nhất.

3. Bài toán: Cho mảng X có n số nguyên. Hãy xây dựng thuật toán tìm số lớn thứ
hai trong mảng X. Đánh giá độ phức tạp của nó trong trường hợp xấu nhất.
Để có thể triển khai được thuật toán tìm số lớn thứ hai trong mảng, cần phải có dữ liệu
đầu vào. Dữ liệu đầu vào có thể là dữ liệu thực tế. Trong bài tập lớn này, dữ liệu đầu vào
được sinh ra tự động nhờ các thuật toán mô phỏng dữ liệu đầu vào bằng máy tính.
3.1. Thuật toán mô phỏng dữ liệu đầu vào
Dữ liệu đầu vào của bài toán là một mảng có n số nguyên. Mảng số này có thể có các
trường hợp sau:
Mảng số có trật tự
 Mảng tăng/không giảm.
 Mảng giảm/ không tăng.
• Mảng số ngẫu nhiên
 Số ngẫu nhiên có xác suất xuất hiện tuân theo một hàm phân bố nào đó.


Các thuật toán để mô phỏng dữ liệu đầu vào như sau:
a. Mảng có trật tự
Ý tưởng thuật toán: tạo một số ngẫu nhiên làm phần tử đầu tiên của dãy. Sinh ra các số
tiếp theo bằng cách cộng thêm vào số ngẫu nhiên ban đầu một giá trị ngẫu nhiên.
Đầu vào: Số phần tử n của dãy cần tạo
Đầu ra: Dãy tăng/không giảm có n phần tử.
Trình bày thuật toán bằng giả mã lệnh:
void generateIncreasingArray()
{
int a[1000];// ví dụ mảng có 1000 phần tử;
for (int i = 0;i<1000;i++)

5


if (i==0)
a[i] = (int)(5*(float)rand()/(float)(RAND_MAX);
else
a[i] = a[i-1]+ (int)(5*(float)rand()/(float)
(RAND_MAX);
}

b. Mảng ngẫu nhiên.
Trong trường hợp tổng quát, hay gặp nhất trong thực tế, mảng X chứa các số nguyên
ngẫu nhiên. Giá trị của các số nguyên ngẫu nhiên thường thoả mãn hai tiêu chí:


Nằm trong một khoảng giá trị nhất định. Tuy vẫn có trường hợp số ngẫu nhiên

tổng quát nằm trong khoảng [-∞,+∞], trường hợp phổ biến hơn cả là số ngẫu nhiên
nằm trong một khoảng giá trị nào đấy, ví dụ: [0,1000], [-100,+100].
• Có xác suất xuất hiện một giá trị tuân theo một hàm phân bố xác suất.
Ý tưởng thuật toán: Các bộ sinh số ngẫu nhiên trong máy tính thường là bộ sinh số ngẫu
nhiên có phân bố đều. Thuật toán sẽ sử dụng bộ sinh số ngẫu nhiên của máy tính để sinh
số ngẫu nhiên có phân bố đều. Sau đó áp dụng các phép biến đổi, các công thức toán học,
các phép lấy mẫu…để biến đổi số ngẫu nhiên có phân bố đều thành số ngẫu nhiên có
phân bố theo một hàm phân bố toán học mong muốn (phân bố Gauss, phân bố Poisson..).
Đầu vào:



Số phần tử n của dãy ngẫu nhiên.

Các giá trị đặc trưng của từng hàm phân bố (ví dụ giá trị trung bình và phương sai
của phân bố Gauss, giá trị trung bình của phân bố Poisson…). Để các dãy số đầu
vào cho thuật toán tìm kiếm có tính thống nhất, các tham số của hàm phân bố sẽ
được lựa chọn sao cho các số ngẫu nhiên sinh ra có giá trị tập trung trong khoảng
[0,1000]. Tham số được lựa chọn tương ứng cho các hàm phân bố là:
 Phân bố đều: mean = 500;
 Phân bố Gauss: mean = 500, standard deviation = 300
 Phân bố Poisson: mean = 500;
 Phân bố Exponential: rate = 500;

Đầu ra: Dãy ngẫu nhiên có n phần tử tuân theo phân bố xác suất.
Trình bày thuật toán bằng giả mã lệnh:
//Tạo số ngẫu nhiên theo phân bố chuẩn
int Uniform(float mean)
6


{

int R;
R = (int)((float)rand()/(float)(RAND_MAX));

return (int)(2*mean*R);
}
//Tạo số ngẫu nhiên theo phân bố Exponential
int Exponential(float mean)
{
int R;
R = (int)((float)rand()/(float)(RAND_MAX));
return (int)(-mean*log(R));

}
//Tạo số ngẫu nhiên theo phân bố Poisson.
int Poisson(float mean)
{
float R;
float sum = 0;
int i;
i=-1;
float z;
while(sum <=mean)
{
R = (float)rand()/(float)(RAND_MAX);
z = -log(R);
sum+= z;
i++;
}
return i;
}
//Tạo số ngẫu nhiên theo phân bố chuẩn/Gauss.
int Normal(float mean, float stdev)
{
int R1;
R1 = (int)((float)rand()/(float)(RAND_MAX));
int R2;
R2 = (int)((float)rand()/(float)(RAND_MAX));
return (int)(mean + stdev*cos(2*3.14*R1)*sqrt(-log(R2)));
}

Đồ thị hàm phân bố mật độ xác suất của mảng số nguyên ngẫu nhiên sinh ra từ các thuật
toán trên được cho trong hình 1 (với dãy ngẫu nhiên 100000 số).


7


Hình 1. Đồ thị phân bố mật độ xác suất xuất hiện số ngẫu nhiên sinh theo các hàm phân
bố đều, phân bố exponential, phân bố gauss, phân bố poisson (từ trên xuống dưới).

3.2. Các thuật toán tìm kiếm số lớn thứ hai trong mảng.
3.2.1. Tìm kiếm tuyến tính
Tìm kiếm tuyến tính (hay tìm kiếm tuần tự) là thuật toán tìm kiếm phổ biến nhất và gần
như là thuật toán tìm kiếm bắt buộc khi thực hiện việc tìm kiếm trên các dãy ngẫu nhiên
không có trật tự, hoặc các dãy không có khả năng truy cập vào phần tử bất kì. Thuật toán
tìm kiếm tuyến tính không yêu cầu mảng (dãy) dữ liệu đầu vào phải được sắp xếp hoặc
tiền xử lý.
Ý tưởng thuật toán.
Lần lượt so sánh phần tử cần tìm x với phần tử thứ nhất, thứ hai, thứ ba,… của dãy số cho
đến phần tử cuối cùng của dãy. Lưu lại địa chỉ (vị trí) các phần tử thỏa mãn (nếu tìm
được).
Đầu vào:




x: Phần tử cần tìm (phần tử lớn thứ hai)
n: Số phần tử của mảng (dãy) đầu vào.
A[n]: Mảng chứa các phần tử.

Đầu ra :



Phần tử lớn thứ hai trong dãy (nếu có).

Chi tiết thuật giải (giả mã lệnh)
void sequential_search(int n, array A)
{
int max = A[0];
int second_max = max;
for (int i=0;i{
if (max < A[i])
{
second_max = max;
max = A[i];
}
else if ((max > A[i])&&(second_max < A[i]))

n phép duyệt
1 phép so sánh
2 phép gán
1 phép so sánh
8


second_max = A[i];
else if (second_max == max)
second_max = A[i];

}
printf(“Second max = %d”,second_max);


}

1 phép gán
1 phép so sánh
1 phép gán

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


Cần tiến hành n bước duyệt. Với mỗi bước duyệt cần 1 phép so sánh trong trường



hợp tốt nhất và 3 phép so sánh liên tiếp trong trường hợp xấu nhất. Khi phép so
sánh trả về đúng, cần thực hiện 1 phép gán. Vậy trong trường hợp xấu, với mỗi lần
duyệt cần thực hiện 4 phép toán gồm 3 phép so sánh và 1 phép gán. Tổng số phép
toán trong trường hợp xấu nhất là 4n phép toán.
Do duyệt tuần tự, số lượng các phần tử thỏa mãn yêu cầu tìm kiếm có thể là bất kì,



do vậy trong hầu hết mọi trường hợp đều cần duyệt đến cuối danh sách, nên độ
phức tạp tính toán trung bình phần lớn bằng độ phức tạp tính toán trong trường
hợp xấu nhất.
Độ phức tạp tính toán O(n).

3.2.2. Sắp xếp mảng ngẫu nhiên và tìm kiếm duyệt từ một phía có điều kiện.
Thuật toán tìm kiếm số lớn thứ hai theo chỉ số dựa trên việc sử dụng các thuật toán sắp
xếp để đưa một mảng ngẫu nhiên về một mảng có trật tự. Công việc của thuật toán này
bao gồm hai bước: sắp xếp mảng ngẫu nhiên đã cho và tìm kiếm dựa trên chỉ số của phần

tử lớn thứ 2.
Ý tưởng thuật toán.
Đầu vào là mảng (dãy) Acó n phần tử đã sắp xếp có trật tự. Nếu dãy A là dãy tăng, phần
tử lớn thứ hai là phần tử A[n-2], nếu dãy A là dãy giảm, phần tử lớn thứ hai là phần tử
A[1]. Nếu dãy A là dãy không tăng hoặc không giảm, tức là sẽ có thể có các phần tử bằng
nhau, thì cần tiến hành duyệt dãy A để tìm ra tất cả các vị trí của phần tử lớn thứ hai.
Đầu vào:




x: Giá trị cần tìm.
n: Số phần tử của mảng.
A[n]: Mảng chứa các phần tử đã được sắp xếp có trật tự (tăng hoặc không giảm –
tương tự với trường hợp mảng giảm hoặc không tăng).
9


Đầu ra:


Vị trí chứa phần tử thỏa mãn điều kiện cần tìm (nếu có).

Chi tiết thuật giải (giả mã lệnh)
//Truong hop day tang
void index_search(n, array A) //Array A la day da sap xep tang
{
printf(“Second max = %d”,A[n-2]);
1 phép lấy dữ liệu
}

//Truong hop day khong giam
void index_search(n, array A) //Array A la day da sap xep khong giam
{
int max = A[n-1];
int second_max;
while ((max == A[n-1])&&(n>1))
n--;
tối đa n lần duyệt
second_max = A[n-1];
printf(“Second_max = %d”,second_max);
}

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


Với dãy tăng, có thể đơn giản xác định ngay phần tử lớn thứ hai là phần tử A[n-2].



Độ phức tạp tính toán là O(1).
Với dãy không giảm, do có thể có nhiều hơn một số lớn nhất, nên để tìm phần tử
lớn thứ hai cần duyệt từ cuối dãy để tìm ra phần tử lớn thứ hai. Sẽ có tối đa n lần
duyệt (ứng với mảng n phần tử giống hệt nhau). Độ phức tạp tính toán trong
trường hợp xấu nhất là O(n).

Độ phức tạp tính toán vào thời gian tìm kiếm của thuật toán tìm kiếm nhị phân nhỏ hơn
nhiều so với tìm kiếm tuần tự. Tuy nhiên, để đạt được độ phức tạp và thời gian tìm kiếm
này, thuật toán tím kiếm nhị phân chỉ làm việc trên các mảng đã được sắp xếp. Do vậy độ
phức tạp tính toán tổng quát của thuật toán tìm kiếm nhị phân còn gồm cả độ phức tạp
tính toán của thuật toán sắp xếp được lựa chọn để sắp xếp mảng dữ liệu đầu vào. Các

thuật toán sắp xếp phổ biến được chỉ ra trong bảng 1.
Thuật toán sắp xếp
Sắp xếp chọn

Độ phức tạp tính
toán trường hợp tốt
nhất

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

Độ phức tạp tính
toán trường hợp xấu
nhất

O(n2)

O(n2)

O(n2)
10


O(n2) phép so sánh,
đổi chỗ

O(n) phép so sánh,
O(1) phép đổi chỗ

O(n2) phép so sánh,

đổi chỗ

Sắp xếp nổi bọt

O(n)

O(n2)

O(n2)

Sắp xếp nhanh

O(nlogn)

O(nlogn)

O(n2)

Sắp xếp trộn

O(nlogn)

O(nlogn)

O(nlogn)

Sắp xếp vun đống

O(nlogn)


O(nlogn)

O(nlogn)

Sắp xếp chèn

Bảng 1. Độ phức tạp tính toán các thuật toán sắp xếp

Quá trình sắp xếp dữ liệu có thể được thực hiện ngay khi xây dựng dữ liệu hoặc khi
thêm/bớt dữ liệu. Quá trình này có thể tiến hành trước khi có yêu cầu tìm kiếm. Vì vậy
trong một số trường hợp dữ liệu đã hoàn thành sắp xếp vào một khoảng thời gian “rảnh”
trước khi có yêu cầu tìm kiếm, và thời gian sắp xếp không ảnh hưởng vào thời giam tìm
kiếm tổng cộng. Do vậy, khi lựa chọn thuật toán tím kiếm tuyến tính hay tìm kiếm nhị
phân, cần xác định rõ trường hợp áp dụng của bài toán, thời gian “rảnh” của bài toán để
xác định đúng thuật toán cho thời gian tìm kiếm nhanh hơn.
3.3. Các tình huống phức tạp
Các thuật toán máy tính được nghiên cứu để giải quyết một bài toán thực tế. Để tiến hành
giải quyết bài toán thực tế, các chương trình máy tính phải tiến hành mô phỏng điều kiện
thực tế vào trong bài toán. Đó là các cơ sở dữ liệu đầu vào, các điều kiện, yêu cầu của bài
toán. Các tình huống phức tạp trong các thuật toán xảy ra có thể chia thành hai trường
hợp chính


Điều kiện thực tế quá phức tạp để mô phỏng chính xác vào trong các cấu trúc dữ

liệu máy tính.
• Quá trình tính toán của giải thuật quá phức tạp khiến cho độ chính xác, độ tin cậy,
độ ổn định của giải thuật không đảm bảo.
Trong trường hợp thuật toán tìm kiếm trên mảng số nguyên, các tình huống phức tạp có
thể đoán nhận được và hướng giải quyết được chỉ ra trong bảng 2:

STT

Tình huống phức tạp

Hướng giải quyết

Ưu – nhược điểm

1

Dữ liệu đầu vào quá lớn, vượt quá - Không load hết - Thời gian tìm kiếm
khỏi khả năng quản lý bộ nhớ của toàn bộ dữ liệu vào sẽ tăng lên rất nhiều
hệ điều hành/ngôn ngữ lập bộ nhớ mà load từng do phải truy cập vào
11


trình/trình dịch. Ví dụ các cơ sở dữ
liệu về thông tin dân số, quy mô dữ
liệu có thể lên đến hàng chục, hàng
trăm triệu mẫu dữ liệu.

đoạn dữ liệu vào bộ
nhớ, phần dữ liệu
còn lại vẫn nằm trên
đĩa cứng, hoặc tiến
hành tìm kiếm trực
tiếp trên đĩa cứng.

đĩa cứng, vốn có tốc
độ truy cập chậm

hơn bộ nhớ trong.

Dữ liệu đầu vào có nhiều kiểu khác
nhau: có kiểu nguyên, kiểu thực…
thậm chí cả những kiểu dữ liệu
không có ý nghĩa do quá trình lấy
mẫu thực tế gặp vấn đề.

- Xây dựng hàm
kiểm tra dữ liệu để
đảm bảo độ “an
toàn” dữ liệu trước
khi đưa vào tìm
kiếm.

- Thời gian tìm kiếm
tổng quát sẽ tăng lên
do phải triển khai
hàm kiểm tra dữ
liệu.

- Cần xây dựng
thuật toán tổng quát,
có phương án xử lý
khi gặp các mẫu dữ
liệu nằm ngoài kiểu
dữ liệu định trước
của thuật toán.

2


3

- Hướng giải quyết
không khả thi trong
một số trường hợp
- Quản lý, cấp phát phải truy cập đến
bộ nhớ động (dùng toàn bộ dữ liệu một
con trỏ, malloc…)
cách gần như tức
thời, ví dụ trong các
thuật toán sắp xếp.

Thời gian tìm kiếm/sắp xếp quá
lâu, lên đến hàng giờ, ngày -> có
thể gặp các rủi ro như máy tính bị
crash, mất điện,etc..khiến cho toàn
bộ công việc đã thực hiện được bị
mất, phải tiến hành lại từ đầu…

- Xây dựng thuật
toán theo hướng có
thể “phân mảnh
hoá” quá trình tìm
kiếm.
- Lưu dữ liệu đầu ra
của bước tìm kiếm
trước để làm đầu
vào bổ sung của
bước tìm kiếm sau.


Bảng 2. Các hình huống phức tạp với thuật toán tìm kiếm trên mảng

12


4. Thử nghiệm và phân tích kết quả
4.1. Quy trình thử nghiệm
Sử dụng thuật toán mô phỏng dữ liệu đầu vào đã trình bày ở phần 3.1, tạo ra các dãy số
ngẫu nhiên theo phân bố đều, phân bố Exponential, phân bố Gauss, phân bố Poisson lần
lượt có 1000, 10 000, 100 000, 200 000, 500 000, 1 000 000, 2 000 000, 5 000 000 phần
tử.
Áp dụng thuật toán tìm kiếm tuần tự để tìm kiếm phần tử lớn thứ hai. Đo thời gian thực
hiện thuật toán bằng bộ đếm thời gian trong máy tính. So sánh kết quả thời gian thực hiện
thuật toán với các dãy có số lượng phần tử khác nhau để kiểm chứng công thức đánh giá
độ phức tạp thuật toán.
Áp dụng thuật toán sắp xếp và tìm kiếm theo chỉ số để tìm kiếm phần tử lớn thứ hai. Đo
thời gian thực hiện thuật toán bằng bộ đếm thời gian trong máy tính. So sánh kết quả thời
gian thực hiện thuật toán với các dãy có số lượng phần tử khác nhau để kiểm chứng công
thức đánh giá độ phức tạp thuật toán.
Các trường hợp phức tạp của thuật toán là các trường hợp số lượng phần tử của mảng đầu
vào rất lớn, lên đến hàng triệu phần tử.
4.2. Kết quả và phân tích kết quả
a. Thuật toán tìm kiếm tuần tự
Thời gian tìm kiếm phần tử lớn thứ hai trong mảng, sử dụng thuật toán tìm kiếm tuần tự
được trình bày trong bảng 3.
Đơn vị:
microsecond

1000


10000

100000

200000

500000

1M

2M

5M

Uniform

17

35

299

606

1553

3035

6498


15052

Exponential

9

67

372

744

1712

3158

6371

15125

Gauss

16

83

762

1139


1642

3025

6103

14999

Poisson

8

53

53

643

1496

3021

6094

15044

Bảng 3. Thời gian tìm kiếm sử dụng thuật toán tìm kiếm tuần tự

Từ đồ thị biểu diễn thời gian tìm kiếm phần tử lớn thứ hai trong mảng theo số lượng phần

tử của mảng, ta có thể thấy, thời gian tìm kiếm phần tử lớn thứ hai trong mảng biến đổi

13


gần tuyến tính với số lượng phần tử của mảng. Điều này phù hợp với công thức đánh giá
độ phức tạp thuật toán tìm kiếm tuần tự là O(n).
Đối với các bài toán sử dụng thuật toán tìm kiếm tuần tự, độ ổn định của thuật toán là khá
tốt. Thuật toán chỉ bị giới hạn độ phức tạp khi xảy ra các trường hợp số lượng mẫu dữ
liệu quá lớn dẫn đến tràn bộ nhớ, chương trình máy tính không quản lý được bộ nhớ.
b. Sắp xếp mảng và tìm kiếm theo chỉ số.
Thời gian sắp xếp mảng đầu vào thành một mảng không giảm và tìm kiếm phần tử lớn
thứ hai trong mảng được trình bày trong bảng 4 và bảng 5.
Thuật toán sắp xếp nhanh:
Đơn vị:
microsecond

1000

10000

100000

200000

500000

1M

2M


Uniform

123

1327

25684

76026

391319

1371710

5366560

Exponential

156

1532

46070

152765

834429

3260902


13019054

Gauss

140

2074

29826

91976

477538

1799388

6939577

Poisson

117

2682

176995

286348

1654354


4321954

12543546

Bảng 4. Thời gian tìm kiếm sử dụng thuật toán sắp xếp nhanh

Từ đồ thị biểu diễn thời gian sắp xếp mảng và tìm kiếm phần tử lớn thứ hai trong mảng
sử dụng thuật toán sắp xếp nhanh, ta thấy:
Thời gian tính toán của thuật toán khá tương đồng với công thức Độ phức tạp tính
toán trung bình của thuật toán là O(nlogn).
• Thuật toán sắp xếp nhanh hơn trên các mô hình dữ liệu ngẫu nhiên có phân bố
đều và phân bố Gauss, chậm hơn trên các mô hình dữ liệu ngẫu nhiên có phân bố
Exponential và phân bố Poisson, đặc biệt là khi số lượng mẫu dữ liệu trở nên rất
lớn.


Đơn vị:
microsecond

1000

10000

100000

200000

500000


1M

2M

Uniform

210

2316

56922

114320

291026

583014

1187470

Exponential

218

2364

59613

117333


293489

594197

1178007

Gauss

257

3387

56779

119087

290577

592123

1188633

14


Poisson

218

2212


54820

110361

282312

568818

1146574

Bảng 5. Thời gian tìm kiếm sử dụng thuật toán sắp xếp trộn

Từ đồ thị biểu diễn thời gian sắp xếp mảng và tìm kiếm phần tử lớn thứ hai trong mảng
sử dụng thuật toán sắp xếp trộn, ta thấy:
Thời gian tính toán của thuật toán có dạng giống với công thức Độ phức tạp tính
toán trung bình của thuật toán là O(nlogn).
• Thuật toán sắp xếp trộncó thời gian tính toán khá tương đồng nhau đối với các
mảng đầu vào ngẫu nhiên có phân bố khác nhau. Do đó, thuật toán sắp xếp trộn
có ưu điểm về độ đồng nhất thời gian tính toán với nhiều mô hình dữ liệu khác
nhau.
• Thuật toán sắp xếp trộn có thời gian tính toán chậm hơn thuật toán sắp xếp nhanh
trong trường hợp số lượng mẫu là nhỏ (ít hơn 100000 mẫu) và nhanh hơn thuật
toán sắp xếp nhanh trong trường hợp số lượng mẫu là lớn đến rất lớn (từ trên
100000 mẫu đến vài triệu mẫu).


5. Kết luận
Bài tập lớn đã xây dựng và trình bày 2 thuật toán tìm kiếm chính để giải quyết bài toán
tìm phần tử lớn thứ hai trong mảng số nguyên n phần tử.

• Thuật toán tìm kiếm tuần tự. Độ phức tạp trong trường hợp xấu nhất: O(n).
• Thuật toán tìm kiếm duyệt từ một phía có điều kiện của mảng đã sắp xếp. Độ phức
tạp của thuật toán phụ thuộc vào độ phức tạp của thuật toán sắp xếp. Sử dụng thuật
toán sắp xếp nhanh, độ phức tạp trong trường hợp xấu nhất là O(n2), sử dụng thuật
toán sắp xếp trộn, độ phức tạp trong trường hợp xấu nhất là O(nlogn).
Các kết luận rút ra từ bài tập lớn.
• Kiểm chứng được các công thức đánh giá độ phức tạp của hai thuật toán nói trên
bằng thử nghiệm thực tế.
• Thuật toán tìm kiếm tuần tự cho thời gian tìm kiếm phần tử lớn thứ hai nhanh hơn
so với thuật toán yêu cầu sắp xếp.
• Nếu có thể tiến hành thuật toán sắp xếp vào khoảng thời gian “rảnh” trước khi có
yêu cầu tìm kiếm, thì thời gian tìm kiếm trên mảng đã sắp xếp nhanh hơn nhiều so
với tìm kiếm tuần tự.
• Thuật toán sắp xếp trộn chậm hơn thuật toán sắp xếp nhanh với các mảng có ít
phần tử (từ 100000 phần tử trở xuống), và nhanh hơn đáng kể thuật toán sắp xếp
15


nhanh với các mảng có từ nhiều đến rất nhiều phần tử (từ vài trăm nghìn đến vài
triệu phần tử).
• Các tình huống phức tạp có thể xảy ra khi tính toán là: khối lượng dữ liệu vào quá
lớn, gây ra các sự cố về tràn bộ nhớ, cấp phát bộ nhớ. Chương trình giải quyết tình
huống phức tạp này bằng cách áp dụng các kỹ thuật cấp phát bộ nhớ động, ghi dữ
liệu ra file và đọc tuần tự từng phần vào bộ nhớ.
Nhóm tác giả thực hiện bài tập lớn xin chân thành cảm ơn PGS.TS. Đào Thanh Tĩnh đã
tận tình giảng dạy và hướng dẫn nhóm tác giả trong suốt quá trình nghiên cứu môn học
Phân tích đánh giá thuật toán cũng như quá trình nghiên cứu, thực hiện bài tập lớn.

16




×