Tải bản đầy đủ (.doc) (25 trang)

giải quyết đụng độ trong phép biến đổi khoá băm bằng phương pháp địa chỉ 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 (265.61 KB, 25 trang )

HỌC VIỆN KỸ THUẬT QUÂN SỰ
KHOA CÔNG NGHỆ THÔNG TIN
o0o

BÀI TẬP MÔN HỌC
PHÂN TÍCH THIẾT KẾ THUẬT TOÁN
Đề tài : GIẢI QUYẾT ĐỤNG ĐỘ TRONG PHÉP BIẾN ĐỔI
KHOÁ BĂM BẰNG PHƯƠNG PHÁP ĐỊA CHỈ MỞ
GV hướng dẫn: Đại tá - TS ĐÀO THANH TĨNH
Người thực hiện: Thiếu tá - KS PHẠM HỒNG SƠN
Lớp : CH CNTT K17 - Học viện KTQS
Hà Nội, tháng 02 năm 2006
2
MỤC LỤC
Trang
Mục lục 2
Đặt vấn đề 3
Phần 1 Giải quyết đụng độ trong phép biến đổi khoá băm
bằng phương pháp địa chỉ mở
5
1.1. Mô tả chung 5
1.2. Phương pháp thăm dò tuyến tính (Linear Probing) 6
1.3. Phương pháp thăm dò bậc hai (Quadrratic Probing) 10
1.4. Phương pháp thăm dò kép (Double Hashing) 13
Phần 2 Phân tích phép biến đổi khoá
bằng phương pháp địa chỉ mở
16
3.1. Phân tích phép biến đổi khoá 16
3.2. Một số lưu ý khi sử dụng phương pháp thử 18
Phần 3 Chương trình minh hoạ 19
Tài liệu tham khảo 23


3
BÀI TẬP
Môn học: Phân tích và thiết kế thuật toán
Người thực hiện: PHẠM HỒNG SƠN
Lớp: CH CNTT K17 - Học viện KTQS
GIẢI QUYẾT ĐỤNG ĐỘ TRONG PHÉP BIẾN ĐỔI KHOÁ
BẰNG PHƯƠNG PHÁP ĐỊA CHỈ MỞ
ĐẶT VẤN ĐỀ
Các phép toán trên các cấu trúc dữ liệu như danh sách, cây nhị phân,… phần
lớn được thực hiện bằng cách so sánh các phần tử của cấu trúc, do vậy thời gian
truy xuất không nhanh và phụ thuộc vào kích thước của cấu trúc.
Phép băm được đề xuất và hiện thực trên máy tính từ những năm 50 của thế
kỷ 20. Nó dựa trên ý tưởng: biến đổi giá trị khóa thành một số (xử lý băm) và sử
dụng số này để đánh lại chỉ cho bảng dữ liệu.
Nhiều ứng dụng yêu cầu một tập hợp động chỉ hỗ trợ các phép toán từ điển
như INSERT, SEARCH, DELETE. Các phép toán này trên bảng băm sẽ giúp
hạn chế số lần so sánh, và vì vậy sẽ cố gắng giảm thiểu được thời gian truy xuất.
Độ phức tạp của các phép toán trên bảng băm trong trường hợp tối ưu thường có
bậc là O(1) và không phụ thuộc vào kích thước của bảng băm.
Thông thường bảng băm được sử dụng khi cần xử lý các bài toán có dữ liệu
lớn và được lưu trữ ở bộ nhớ ngoài.
Ta có thể mô ta cấu trúc của một bảng băm tổng quát như sau:
Tập khóa K Hàm băm h(k) Tập địa chỉ M
Trong đó:
- K: tập các khoá
- M: tập các địa chỉ
- h(k): hàm băm dùng để ánh xạ một khoá k từ tập các khoá K thành một
địa chỉ tương ứng trong tập M.
4
Với mỗi loại bảng băm cần thiết phải xác định tập khóa K, xác định tập địa

