GIẢI QUYẾT ĐỤNG ĐỘ TRONG PHÉP BIẾN ĐỔI KHOÁ BĂMTrong 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.. - hk: hàm băm biếm đổi khoá k thành vị trí
Trang 1HỌ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
Trang 2Hà Nội, tháng 02 năm 2006
Trang 3Phần 2 Phân tích phép biến đổi khoá
bằng phương pháp địa chỉ mở
16
Trang 4BÀ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
Trang 5Vớ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
Trang 6GIẢI QUYẾT ĐỤNG ĐỘ TRONG PHÉP BIẾN ĐỔI KHOÁ BĂM
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:
end;
if T[x].key = k then t×m thÊy else kh«ng t×m thÊy“ ” “ ”
Trong đó:
Trang 7- 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 h1(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 h2(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ụ:
(Ở đâ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:
- h(12) = 2: đưa 12 vào vị trí tương ứng với i = 2
Trang 8Ta có khai báo như sau:
Const free = maxint;
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
Trang 91.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;
begin
x:=h(k);
While T[x].k<>free do begin
x:=x+1;
if x >= m then x:= x - m 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
Trang 101.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:
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;
Trang 11until (not cont) or (not a);
if cont then T[j].key:= T[i].key until not cont
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à k2 tại vị trí p, mặc dù hàm băm h(k2) 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á k2 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
Trang 12Như vậy trong phương pháp pháp thăm dò bậc 2 ta có G(i) = c1i + c2i 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 (c1i + c2i2) 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ị c1, c2 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) = i2 , khi đó dãy các chỉ số để thử sẽ là:
- 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
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
Trang 13Function Hash_Insert(k:integer): integer;
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;
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
Trang 14vì 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 h2(k) Khi đó hàm băm kép có dạng:
h(k) = (h1(k) + i*h2(k)) mod m
Như vậy trong phương pháp pháp thăm kép ta có G(i) = i*h2(k)
Trong đó h1(k) và h2(k) là các hàm băm phụ Vị trí ban đầu được thăm dò là T[h1(k)], các vị trí dò kế tiếp là độ dịch từ các vị trí trước đó theo một lượng (h2(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 (h2(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 (h2(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ố
- 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ế h2(k) sao cho nó luôn tạo ra số lẻ
Ví dụ:
(Ở đây ta có m = 8 và chọn hàm h2(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:
- h (12) = 4: đưa 12 vào vị trí tương ứng với i = 4
Trang 15+ h1(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;
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
Trang 16(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%
Trang 17Phầ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 p1 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 pi là xác suất của phép thêm vào cần đến i phép thử, ta có:
m
nm
p1 = −
1m
nm
*m
nm
*1m
1n
*m
nm
*2im
2in
*
*2m
nm
*1m
1n
Trang 18
1nm
1
*
*2m
2n
*1m
1n
*m
n
*)1n(
1m
nm
*m
n
*2m
nm
++
−
−+
−
=
hay
1nm
1m
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
1m2im
1n
1mEn
1
1 i
11
hm = + + +
là hàm điều hoà Hm có thể xấp xỉ với hm = ln(m) + g với g là hàng số Euler.nếu ta đặt
1m
na
1mlna
1)1nmln(
)1mln(
a
1
−+
+
=+
−
−+
=
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%
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