KHOA CÔNG NGHỆ THÔNG TIN
BÀI TẬP LỚN MÔN HỌC
PHÂN TÍCH ĐÁNH GIÁ THUẬT TOÁN
Đề 27: Thuật toán tìm kiếm. Một xâu gọi là xâu đối xứng nếu đem đảo ngược xâu
đó ta lại nhận được xâu ban đầu. Cho xâu S, hãy tìm số kí tự ít nhất cần thêm
vào S để S trở thành xâu đối xứng.Giả thiết các thao tác chèn kí tự tốn kém thời
gian không đáng kể.
Giáo viên hướng dẫn: PGS.TS. Đào Thanh Tĩnh
Học viên thực hiện: Hồ Trung Dũng
Lớp: Hệ thống thông tin K25B
Hà nội, 5/2014
PHẦN 1: THUẬT TOÁN TÌM KIẾM
I. Bài toán tìm kiếm
Tìm kiếm luôn là thao tác nền móng cho rất nhiều tác vụ tính toán. Tìm kiếm
nghĩa là tìm một hay nhiều mẩu thông tin đã được lưu trữ. Thông thường, thông tin
được chia thành các mẩu tin (record), mỗi mẩu tin đều có một khóa (key) dùng cho
việc tìm kiếm. Ta sẽ luôn có một khoá cho trước giống như khoá của các mẩu tin mà
ta cần tìm. Mỗi mẩu tin được tìm thấy sẽ chứa toàn bộ thông tin để cung cấp cho một
quá trình xử lý nào đó.
Việc tìm kiếm được áp dụng rất đa dạng và rộng rãi. Một Ngân hàng nắm giữ tất
cả thông tin của rất nhiều tài khoản khách hàng và cần tìm kiếm để kiểm tra các biến
động. Một hãng Bảo hiểm hay một hệ thống trợ giúp bán vé xe, vé máy bay….Việc
tìm kiếm thông tin để đáp ứng việc xắp đặt ghế và các yêu cầu tương tự như vậy là
thực sự cần thiết. Bài toán tìm kiếm thông thường xảy ra trong hai tình huống:
+ Dữ liệu được coi là ngẫu nhiên
+ Dữ liệu thỏa mãn một số ràng buộc nhất định
II. Tìm kiếm tuần tự và tìm kiếm nhị phân:
1. Tìm kiếm tuần tự:
a. Phạm vi áp dụng: Thường được sử dụng cho dữ liệu không được săp xếp, không có
ràng buộc giữa các phần tử
b. Ý tưởng: Duyệt lần lượt các phần tử từ phần tử thứ nhất đến phần tử thứ N. Nếu xuất
hiện phần tử thỏa mãn yêu cầu thì ghi nhận lại vị trí của phần tử đó. Thông tin trả về
dựa theo tình trạng tìm thấy.
Đây là một trong những phương pháp tìm kiếm đơn giản dễ thực hiện đối với các
thông tin được lưu trữ kiểu mảng.
2
2
c. Thuật toán:
• Input: Cho dãy A[0..N-1], x là giá trị cần tìm kiếm
• Output: Thông tin về vị trí của X
for(i=0; i< N;i++)
if(a[i]==x)
break;
return i;
Từ thuật toán trên ta thấy trong trường hợp xấu nhất là không tìm kiếm thành
công thuật toán sử dụng N+ 1 phép so sánh với N là số phần tử trong mảng A vì vậy
độ phức tạp của thuật toán là O(N)
2. Tìm kiếm nhị phân:
Chúng ta đã xét phương pháp tìm kiếm tuần tự, cách này đơn giản trong quá trình
cài đặt. Song , hạn chế của phương pháp tuần tự là thời gian tìm kiếm sẽ lâu trong
trường hợp tập hợp tổng số mẩu tin lớn. Để khắc phục hạn chế này, ta có phương pháp
tìm kiếm nhị phân. Nếu tập hợp các mẩu tin lớn thì tổng số thời gian tìm kiếm sẽ được
rút ngắn bằng cách dùng một thủ tục tìm kiếm dựa trên sự ứng dụng sơ đồ “chia - để trị”.
a.
b.
-
Phạm vi áp dụng: Tìm kiếm trên dữ liệu đã được sắp xếp.
Ý tưởng: Thay vì tìm kiếm tuần tự ta sé tìm kiếm phần tử theo vị trí ở giữa dãy.
Nếu chỉ số phải bé hơn chỉ số trái thì kết thúc.
Nếu giá trị tìm kiếm trùng với giá trị ở giữa thì kết thúc
Nếu bé hơn thì tìm kiếm từ đầu đến phần tử trước phần tử hiển tại
Nếu lớn hơn phần tử ở giữa thì tìm kiếm từ phần tử sau phần tử ở giữa đến phần tử ở
cuối dãy.
c. Thuật toán:
Input: A[0..N-1], x cần tìm kiếm
Output: thông tin về vị trí
"int" BinarySearch (Array a, int x) {
int left = 0, right, mid; right = a.n - 1;
3
3
while (left <= right) {
mid = (left + right)/2;
if (a.A[mid] > x) right = mid - 1;
else left = mid + 1;
};
if (left == 0)
printf ("Ko tim thay phan tu %d \n", x);
return left;
-
Dựa vào thuật toán trên ta thấy :
Số phép so sánh của thuật toán là 2* log(n)
Số phép gán của thuật toán là 2* log(n)
Độ phức tạp của thuật toán là O(log(n))
III. Một số cách tiếp cận khác:
1. Tìm kiếm dựa vào quy hoạch động
Phương pháp quy hoạch động cùng nguyên lý tối ưu được nhà toán học Mỹ
R.Bellman đề xuất vào những năm 50 của thế kỷ 20. Phương pháp này đã được áp
dụng để giải hàng loạt bài toán thực tế trong các quá trình kỹ thuật cộng nghệ, tổ chức
sản xuất, kế hoạch hoá kinh tế…Nguyên lý tối ưu của R.Bellmam được phát biểu như
sau: “tối ưu bước thứ n bằng cách tối ưu tất cả con đường tiến đến bước n-1 và chọn
con đường có tổng chi phí từ bước 1 đến bước n-1 và từ n-1 đến n là thấp nhất (nhiều
nhất)”.
Nói chung, ta có thể giải một bài toán với cấu trúc con tối ưu bằng một quy trình
ba bước:
a. Chia bài toán thành các bài toán con nhỏ hơn
b. Giải các bài toán này một cách tối ưu bằng cách sử dụng đệ quy quy trình ba bước này
c. Sử dụng các kết quả tối ưu đó để xây dựng một lời giải tối ưu cho bài toán ban đầu
Phương pháp quy hoạch động dùng để giải bài toán tối ưu có bản chất đệ quy, tức
là việc tìm phương án tối ưu cho bài toán đó có thể đưa về tìm phương án tối ưu của
một số hữu hạn các bài toán con. Đối với nhiều thuật toán đệ quy chúng ta đã tìm
hiểu, nguyên lý chia để trị (divide and conquer) thường đóng vai trò chủ đạo trong
việc thiết kế thuật toán. Để giải quyết một bài toán lớn, ta chia nó làm nhiều bài toán
con cùng dạng với nó để có thể giải quyết độc lập.
Quy hoạch động thường dùng một trong hai cách tiếp cận:
4
4
top-down (Từ trên xuống): Bài toán được chia thành các bài toán con, các bài
•
toán con này được giải và lời giải được ghi nhớ để phòng trường hợp cần dùng lại
chúng. Đây là đệ quy và lưu trữ được kết hợp với nhau.
bottom-up (Từ dưới lên): Tất cả các bài toán con có thể cần đến đều được giải
•
trước, sau đó được dùng để xây dựng lời giải cho các bài toán lớn hơn. Cách tiếp cận
này hơi tốt hơn về không gian bộ nhớ dùng cho ngăn xếp và số lời gọi hàm. Tuy
nhiên, đôi khi việc xác định tất cả các bài toán con cần thiết cho việc giải quyết bài
toán cho trước không được trực giác lắm.
Một số bài toán ứng dụng phương pháp quy hoạch động như:
+ Bài toán tính số tổ hợp
+ Bài toán cái balo
+ Bài toán tìm đường đi của người giao hàng
2. Tìm kiếm dựa vào đệ quy
Một đối tượng là đệ qui nếu nó bao gồm chính nó như một bộ phận hay nói một
cách khác là nó được định nghĩa qua chính nó.Lời giải T của bài toán được biểu diễn
qua lời giải T’ có dạng giống như T gọi là lời giải đệ qui.Giải thuật tương ứng với lời
giải đệ qui là giải thuật đệ qui.
Thủ tục đệ qui là thủ tục (bao gồm cả thủ tục (Procedure) và hàm (Function)) là
thủ tục mà trong thân của nó lại chứa lời gọi đến chính nó.
Tính chất của thủ tục đệ qui.
a. Trong thủ tục có lời gọi chính nó.
b.
Có một trường hợp đặc biệt để kết thúc việc nó gọi nó gọi là trường hợp
suy biến.
c.
Mỗi lần gọi lại dần đến trường hợp suy biến.
Người ta thường ứng dụng đệ qui vào giải rất nhiền bài toán, tuy vậy:
5
5
- Có những bài toán sử dụng đệ quy rất có hiệu quả
- Khi sử dụng đệ quy, nhiều thuật toán thường gây ra hiện tượng tràn ô nhớ
- Có thể thay giải thuật đệ quy bằng giải thuật không đệ quy. Việc làm này được
gọi là khử đệ quy.
Đệ quy được ứng dụng rất nhiều trong bài toán tìm kiếm. Tìm kiếm trong không
gian trạng thái là một quá trình đệ quy. Để tìm đường đi từ trạng thái hiện hành đến
đích, bạn chuyển đến một trạng thái con và thực hiện phép đệ quy. Nếu trạng thái con
đó không dẫn đến đích, bạn thử lần lượt các trạng thái anh em của nó. Phép đệ quy sẽ
chia một bài toán lớn và khó (tìm kiếm khắp không gian) thành các bài toán nhỏ và
đơn giản hơn (phát sinh các con của một trạng thái) và áp dụng chiến lược đệ quy cho
từng bài toán nhỏ đó. Quá trình cứ tiếp tục như vậy cho đến khi phát hiện được đích
hoặc hết không gian.
Function Depthsearch;
Begin
If open rỗng then trả lời (thất bại);
Trạng thái hiện hành := phần tử đầu tiên của open;
If trạng thái hiện hành là trạng thái đích
then trả lời (thành công)
Else
begin
Open := open - phần tử đầu tiên của open;
Closed := closed + trạng thái hiện hành;
For mỗi trạng thái con của trạng thái hiện hành do If chưa có
trong closed hay open
then bổ sung con đó vào đầu danh sách open
end;
Tìm kiếm sâu;
6
6
End;
PHẦN 2: ỨNG DỤNG THUẬT TOÁN TÌM KIẾM
CHO XÂU S, HÃY TÌM SỐ KÝ TỰ ÍT NHẤT CẦN THÊM
VÀO S ĐỂ S TRỞ THÀNH XÂU ĐỐI XỨNG
I.
Đặt vấn đề và các cách giải quyết bài toán
7
7
Một xâu được gọi là đối xứng nếu nó không ít hơn một ký tự và nếu ta đọc từ trái sang
phải hay từ phải sang trái đều được kết quả giống nhau.
1. Sử dụng quy đệ quy
Gọi L(i,j) là số kí tự ít nhất cần thêm vào xâu con S[i..j] của S để xâu đó trở thành
đối xứng. Đáp số của bài toán sẽ là L(1,n) với n là số kí tự của S. Ta có công thức sau
để tính L(i,j):
• L(i,i)=0.
• L(i,j)=L(i+1,j–1) nếu S[i]=S[j]
• L(i,j)=max(L(i+1,j), L(i,j–1)) nếu S[i]≠S[j]
Thật vây:
Xét L(i,i)=0 với i chạy từ 1 -> Len(chuỗi) (vì chuỗi 1 kí tự đã đc xem là chuỗi đối
xứng rồi nên ko cần thêm kí tự nào nữa hết).
Công thức truy hồi tìm L(i,j):
+ Nếu kí tự thứ i = kí tự thứ j thì số kí tự cần thêm vào của chuỗi từ i đến j = số kí
tự cần thêm vào của chuỗi từ i+1 đến j-1. Ta có:
L(i,j)=L(i+1,j-1)
+ Nếu 2 kí tự khác nhau thì ta có thể:
- Thêm kí tự i vào bên phải kí tự j, như vậy thì ta có kí tự thứ i = kí tự thứ j+1,
và đưa về như trường hợp đầu, ta có:
L(i,j) = L(i+1,j)+1 ( +1 vào là do có 1 kí tự vừa được thêm vào)
- Thêm kí tự j vào bên trái kí tự i, như vậy thì ta có kí tự thứ i - 1 = kí tự thứ j,
và đưa về như trường hợp đầu, ta có:
L(i,j)=L(i,j-1)+1 ( +1 vào là do có 1 kí tự vừa được thêm vào)
Vậy ta có công thức truy hồi trong trường hợp 2 kí tự khác nhau là:
L(i,j)=max(L(i+1,j), L(i,j-1)) + 1 (với hàm max(a,b) sẽ trả về giá trị lớn hơn
trong 2 giá trị a,b)
Với cách giải thuật sử dụng đệ quy có nhớ trên chi phí sẽ la O().
2. Sử dụng quy hoạch động
Một cách tiếp cận khác của bài toán là sử dụng quy hoạch động để thực hiện.
Thông qua việc tìm xâu con chung dài nhất
Nếu ta gọi P là xâu đảo của S
8
8
T là xâu con chung dài nhất của S và P
Khi đó các ký tự của S không thuộc T cũng là các ký tự cần thêm vào để S trở
thành xâu đối xứng.
Vậy số ký tự ít nhất cần thêm vào sẽ là n-k với k là đọ dài của T
Ví dụ: Cho xâu S:edbabcd - >Xâu đảo của S sẽ là : dcbabde
Xâu con chung xài nhất: T: dbabd
Vậy e và c sẽ là 2 ký tự cần thêm vào để S thành xâu đối xứng.
Với việc sử dụng quy hoạch động để giải quyết bài toán, chúng ta phải giải quyết
bài toán con – tìm xâu con chung dài nhất. Khi đó bài toán sẽ đưa về giải quyết vấn
đề:
Cho hai dãy X = <x1,x2,…,xm> và Y = <y1,y2,…,yn>. Cần tìm dãy con chung dài
nhất của hai dãy X và Y.Thuật toán trực tiếp để giải bài toán đặt ra là: Duy ệt tất
cả các dãy con của dãy X và kiểm tra xem mỗi dãy như vậy có là dãy con của dãy
Y, và giữ lại dãy con dài nhất. Mỗi dãy con của X tương ứng với dãy chỉ số
…, ik> là tập con k phần tử của tập chỉ số {1, 2, …, m}, vì thế có tất cả 2m dãy con
của X.Như vậy thuật toán trực tiếp đòi hỏi thời gian hàm mũ và không thể ứng
dụng được trên thực tế.
Ta xét áp dụng quy hoạch động để xây dựng thuật toán giải bài toán này.
Phân rã . Với mỗi 0 ≤i≤m và 0 ≤j≤n xét bài toán C(i,j); tính C[i,j] là độ dài của
dãy con chung dài nhất của hai dãy.
Xi=<x1,x2,…,xi>
và
Yi=<y1,y2,…yi>
Như vậy ta đã phân bài toán cần giải ra thành (m+1)(n+1) bài toán con. Bản thân
bài toán xuất phát là bài toán con có kích thước lớn nhất C(m,n).Dễ dàng đánh giá
được thời gian tính của thuật toán là O(mn).
9
9
II.
Giải thuật
Input : xâu S (doixung.in)
Output : Số ký tự ít nhất thêm vào S để S đối xứng (doixung.out)
Ta sẽ có thuật toán như sau:
B1 : Ta gọi P là dãy đảo ngược của dãy S
B2: Áp dụng Quy hoạch động,tìm dãy con chung của S và P (ta g ọi là T)
B3 : Đặt n = length(s); m= length(t); đáp án bài toán là n-m
var
f:text;
s1,s2:string;
n,m,i,j:integer;
d:array[0..1000,0..1000] of integer;
function max(a,b:integer):integer;
begin
if a>b then exit(a);
exit(b);
end;
begin
assign(f,'doixung.in'); reset(f); readln(f,s1); close(f);
s2:='';
for i:=length(s1) downto 1 do (* làm 1 chuỗi đảo ngược của chuỗi đã cho ban đầu*)
s2:=s2+s1[i];
n:=length(s1); m:=length(s2);
for i:=1 to n do d[i,0]:=0;
for j:=1 to m do d[0,j]:=0;
for i:=1 to n do
for j:=1 to n do
if s1[i]=s2[j] then
d[i,j]:=d[i,j-1]+1
else
d[i,j]:=max(d[i,j-1],d[i-1,j]);
assign(f,’doixung.out'); rewrite(f); writeln(f,n-d[n,m]);
10
10
close(f);
end
III.
Đánh giá độ phức tạp và kiểm thử
1. Đánh giá độ phức tạp của thuật toán
Phương pháp tốt nhất để giải quyết bài toán vấn là tìm xâu con chung dài nhất của
xâu S đưa vào và xâu đảo của nó. Việc tím kiếm xâu con chung dài nhất có thể thực
hiện bằng quy hoạch động. Khi đó một bảng gồm 3 trường có thể được xây dựng,
trong đó có 2 trường được dùng để lưu trữ. Sự phức tạp của bài toán sẽ cần O(N)
không gian lưu trữ và O() thời gian.
2. Kiểm thử
Thực hiện kiểm thử một số tình huống khi chạy thực tế cho kết quả sau:
Case # N D A Loại dữ liệu
------ ---- -- ---- -----------1
62 62 61 Each allowed character exactly once
2 4960 62 4801 80 repetitions of case #1
3 5000 1 0 '9'^5000
4 5000 2 2500'A'^2500 ++ 'z'^2500
5 5000 2 1 'PC'^2500
6 100 48 79 Random
7
3 2 1 'FFT'
8 5000 2 919 Random with only characters 'O' and 'K'
9 4999 10 2628 Random with only digits
Với: N là chiều dài của chuỗi đưa vào
D là số phần từ khác nhau trong chuỗi
A là kết quả thu được
11
11