chỉ M và xây dựng hàm băm h cho phù hợp.
Quá trình biến đổi từ tập khoá K ra tập địa chỉ M tương ứng gọi là phép biến
đổi khoá. Phép biến đổi khoá được thực hiện qua hai bước:
- Bước thứ nhất của phép biến đổi khoá là tính toán hàm h(k) để biến đổi tập
khoá K thành tập địa chỉ M trong bảng. Trường hợp lý tưởng là những khoá
khác nhau thông qua hàm h(k) sẽ cho những địa chỉ khác nhau tương ứng trong
bảng. Nhưng trong thực tế thì hai hoặc nhiều khoá khác nhau sau khi qua phép
biến đổi khoá h(k) lại cho cùng một địa chỉ trong bảng.
- Bước thứ hai của phép biến đổi khoá là quá trình giải quyết sự đụng độ cho
những khoá khác nhau nhưng có cùng một địa chỉ trong bảng.
Phép biến đổi khoá tốt là phải đảm bảo giải quyết hợp lý về thời gian và bộ
nhớ. Nếu không bị giới hạn về bộ nhớ thì có thể tìm kiếm một khóa bất kì với
một lần truy xuất bộ nhớ bằng cách cho khoá đó chính là địa chỉ của bộ nhớ.
Ngược lại nếu không bị giới hạn về thời gian tìm kiếm thì ta có thể sử dụng một
bộ nhớ có kích thước tối thiểu với phương pháp tìm kiếm tuần tự.
Có nhiều phương pháp giải quyết đụng độ. Một trong những cách giải quyết
đó là dùng danh sách liên kết bởi ta không thể biết trước số các khoá khác nhau
có cùng địa chỉ trong bảng là bao nhiêu. Một cách giải quyết khác với thời gian
nhanh hơn là dùng danh sách có kích thước cố định.
Trong bảng băm với phương pháp kết nối trực tiếp mỗi địa chỉ của bảng băm
tương ứng một danh sách liên kết. Các phần tử bị xung đột được kết nối với
nhau trên một danh sách liên kết.
Phương pháp kết nối trực tiếp có một nhược điểm là phải duy trì các danh
sách liên kết và mỗi phần tử phải có thêm vùng liên kết để chỉ đến phần tử kế
tiếp trong danh sách.
Một cách khác để giải quyết đụng độ là khi có đụng độ xảy ra thì ta sẽ tìm
đến địa chỉ kế tiếp nào đó trong bảng cho đến khi tìm thấy phần tử mong muốn
hoặc vị trí kế tiếp là vị trí trống (không thấy). Do đó phương pháp này được gọi
là phương pháp địa chỉ mở. Dãy các chỉ số của bước thứ 2 (để xác định vị trí kế
tiếp) phải luôn luôn như nhau đối với mỗi khoá cho trước.

Trong phạm vi của đề tài, sau đây là những phân tích, đánh giá các kỹ thuật
(phương pháp) tạo địa chỉ mở trong phép biến đổi khoá.
Nội dung đề tài gồm 3 phần chính:
- Phần 1: mô tả các cách biến đổi khoá băm bằng phương pháp địa chỉ mở.
- Phần 2: nêu lên những phân tích, đánh giá của phương pháp địa chỉ mở
- Phần 3: Chương trình minh hoạ
Phần 1
5
GIẢI QUYẾT ĐỤNG ĐỘ TRONG PHÉP BIẾN ĐỔI KHOÁ BĂM
BẰNG PHƯƠNG PHÁP ĐỊA CHỈ MỞ
1.1. Mô tả chung
Bảng băm sử dụng phương pháp địa chỉ mở sẽ lưu giữ các phần tử ngay trong
bảng chứ không dùng mảng làm các chỉ điểm đầu. Ô thứ i của bảng chứa phần
tử có giá trị băm là i. Nhưng có thể có nhiều phần tử có cùng giá trị băm nên ta
thường gặp trường hợp ta muốn đưa vào ô thứ i một phần tử nhưng ô này đã bị
chiếm bởi phần tử j nào đó. Như vậy, khi thiết kế một bảng địa chỉ mở ta phải có
cách để giải quyết sự đụng độ này.
Trong phương pháp địa chỉ mở ta gọi m là số phần tử của bảng băm và n là
số phần tử đã sử dụng. Bảng băm được gọi là đầy khi n=m-1. Như vậy bảng
băm bao giờ cũng phải có ít nhất 1 phần tử trống.
Khi tìm kiếm 1 phần tử trong bảng băm, có 3 trường hợp có thể xảy ra đối
với vị trí kế tiếp là:
1. Nếu phần tử tại vị trí này là phần tử cần phải tìm thì giải thuật kết thúc
thành công (tìm thấy).
2. Nếu phần tử tại vị trí này là vị trí trống thì giải thuật kết thúc không thành
công (không tìm thấy).
3. Nếu phần tử tại vị trí này không phải là vị trí cần tìm thì ta tiếp tục xét vị
trí kế tiếp.
Để có thể nhận biết được các vị trí trống của bảng băm ta cho khoá của các
phần tử tại các vị trí này là một giá trị đặc biệt free, chẳng hạn như 1 số nguyên

tố lớn nhất.
Một cách tổng quát, giải thuật tìm kiếm khoá k trong phương pháp địa chỉ mở
có thể mô tả như sau:
x:= h(k);
i:= 0;
While (T[x].key <>free) and (T[x].key <> k ) do
begin
x:= (h(k) + G(i)) mod m ;
i:= i + 1;
end;
if T[x].key = k then t×m thÊy else kh«ng t×m thÊy“ ” “ ”
Trong đó:
6
- h(k): hàm băm biếm đổi khoá k thành vị trí trong bảng băm
- G(i): hàm tạo ra dãy các chỉ số của phép thăm dò thứ 2
Thông thường có ba kỹ thuật được dùng để tính toán các dãy của phép thăm
dò thứ hai cho phương pháp định địa chỉ mở: Phương pháp thăm dò tuyến tính,
phương pháp thăm dò bậc hai và Phương pháp thăm dò kép.
Sau đây ta lần lượt xem xét các phương pháp thăm dò trên.
1.2. Phương pháp thăm dò tuyến tính (Linear Probing)
1.2.1. Mô tả phương pháp
Một phương pháp địa chỉ mở đơn giản nhất là phương pháp thăm dò tuyến
tính: Khi thêm phần tử vào bảng băm nếu bị đụng độ thì sẽ dò địa chỉ kế tiếp…
cho đến khi gặp địa chỉ trống đầu tiên thì thêm phần tử vào địa chỉ này.
- Cấu trúc dữ liệu: Bảng băm trong trường hợp này được cài đặt bằng danh
sách kề có m phần tử, mỗi phần tử của bảng băm là một mẫu tin có một trường
key để chứa khoá của phần tử. Khi khởi động bảng băm thì tất cả trường key
được gán NullKey;
- Khi thêm phần tử có khoá k vào bảng băm, hàm băm h(k) sẽ xác định địa
chỉ i trong khoảng từ 0 đến m-1:

+ Nếu chưa bị xung đột thì thêm phần tử mới vào địa chỉ này.
+ Nếu bị xung đột thì hàm băm lại lần 1, hàm h
1
(k) sẽ xét địa chỉ kế tiếp,
nếu lại bị xung đột thì hàm băm băm lại lần 2, hàm h
2
(k) sẽ xét địa chỉ kế tiếp
nữa, …, và quá trình cứ thế cho đến khi nào tìm được địa chỉ trống và thêm phần
tử mới vào địa chỉ này.
Ví dụ:
Cho tập khoá k
1 12 23 21 24 17 14 22 13 9
Giá trị h(k) = k mod 10
1 2 3 1 4 7 4 2 3 9
(Ở đây ta có m = 10)
Sau đây ta xem xét lần lượt giá trị khoá k được chèn vào bảng băm theo
phương pháp dò tuyến tính:
i k - h(1) = 1: đưa 1 vào vị trí tương ứng với i = 1
- h(12) = 2: đưa 12 vào vị trí tương ứng với i = 2
7
0 9
1 1
2 12
3 23
4 21
5 24
6 14
7 17
8 22
9 13

- Khi tìm một phần tử có khoá k trong bảng băm, hàm băm h(k) sẽ xác định
địa chỉ i trong khoảng từ 0 đến m-1, tìm phần tử khoá k trong bảng băm xuất
phát từ địa chỉ i.
Các chỉ số x
i
dùng để thử là:
x
0
= h(k)
x
i
= (x
0
+ i) mod m với i = 1, , m-1
Khi có đụng độ xảy ra thì ta tìm đến vị trí kế tiếp (chỉ số tăng lên 1). Như vậy
phương pháp thử tuyến tính sẽ có G(i)=i.
Khi giải thuật kết thúc không thành công, việc thêm một phần tử mới vào
trong bảng sẽ được thực hiện dễ dàng bằng cách chứa phần tử này tại vị trí trống
khi kết thúc giải thuật.
Ta có khai báo như sau:
Const free = maxint;
type
Item = record
k : integer; {kho¸}
data : integer; {dữ liệu}
end;
var
T: array [0 m-1] of item;
n: integer;
1.2.2. Khởi tạo bảng băm

Khi tạo bảng băm ta cho tất cả vị trí của bảng băm là trống và số phần tử đang
sử dụng bằng 0. Thủ tục Hash_Initialize dùng để tạo bảng băm trống như sau:
Procedure Hash_Initialize;
var
8
i: Integer;
begin
for i:=0 to m-1 do T[i].key:=free;
n:=0; {số phần tử ®ang sử dụng}
end;
1.2.3. Thêm 1 khóa vào bảng băm
Hàm Hash_Insert(k:integer) thực hiện việc thêm 1 phần tử có khoá k vào
trong bảng băm và trả về vị trí của phần tử này ở trong bảng hoặc trả về giá trị m
nếu bảng bị đầy.
Function Hash_Insert(k:integer): integer;
var
k: Integer;
begin
if n = m-1 then
Hash_Insert:= m
else
begin
x:=h(k);
While T[x].k<>free do
begin
x:=x+1;
if x >= m then x:= x - m
end;
T[x].key=k;
n:= n + 1;

Hash_Insert: = x;
end;
end;
1.2.4. Tìm kiếm một khóa trong bảng băm
Hàm Hash_Search(k:integer) thực hiện việc tìm kiếm khoá k trong bảng băm
và trả về vị trí của phần tử này nếu tìm thấy hoặc trả về giá trị m nếu không tìm
9
thấy.
Function Hash_Search(k:integer): integer;
var
x: Integer;
begin
x:=h(k);
While (T[x].key<>free) and (T[x].key<>k) do
begin
x:=x+1;
if x >= m then x: = x - m
end;
if T[x].key = k then Hash_Search:=x
else Hash_Insert:= m;
end;
1.2.5. Loại bỏ 1 phần tử của bảng băm
Để tránh trường hợp tìm kiếm không thành công mà khoá cần tìm đã có trong
bảng băm, khi loại bỏ phần tử ở vị trí j thì ta phải di chuyển phần tử khác trống
kế tiếp về vị trí trống này. Điều kiện để di chuyển phần tử khác trống kế tiếp ở
vị trí thứ i về vị trí trống thứ j nếu r = h(T[i].k) nằm ngoài vùng vòng từ vị trí j
đến vị trí i, tức là r không thoả mãn các trường hợp sau đây:
i →
j → r →
r → i → j →

i → r →
j →
j < r <=i r <= i < j i < j < r
Khi chuyển phần tử ở vị trí i đến vị trí j thì vị trí i trở thành vị trí trống, sau
đó ta phải di chuyển phần tử khác trống kế tiếp đến vị trí i này và quá trình di
chuyển này tiếp tục cho đến khi nào phần tử kế tiếp là phần tử trống.
Hash_Delete(i: integer);
Var
i, j, r : integer;
a, cont: boolean;
10
begin
cont:=true;
repeat
T[i].key:=free;
j:= i;
repeat
i:=i+1;
if i >= m then i:= i - m;
if T[i].key:= free then cont:=false
else
begin
r := h(T[i]. key);
a := ((j<r)) and (r<=i)) or ((r <= i) and (i<j)) or ((i<j) and (j <r))
end;
until (not cont) or (not a);
if cont then T[j].key:= T[i].key
until not cont
end;
1.3. Phương pháp thăm dò bậc 2 (Quadrratic Probing)

1.3.1. Mô tả phương pháp
Trong phương pháp thăm dò tuyến tính, để tìm kiếm 1 phần tử có khoá là k
thì ta bắt đầu tìm kiếm từ vị trí h(k) của bảng băm. Nếu phần tử ở vị trí này
không phải là vị trí cần tìm thì ta tìm đến vị trí kế tiếp và cứ như thế cho đến khi
nào tìm thấy phần tử này hoặc cho đến khi nào gặp vị trí trống p. Trong trường
hợp không tìm thấy và ta phải thêm phần tử mới vào bảng băm thì ta chứa phần
tử mới tại vị trí p. Điều này dẫn đến trường hợp sau đó ta không thể thêm 1 phần
tử có khoá là k
2
tại vị trí p, mặc dù hàm băm h(k
2
) cho giá trị là p, như vậy ta
phải tìm đến vị trí trống kế tiếp để thêm phần tử khoá k
2
vào bảng băm. Thời
gian tìm kiếm vị trí trống kế tiếp sẽ rất dài khi bảng băm gần đầy.
Trường hợp xấu nhất là việc thêm 1 phần tử mới có giá trị hàm băm nào đó
có thể làm tăng đáng kể số lần tìm kiếm đối với những khoá có giá trị hàm băm
khác. Hiện tượng này được gọi là “gom tụ” (clustering), có thể làm cho phương
pháp thử tuyến tính thực hiện rất chậm khi bảng băm gần đầy.
Ta có thể tránh được hiện tượng gom tụ bằng cách dùng phương pháp thăm
dò bậc hai.
Phương pháp thăm dò bậc hai sử dụng một hàm băm có dạng tổng quát như sau:
h(k,i) = (h’(k) + c
1
i + c
2
i
2
) mod m

Giống như phương pháp thăm dò tuyến tính h’(k) là hàm băm phụ, còn c
1
, c
2
là các hằng số phụ và i = 0, 1, , m-1.
11
Như vậy trong phương pháp pháp thăm dò bậc 2 ta có G(i) = c
1
i + c
2
i
2

là một
hàm bậc hai của biến i.
Trong phương pháp thăm dò bậc hai, nếu băm lần đầu bị xung đột thì sẽ dò
đến địa chỉ mới, ở lần dò thứ i sẽ xét phần tử cách (c
1
i + c
2
i
2
) cho đến khi gặp địa
chỉ trống đầu tiên thì thêm phần tử vào địa chỉ này.
Phương pháp này làm việc tốt hơn phương pháp dò tuyến tính, tuy nhiên để
bảng băm đạt hiệu quả cao nhất các giá trị c
1
, c
2
và m phải chịu một ràng buộc

nào đó.
Một cách đơn giản ta cho G(i) = i
2
, khi đó dãy các chỉ số để thử sẽ là:
x
0
= h(k)
x
i
= (x
0
+i
2
) mod m với i > 0
Ví dụ:
Cho tập khoá k
1 12 23 21 24 17 14 22 13 9
Giá trị h(k) = k mod 10
1 2 3 1 4 7 4 2 3 9
(Ở đây ta có m = 10)
Sau đây ta xem xét lần lượt giá trị khoá k được chèn vào bảng băm theo phương
pháp dò bậc hai:
i
k
- h(1) = 1: đưa 1 vào vị trí tương ứng với i = 1
- h(12) = 2: đưa 12 vào vị trí tương ứng với i = 2
- h(23) = 3: đưa 23 vào vị trí tương ứng với i = 3
- h(21) = 1: vị trí này đã bị chiếm
+ h(21)
1

= (1+1
2
) mod 10

= 2 cũng bị chiếm
+ h(21)
2
= (1+2
2
) mod 10

= 5 còn trống, ta đưa 21 vào vị trí 5
- h(24) = 4: đưa 24 vào vị trí tương ứng với i = 4
- h(17) = 7: đưa 17 vào vị trí tương ứng với i = 7
- h(14) = 4: vị trí này đã bị chiếm
+ h(14)
1
= (4+1
2
) mod 10

= 5 cũng bị chiếm
+ h(14)
2
= (4+2
2
) mod 10

= 8 còn trống, ta đưa 14 vào vị trí 8
- h(22) = 2: vị trí này đã bị chiếm

+ h(22)
1
= (2+1
2
) mod 10

= 3 bị chiếm
+ h(22)
2
= (2+2
2
) mod 10

= 6 còn trống, ta đưa 22 vào vị trí 6
- h(13) = 3: vị trí này đã bị chiếm
+ h(13)
1
= (3+1
2
) mod 10

= 4 bị chiếm
+ h(13)
2
= (3+2
2
) mod 10

= 7 bị chiếm
+ h(13)

3
= (3+3
2
) mod 10

= 0 còn trống, ta đưa 13 vào vị trí 0
0
13
1
1
2
12
3
23
4
24
5
21
6
22
7
17
8
14
9
9
1.3.2. Thêm 1 khoá mới vào bảng băm
Hàm Hash_Insert(k:integer) thực hiện việc thêm 1 phần tử có khoá k vào
trong bảng băm và trả về vị trí của phần tử này ở trong bảng hoặc trả về giá trị
M nếu bảng bị đầy.

12
Function Hash_Insert(k:integer): integer;
var
x, i : Integer;
begin
if n = m - 1 then Hash_Insert:= m
else
begin
x:= h(k);
i:= 1;
While T[x].key <> free do
begin
x:= (x + i*i) mod m;
i:= i + 1;
end;
T[x].key:= k;
n:=n+1;
Hash_Insert:=x;
end;
end;
1.3.3. Tìm kiếm 1 khóa trong bảng băm
Hàm Hash_Search(k:integer) thực hiện việc tìm kiếm khoá k trong bảng băm
và trả về vị trí của phần tử này nếu tìm thấy hoặc trả về giá trị M nếu không tìm
thấy.
Function Hash_Search(k:integer): integer;
var
x, i: Integer;
begin
x:= h(k);
i: = 1;

While (T[x].key<>free) and (T[x].key<>k) do
begin
x:= (x + i*i) mod m;
i: = i + 1;
end;
if T[x].key = k then Hash_Search: = x
else Hash_Insert:= m;
end;
1.4. Phương pháp thăm dò kép (Double Hashing)
1.4.1. Mô tả phương pháp
Ta cũng có thể tránh hiện tượng gom tụ bằng cách dùng phương pháp thăm
dò kép. Về cơ bản cũng giống như phương pháp thăm dò tuyến tính, nhưng thay
13
vì xét các vị trí liên tiếp đi sau vị trí đụng độ ta dùng hàm băm thứ 2 để cho mật
độ tăng cố định được dùng trong các lần thử sau đó. Điều này được thực hiện dễ
dàng bằng cách sử dụng một hàm băm thứ hai h
2
(k). Khi đó hàm băm kép có
dạng:
h(k) = (h
1
(k) + i*h
2
(k)) mod m
Như vậy trong phương pháp pháp thăm kép ta có G(i) = i*h
2
(k)
Trong đó h
1
(k) và h

2
(k) là các hàm băm phụ. Vị trí ban đầu được thăm dò là
T[h
1
(k)], các vị trí dò kế tiếp là độ dịch từ các vị trí trước đó theo một lượng
(h
2
(k) mod m). Như vậy khác với trường hợp thăm dò tuyến tính và thăm dò bậc
hai, dãy thăm dò ở đây phụ thuộc vào khoá k theo hai cách: vị trí thăm dò ban
đầu, độ dịch chuyển hoặc cả hai yếu tố trên.
Hàm băm thứ 2 (h
2
(k))

phải được chọn cẩn thận vì ngược lại chương trình có
thể không thực hiện gì cả.
Nếu chọn y = 0, chương trình sẽ lặp vô tận khi xảy ra đụng độ.
Có nhiều cách để chọn hàm băm thứ hai (h
2
(k)) tuỳ thuộc vào kích thước của
bảng băm (m):
- m và y phải là 2 số nguyên tố cùng nhau bởi vì trong trường hợp ngược lại
sẽ có những chuỗi phép thử rất ngắn. Điều này bắt buộc m phải là số nguyên tố
và h
2
(k) < m.
- Hàm thứ 2 (h
2
(k)) nên khác với hàm thứ nhất (h
1

(k)) bởi vì trong trường hợp
ngược lại sẽ xảy ra hiện tượng gom tụ. Có thể chọn hàm h
2
(k) như sau:
h
2
(k) = m - 2 - k mod (m - 2). Tuy nhiên việc sử dụng hàm băm thứ hai có thể
dẫn đến trường hợp thời gian tính giá trị hàm h
2
(k) lại đáng kể hơn so với thời
gian tìm kiếm của phương pháp thử tuyến tính, đặc biệt là đối với các khoá dài.
- Một cách tiện dụng để bảo đảm dung hoà được các yếu tố trên, ta chọn m là
luỹ thừa của 2 và thiết kế h
2
(k) sao cho nó luôn tạo ra số lẻ.
Ví dụ:
Cho tập khoá k
2 12 23 21 24 17 16 27
Giá trị h
1
(k) = k mod m
2 4 7 5 0 1 0 3
(Ở đây ta có m = 8 và chọn hàm h
2
(k) = m - 1)
Sau đây ta xem xét lần lượt giá trị khoá k được chèn vào bảng băm theo phương
pháp dò kép:
i k - h
1
(1) = 2: đưa 2 vào vị trí tương ứng với i = 2

- h
1
(12) = 4: đưa 12 vào vị trí tương ứng với i = 4
- h
1
(23) = 7: đưa 23 vào vị trí tương ứng với i = 7
0 24
14
1 17
2 2
3 27
4 12
5 21
6 16
7 23
- Một cách khác đó là ta có thể cho m là số nguyên tố và thiết kế hàm h
2
(k)
sao cho nó luôn trả về một số nguyên dương nhỏ hơn m. Ví dụ, cho m là số
nguyên tố và :
+ h
1
(k) = k mod m
+ h2(k) = 1 + (k mod m’)
Trong đó m’ chọn nhỏ hơn m. Chẳng hạn m’ = m - 1 hoặc m’ = m - 2
1.4.2. Tìm kiếm và thêm 1 khoá mới vào bảng băm
Hàm Search(k:integer) thực hiện việc tìm kiếm và thêm khoá k trong bảng
băm và trả về vị trí của phần tử này nếu tìm thấy hoặc vị trí thêm vào nếu giá trị
của hàm nhỏ hơn m hoặc trả về giá trị m nếu bảng băm bị đầy.
Function Hash_Search(k:integer): integer;

var
x, y: Integer ;
begin
x:= h
1
(k) ;
y:= h
2
(k) ;
While (T[x].key<>free) and (T[x].key<> k) do
x:= (x + i*y) mod m;
if T[x].key = k then Search:= x; {T×m thÊy}
else
if n = m - 1 then Search:= m; {bảng băm đầy}
else
begin
T[x].key:= k; {ChÌn thªm kho¸ k vµo}
n: = n + 1;
Search:= x;
end;
end;
Số lần so sánh trung bình cho 1 lần tìm kiếm thành công có lớn hơn chút ít so
với phương pháp thử tuyến tính, nhưng phương pháp này tránh được hiện tượng
gom tụ.
Phương pháp băm kép sử dụng ít phép thử hơn sơ với phương pháp thử tuyến
15
tính (trường hợp trung bình)
Số lần so sánh trung bình trong trường hợp tìm kiếm không thành công là:
C = 1/(1 - a)
Số lần so sánh trung bình trong trường hợp tìm kiếm thành công là:

C = ln(1 - a)/a
(a = n/m gọi là hệ số tải của bảng băm)
Thực tế số phép thử trung bình nhỏ hơn 5 lần cho trường hợp tìm kiếm không
thành công nếu bảng băm chứa ít hơn 80% và cho trường hợp tìm kiếm thành
công nếu bảng băm chứa ít hơn 99%.
Phép thử trung bình nhỏ hơn 5 lần cho trường hợp tìm kiếm không thành
công nếu bảng băm chứa ít hơn 80% và cho trường hợp tìm kiếm thành công
nếu bảng băm chứa ít hơn 99%.
16
Phần 2
PHÂN TÍCH PHÉP BIẾN ĐỔI KHOÁ
BẰNG PHƯƠNG PHÁP ĐỊA CHỈ MỞ
3.1. Phân tích phép biến đổi khoá
Ta thấy phương pháp địa chỉ mở không thích hợp trong trường hợp có 1 số
lớn các phần tử có cùng giá trị hàm băm, nhưng việc tìm kiếm trên bảng băm thì
được thực hiện dễ dàng trong trường hợp này.
Các kỹ thuật định địa chỉ mở có thể bất lợi trong tình huống khi mà số lần
thêm vào là loại bỏ chưa biết trước là bao nhiêu.
Kích thước bảng băm của phương pháp địa chỉ mở lớn hơn so với kích thước
bảng băm của phương pháp kết nối trực tiếp, bởi vì ta phải có m>n, nhưng vùng
nhớ tổng cộng của phương pháp này lại nhỏ hơn bởi vì các phần tử không có
vùng liên kết.
Giả sử tất cả các khoá trong bảng băm đều có cùng xác suất và hàm băm
phân bố chúng đều đặn trên miền chỉ số của bảng. Khi ta thêm một khoá mới
vào bảng băm có kích thước m đã chứa n phần tử, xác suất để có 1 vị trí trống ở
lần thử đầu tiên là
m
n
1−
, đây cũng là xác suất p

1
cần

cho một lần so sánh. Xác
suất cần một lần thử thứ hai bằng tích xác suất lần thử đầu tiên có đụng độ với
xác suất để có một vị trí trống trong lần thử thứ hai. Một cách tổng quát gọi p
i

xác suất của phép thêm vào cần đến i phép thử, ta có:
m
nm
p
1

=
1m
nm
*
m
n
p
2


=
2m
nm
*
1m
1n

*
m
n
p
3




=

1im
nm
*
2im
2in
* *
2m
nm
*
1m
1n
*
m
n
p
i
+−

+−

+−




=
Kỳ vọng của số lần thử cần thiết khi thêm khoá thứ n + 1 là:

+
=
+
=
1n
1i
i1n
p*iE
17

1nm
1
* *
2m
2n
*
1m
1n
*
m
n
*)1n(

1m
nm
*
m
n
*2
m
nm
*1
+−−



+++


+

=
hay
1nm
1m
E
1n
+−
+
=
+
Bởi vì số lần thử cần thiết để thêm vào một phần tử của bảng băm cũng chính
là số lần thử cần thiết để tìm kiếm nó, nên kỳ vọng có thể được dùng để tính số

trung bình E các phép thử cần thiết để truy xuất một khoá ngẫu nhiên trong
bảng.
Xét bảng có kích thước m đã chứa n khoá thì:
)hh(
n
1m
2im
1
n
1m
E
n
1
E
1nmim
n
1i
n
1i
i +−+
==

+
=
+−
+
==
∑∑
với
m

1

2
1
1h
m
+++=
là hàm điều hoà. H
m
có thể xấp xỉ với h
m
= ln(m) + g với g là hàng số Euler.
nếu ta đặt
1m
n
a
+
=
thì:
( )
)a1ln(
a
1
n1m
1m
ln
a
1
)1nmln()1mln(
a

1
E −−=
−+
+
=+−−+=
ta có thể coi a là hệ số tải của bảng băm, nếu a = 0 ⇒ bảng băm là trống;
a = 1 ⇒ bảng băm gần đầy.
Sau đây là bảng các giá trị của kỳ vọng E các phép thử theo hệ số tải a.
a E
Từ bảng kết quả này ta nhận thấy tính hiệu quả khá tốt của phép
biến đổi khoá. Kể cả trong trường hợp bảng băm đã đầy tới 90%
0,1 1,05
0,25 1,15
0,5 1,39
0,75 1,85
0,9 2,56
0,95 3,15
0,99 4,66
Trên đây là việc phân tích phương pháp giải quyết đụng độ với giả thiết rằng
các khoá còn lại của bảng băm trải đều trên các vị trí của bảng. Nhưng trong
thực tế các phương pháp có hiệu quả kém hơn một chút.
Khi phân tích chi tiết phương pháp thử tuyến tính ta nhận được kỳ vọng của
18
các phép thử là:
a1
2
a
1
E



=
. Với Kỳ vọng này ta nhận được một vài giá trị của
hàm E như sau:
a E
Kết quả này cho thấy, phương pháp đơn giản nhất (dò tuyến
tính) cũng có một hiệu quả tương đối tốt. So với các cấu trúc dữ
liệu kiểu cây kết quả này cũng đạt hiệu quả cao hơn, đặc biệt
trong các phép loại bỏ hay chèn thêm khoá vào cấu trúc dữ liệu
0,1 1,06
0,25 1,17
0,5 1,50
0,75 2,50
0,9 5,50
0,95 10,50
Tuy nhiên cũng cần nói tới nhược điểm lớn nhất của phép biến đổi khoá đó là
kích thước của bảng băm bị cố định và không thể điều chỉnh theo yêu cầu thực
tế. Như vậy khi thiết kế dữ liệu ta cần căn cứ vào số phần tử để ước lượng kích
thước của bảng băm để tránh hiệu quả kém cũng như lãng phí vùng nhớ. Kể cả
khi đã xác định chính xác số phần tử được biết trước thì kích thước của bảng
băm cũng nên chọn lớn hơn số phần tử khoảng 10%.
3.2. Một số lưu ý khi sử dụng phương pháp thử
Khi chọn kích thước bảng băm (m) cần lưu ý:
- Nếu hàm băm là hàm lấy phần dư (mod) thì m là số nguyên tố;
- Kích thước m không được lớn lắm để tránh lãng phí bộ nhớ;
- Kích thước m cũng không được nhỏ quá để tránh đầy bảng.
Việc chọn kích thước m phải hợp lý, m phải chọn vừa đủ lớn và khi bảng
băm gần đầy (hệ số tải lớn) thì ta tăng kích thước của bảng băm.
Kích thước m và hàm băm không phù hợp với nhau có thể dẫn đến trường
hợp không tìm đến được 1 vị trí trống để thêm một khoá vào bảng băm mặc dù

bảng băm chưa đầy.
Một phần tử không thể đơn giản bị loại khỏi bảng băm, lý do là khi loại bỏ 1
phần tử thì việc tìm kiếm các phần tử bị đụng độ với nó trước kia sẽ được kết
thúc tại vị trí loại bỏ này (vị trí trống sau khi loại bỏ). Điều này dẫn đến trường
hợp tìm kiếm không thành công, mặc dù vẫn có khoá cần tìm trong bảng băm.
Cách giải quyết trong trường hợp này là ta sử dụng 1 khóa đặc biệt để chỉ vị trí
trống khi thêm vào hay loại bỏ, những khoá đặc biệt này chỉ vị trí khác trống khi
tìm kiếm.
19
20
Phần 3
CHƯƠNG TRÌNH MINH HOẠ
Chương trình minh hoạ cách tạo bảng bảng băm theo ba kỹ thuật: thăm dò
tuyến tính (Linear brobing), thăm dò bậc hai (Quadratic brobing) và thăm dò
kép (Double brobing). Chương trình được viết bằng ngôn ngữ Borland
Delphi.
Để tiện cho việc so sánh kết quả của ba kỹ thuật thăm dò, chương trình sử
dụng chung một tập dữ liệu liệu khoá chung cho cả ba kỹ thuật thăm dò. Tập
dữ liệu khoá là các số tự nhiên được nhập trực tiếp từ bàn phím hoặc có thể
sinh ngẫu nhiên trong khoảng từ 0 đến 100.
Giao diện chương trình minh hoạ cách tạo dữ liệu
cho bảng băm bằng phương pháp địa chỉ mở
Sau đây là các các thủ tục tạo dữ liệu cho bảng băm bằng phương pháp địa chỉ
mở theo ba kỹ thuật thăm dò khác nhau:
21
1. Thủ tục chèn một khoá bằng kỹ thuật dò tuyến tính
procedure Brobing.LinearClick(Sender: TObject);
var
i,j: integer;
begin

InputKey;
for i:=0 to dem-1 do
begin
tempbk:=kn[i] mod m;
if bk[tempbk]=-1 then bk[tempbk]:=kn[i]
else
begin
j:=1;
while bk[(tempbk+j) mod m]<>-1 do j:=j+1;
bk[(tempbk+j) mod m]:=kn[i];
end;
end;
HashTable1.RowCount:=m+1;
for i:=0 to m-1 do
begin
HashTable1.Cells[0,i+1]:=IntToStr(i);
if bk[i]=-1 then HashTable1.Cells[1,i+1]:='NULL'
else HashTable1.Cells[1,i+1]:=IntToStr(bk[i]);
end;
end;
22
1. Thủ tục chèn một khoá bằng kỹ thuật dò bậc hai
procedure Brobing.QuadraticClick(Sender: TObject);
var
i,j: integer;
begin
InputKey;
for i:=0 to dem-1 do
begin
tempbk:=kn[i] mod m;

if bk[tempbk]=-1 then bk[tempbk]:=kn[i]
else
begin
j:=1;
while bk[(tempbk+j*j) mod m]<>-1 do
begin
tempbk:=tempbk+j;
j:=j+1;
end;
bk[(tempbk+j*j) mod m]:=kn[i];
end;
end;
HashTable2.RowCount:=m+1;
for i:=0 to m-1 do
begin
HashTable2.Cells[0,i+1]:=IntToStr(i);
if bk[i]=-1 then HashTable2.Cells[1,i+1]:='NULL'
else HashTable2.Cells[1,i+1]:=IntToStr(bk[i]);
end;
end;
23
3. Thủ tục chèn một khoá bằng kỹ thuật thăm dò kép
procedure Brobing.DoubleClick(Sender: TObject);
var
i,j: integer;
begin
InputKey;
for i:=0 to dem-1 do
begin
tempbk:=kn[i] mod m;

if bk[tempbk]=-1 then bk[tempbk]:=kn[i]
else
begin
j:=1;
tempbk1:=m-1;
while bk[(tempbk+j*tempbk1) mod m]<>-1 do
begin
j:=j+1;
end;
bk[(tempbk+j*tempbk1) mod m]:=kn[i];
end;
end;
HashTable3.RowCount:=m+1;
for i:=0 to m-1 do
begin
HashTable3.Cells[0,i+1]:=IntToStr(i);
if bk[i]=-1 then HashTable3.Cells[1,i+1]:='NULL'
else HashTable3.Cells[1,i+1]:=IntToStr(bk[i]);
end;
end;
24
TÀI LIỆU THAM KHẢO
[1] Đào Thanh Tĩnh - Cấu trúc dữ liệu và giải thuật
(Tài liệu ôn thi cao học ngành CNTT) - HVKTQS - 2005.
[2] Hà Huy Khoái - Nhập môn số học thuật toán - Nxb KHKT – 1997.
[3] Nguyễn Trung Trực - Cấu trúc dữ liệu.
Đại học bách khoa thành phố HCM - 1997.
[4] Thomas H. Cormen, Charles E. Leiserson, Ronald L. Rivest
Giáo trình thuật toán - Nxb Thống kê - 2002
[5] Robert Sedgewick - Algoritms, Addison Wesley

Nxb KHKT - 1994.
[6] Kenneth H.Rosen - Toán rời rạc ứng dụng trong tin học
Nxb KHKT - 1997.
[7] Dictionary of Algorithms and Data Structures
25

×