Tải bản đầy đủ (.pdf) (11 trang)

PHÉP BIẾN ĐỔI KHÓA – HÀM BĂM part 1 ppt

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 (283.23 KB, 11 trang )



PHÉP BIẾN ĐỔI KHÓA – HÀM BĂM

Băm là phương pháp rất thích hợp để cài đặt tập hợp có số phần tử lớn và thời
gian cần thiết để thực hiện các phép toán từ điển, ngay cả trong trường hợp xấu
nhất, là tỉ lệ với cỡ của tập hợp.
Chúng ta sẽ đề cập tới hai phương pháp băm khác nhau. Một gọi là băm mở cho
phép sử dụng một không gian không hạn chế để lưu giữ các phần tử của tập hợp.
Phương pháp băm khác được gọi là băm đóng sử dụng một không gian cố định do
đó tập hợp được cài đặt phải có cỡ vượt qua không gian cho phép

$1. Khái niệm băm và hàm băm.
1.1. Băm mở và hàm băm.
a. Băm mở. Nội dung cơ bản của băm mở là phân chia tập hợp đã cho thành một
số cố định các lớp. Chẳng hạn ta muốn phân thành N lớp được đánh số 0,1, ,N-1.
Ta sử dụng mảng T với chỉ số chạy từ 0 đến N-1. Mối thành phần T[i] cúa mảng
được nói đến như một rổ đựng các phần tử của tập hợp thuộc lớp thứ i. Các phần
tử của tập hợp thuộc mỗi lớp tổ chức dưới dạng một danh sách liên kết. Do T[i] sẽ
chứa con trỏ trỏ đến danh sách của lớp i.
b. Bảng băm. Ta gọi mảng T mà mỗi phần tử của nó như là một rổ đựng các phần
tử của tập hợp thuộc lớp tương ứng là bảng băm.
Việc phân chia các phần tử của tập hợp vào các lớp được thực hiện bởi hàm băm
h.
c. Hàm băm. Nếu x là một giá trị khoá của phần tử nào đó của tập hợp thì h(x) là
chỉ số nào đó của mảng T và ta gọi h(x) là giá trị băm (hash value) của x. Như vậy h
là ánh xạ từ tập hợp các khoá K vào tập hợp { 0,1,…,N-1}.


$.2. Các phương pháp lựa chọn, thiết kế hàm băm và giải quyết va chạm.
2.1. Các phương pháp lựa chọn, thiết kế hàm băm


Có hai tiêu chuẩn chính để lựa chọn hàm băm. Trước hết nó phải cho phép tính
được dễ dàng và nhanh chóng giá trị băm của mỗi khoá. Thứ hai, nó phải phân bố


đều các khóa vào các rổ. Trên thực tế tiêu chuẩn thứ hai rất khó được thực hiện.
Sau đây chúng ta đưa ra một số phương pháp thiết kế hàm băm.
a. Phương pháp cắt bỏ.
Giả sử khoá là số nguyên (nếu khoá không phải là số nguyên, ta xét đến các mã số
của chúng). Ta sẽ bỏ đi một phần nào đó của khóa, và lấy phần còn lại lám giá trị
băm của khoá. Chẳng hạn nếu khoá là các số nguyên gồm 10 chữ số và bảng băm
gồm 1000 thành phần, khi đó ta có thể lấy chữ số thứ nhất, thứ ba và thứ bảy từ
bên trái làm giá trị băm. Ví dụ: h(7103592810) = 720.
Phương pháp cắt bỏ rất đơn giản nhưng nó thường không phân bố đều các khoá.
b. Phương pháp gấp.
Giả sử khoá là số nguyên. Ta phân chia khóa thành một số phần, sau đó kết hợp
các phần lại bằng một cách nào đó (phép cộng hoặc phép nhân) để nhận giá trị
băm. Nếu khoá là số nguyên 10 chữ số ta phân thành các nhóm ba ba hai hai chữ
số từ bên trái, cộng các nhóm với nhau sau đó cắt cụt nếu cần thiết, ta sẽ nhận
được giá trị của hàm băm. Ví dụ: số 7103592810 được biến đổi thành
710+359+28+10 = 1107, do đó ta có giá trị băm là 107.
Vì mọi thông tin trong khoá đều được phản ánh vào giá trị băm, nên phương pháp gấp
cho phân bố đều các khoá hơn phương pháp cắt bỏ.

c. Phương pháp sử dụng phép toán lấy dư.
Giả sử khoá là số nguyên và ta muốn chia tập hợp các khoá thành n lớp. Chia số
nguyên cho n rồi lấy phần dư làm giá trị băm. Điều này trong Pascal được thực
hiện bằng phép toán mod. Tính phân bố đều các khoá của hàm băm được xác định
bằng phương pháp này phụ thuộc nhiều vào việc chọn n. Tốt nhất chọn n là số
nguyên tố. Chẳng hạn thay cho việc chọn n = 1000 ta sẽ chọn n = 997 hoặc n =
1009.

Ví dụ viết một hàm băm trong Pascal để băm các khoá là các xâu kí tự có độ dài 10
thành các giá trị từ 0…n-1.

Type keytype = string[10];
Function h(x: keytype):0 n-1;


Var
i, sum:integer ;
Begin
Sum:=0;
For i=1 to 10 do
Sum=sum + ord(x[i]);
h:= sum mod n;
end.

Trong hàm băm trên, ta đã chuyển đổi các xâu kí tự thánh các số nguyên bằng
cách lấy tổng số của các mã số của từng kí tự trong xâu dùng hàm ord(c) để lấy mã
số của kí tự c.

2.2. Bảng băm đóng.
Trong bảng băm mở, mỗi thành phần T[i] của bảng lưu trữ con trỏ trỏ tới danh sách
các phần tử của tập hợp được đưa vào lớp thứ i (i = 0,…,N-1).
a. Băm đóng. Khác với bảng băm mở, trong bảng băm đóng, mỗi phần tử của tập
lưu giữ trong chính các thành phần T[i] của mảng. Do đó ta có thể khai báo kiểu dữ
liêụ từ điển được cài đặt bởi bảng băm đóng như sau:
0

1


2














Type Dictionary = array [0 N-1] of keytype;
Keytype là kiểu dữ liệu của khoá của các phần tử trong từ điển.


2.3. Phân tích, đánh giá và minh họa phương pháp băm.
a. Phân tich đánh giá. Trong tất cả những thuật toán được xét từ trước tới nay, việc
định vị một mục được xác định bởi một dãy các so sánh. Trong mỗi trường hợp,
mục cần tìm được so sánh nhiều lần với các mục ở những vị trí nào đó trong cấu
trúc. Đối với nhóm n mục, phép tìm kiếm tuyến tính đòi hỏi O(n) lần so sánh, trong
khi đó phép tìm kiếm nhị phân chỉ cần O(log n) lần so sánh. Trong một số trường
hợp, những thuật toàn này thực hiện còn quá chậm. Ví dụ, bảng các kí hiệu được
lập bởi bộ dịch lưu trữ các định danh và những thông tin về chúng. Vận tốc xây
dựng và tìm kiếm trong bảng này quyết định vận tốc dịch. Một cấu trúc dữ liệu thực
hiện tìm kiếm nhanh hơn, được gọi là bảng băm (hash T), trong đó vị trí cuả một
mục được xác định trực tiếp bằng một hàm của chính nó chứ không phải bằng một

dãy các so sánh thử và sai (trial-and-error). Thời gian gian cần thiết để định vị một
mục trong bảng băm trong trường hợp tốt nhất là O(1), nghĩa là nó là một hằng số
và không phụ thuộc vào số các mục được lưu trữ.
b. Ví dụ minh họa. Để minh hoạ, giả sử rằng cần lưu trữ 25 số nguyên trên đoạn
0 999 trong bảng băm. Có thể cài đặt bảng băm này bằng một mảng T các số
nguyên được đánh chỉ số trên đoạn 0 999, trong đó mỗi phần tử của mảng được
khởi động tại một giá trị câm nào đó, chẳng hạn như tại 1. Nếu ta dùng mỗi số
nguyên i trong tập hợp đó làm chỉ số, nghĩa là nếu ta lưu trữ i trong T[i], ta có thể
định vị một số nguyên number cho trước chỉ bằng cách kiểm tra T[number]
=number hay không. Hàm h được định nghĩa bởi h(i)=i xác định vị trí của mục i
trong bảng băm.
Hàm băm trong vị dụ này thực hiện tìm kiếm một cách lý tưởng vì thời gian cần
thiết để tìm kiếm một giá trị cho trước trong báng là hằng số; chỉ cần thử một vị trí.
Như vậy, sơ đồ này hiêu quả về tính thời gian, nhưng không hiệu quả về không
gian, chỉ 25 trong số 1000 vị trí khả dĩ được dùng để lưu trư các mục còn 975 vị trí
còn lại không được dùng do đó đã lãng phí 97,5% không gian, chỉ dùng 2,5%.


Vì có thể lưu trữ 25 giá trị ở 25 vị trí ta có thể sử dụng tốt hơn bằng cách dùng
mảng T được đánh chỉ số trên đoạn từ 0 đến 24. Dĩ nhiên là không thể dùng hàm
băm ban đầu h(i)=i được nữa. Thay vào đó ta có thể dùng: h(i)=i mod 25
Vì hàm này luôn luôn có miền giá trị là những số nguyên trên đoạn từ 0 đến 24.
Như vậy số nguyên 52 sẽ được lưu trữ ở T[2] vì h(52)= 52 mod 25 = 2. Tương tự
với các số nguyên 129; 500 ; 73; 49 sẽ được lưu tại các vị trí 4, 0, 23 và 24 theo
thứ tự đó.

T[0]
T[1]
T[2]
T[3]

T[4]
T[5]
.
.
T[23]
T[24]

Với ví dụ trên nếu lấy hàm h[i]:=i mod 25 thì sẽ xẩy ra hiện tượng gọi là sự va
chạm. vì nếu 77 được lưu trữ, nó sẽ được đặt vào vị trí h(77)= 77 mod 25 = 2,
nhưng vị trí này đã bị chiếm bởi 52. Cũng bằng cách đó, nhiều giá trị khác có thể
trùng vào một vị trí cho trước,ví dụ,2,27,102 và trong thực tế tất cả các số nguyên
dạng 25k + 2 đều được lưu trữ tại vị trí 2. Dĩ nhiên là cần phải có một cách nào đó
để giải quyết những va chạm này.

$3. Các phương pháp xử lý va chạm.
3.1.Phương pháp thăm dò tuyến tính.
a. Nội dung. Trong sơ đồ này, phép tuyến tính trong bảng bắt đầu từ chỗ có va
chạm và tiếp tục dò đền khi tìm thấy một khe trống có thể lưu trữ được, va chạm
với giá trị 52 ở vị trí 2, một cách đơn giản là ta đặt 77 ở giá trị 3; để chèn 102,
500
-1
52
-1
129
-1
.
.
273
49




chúng ta đi theo dãy thăm dò gồm các vị trí 2, 3, 4 và 5 để xác định vị trí đầu tiên có
thể dùng được và lưu trữ 102 ở T[5]. Khi đặt đến cuối bảng, ta lại tiếp tục ở vị trí
đầu tiên. Ví dụ, 123 được lưu trữ ở vị trí 1 vì nó va chạm với 273 ở vị trí 23, và dãy
thăm dò 23, 24, 0, 1 xác định khe rỗng đầu tiên ở vị trí 1.

T[0]
T[1]
T[2]
T[3]
T[4]
T[5]
.
.
T[23]
T[24]

Để xác định một giá trị cho trước có nằm trong bảng băm này hay không, trước hết
ta dùng hàm băm để tính vị trí có thể tính trong bảng xẩy ra các khả năng:
+ Thứ nhất, nếu vị trí này rỗng (chứa -1), ta có thể kết luận ngay rằng giá tri này
không có trong bảng.
+Thứ hai, nếu vị trí này chứa giá trị nói trên, ta biết ngay rằng là đã tìm ra.
+Thứ ba, vị trí này có một giá trị khác với giá trị đang tìm do sự va chạm gây ra
bởi cách lập bảng. Trong trường hợp này ta thực hiện tìm kiếm tuyến tính “vòng” tại
vị trí này, và tiếp tục cho đến khi tìm thấy giá trị đó hoặc đạt đến vị trí rỗng hay vị trí
khởi đầu, điều này chỉ ra rằng mục cần tìm không có trong bảng. Như vậy thời gian
tìm kiếm trong hai trường hợp đầu là hằng số, còn trong trường hợp thứ ba không
phải thế. Nếu bảng gần đầy, ta phải thử hầu hết các vị trí trước khi tìm ra mục đó
hay trước khi kết luận rằng mục đó không có trong bảng.

Để làm tốt hơn việc tìm kiếm, ta có thể thực hiện ba việc dưới đây:
- Mở rộng kích thước của bảng.
- Dùng phương pháp khác để giải quyết va chạm.
- Dùng một hàm băm khác.
b. Nhận xét.
500

123

52

77

129

102

.

.
273

49




- Việc làm cho kích thước của bảng bằng số các mục được lưu trữ như trong ví dụ
đầu tiên không thực tế lắm, nhưng nếu dùng bảng nhỏ hơn có khả năng dẫn tới va
chạm. Trong thực tế, ngay cả khi có thể lưu trữ nhiều hơn hẳn số mục cần thiết vẫn

có khả năng xảy ra va chạm. Ví dụ, với bảng băm gồm 365 vị trí lưu trữ 23 mục
được chọn ngẫu nhiên, xác xuất xảy ra va chạm là lớn hơn 0.5 (Điều này liên quan
đến bài toán sinh nhật: xác xuất để ít nhất 2 trong 23 người có cùng ngày sinh là
lớn hơn 50%). Như vậy rõ ràng rằng việc chờ đợi một bảng băm hoàn thiện không
bị va chạm là không xác đáng. Thay vào đó, chúng ta phải thoả mãn với với số va
chạm đủ ít. Những nghiên cứu thực nghiệm cho phép chúng ta dùng những bảng
có độ dài khoảng từ 1.5 đến 2 lần số mục cần lưu trữ.
- Cách thứ hai để hoàn thiện việc tìm kiếm là thiết kế một phương pháp xử lý va
chạm tốt hơn. Trong sơ đồ thăm dò tuyến tính, khi xảy ra va chạm, giá trị va chạm
được lưu trữ ỏ những vị trí được để dành cho các mục băm trực tiếp đến các vị trí
ấy. Lối tiếp cận “lấy của Peter để trả cho Paul” này làm tăng xác xuất xảy ra va
chạm, như vậy làm cho vấn đề phức tạp hơn.
3.2. Phương pháp dây chuyền(chaining).
Phương pháp này dùng các danh sách liên kết để lưu trữ các giá trị. Trong
phương pháp này, ứng với một địa chỉ của bảng ta có một danh sách liên kết chứa
các phần tử có khóa khác nhau mà có cùng một địa chỉ. Để minh họa, giả sử ta
muốn lưu trữ một nhóm các tên. Ta có thể dùng mảng T chứa các con trỏ được
đánh chỉ số trên đoạn ‘A’ ’Z’, lúc đầu là rỗng, và dùng hàm băm h(Name) =
Name[1]. Ví dụ ‘Adam,John’ và ‘Doe,Mary’ được lưu trữ trong các nút được chỉ bởi
T[‘A’] và T[‘D']:
T[‘A”]
T[‘B’]
T[‘C’]
T[‘D’]
T[‘E’]
T[‘F’]

T[‘Y’]
T[‘Z’]







.
.



Adams,Joh


Doe, Mary





Khi xảy ra va chạm, ta chèn mục mới vào vị trí thích hợp của danh sách liên kết .
Ví dụ, vì h(‘Davis, Joe’) = h(‘Doe, Mary’) = ‘D’, va chạm xảy ra khi ta muốn lưu trữ
tên ‘Davis, Joe’, ta đưa thêm một nút mới chứa tên này vào danh sách liên kết
được ghi bởi T[‘D’]:


T[‘A”]
T[‘B’]
T[‘C’]
T[‘D’]
T[‘E’]

T[‘F’]
.
.
T[‘Y’]
T[‘Z’]
a. Khai báo.
Type ref=^node;
Node=record
Key:integer;
Info:integer;
Next:ref;
end;
Var heads : aray[0 M-1] of ref;
t,z: ref;
T[0]
T[1]
T[2]
T[3]
T[4]
T[4]
.
.
T[M-2]
T[M-1]








.
.


Adams,John


Davis, Joe



Doe, Mary








.
.





























z



b. Khởi tạo bảng băm.Mảng heads chứa các con trỏ đấu chỉ vào danh sách liên kết. Phép
khởi tạo bảng băm cho các phần tử đầu chỉ đến phần tử cầm canh z
Procedure Initialize;
Var i:integer;
Begin

New(z);
Z^.next:=z;
For i:=1 to M-1 do
Begin
New(heads[i];
heads[i]^.next:=z;
end;
end;
c. Thêm một khóa mới vào bảng băm.
- Hàm Them_moi(k,heads[k mod M]) thực hiện thêm phần tử có khóa k vào danh sách liên
kết của bảng băm.
- Các danh sách liên kết thuộc vùng tràn đã có thứ tự.
- Nếu đã có nhiều phần tử cùng khóa thì phần tử mới là đầu bảng băm.
- Giá trị của hàm là địa chỉ của phần tử mới thêm vào.
Function THEM_MOI(k:integer; u:ref): ref;
Var x,p:ref;
Begin
Z^.key:=k; { gán k cho phần tử cầm canh}
{Timf khóa k trong danh sách liên kết có con trỏ đầu là p}
p:=u^.next { u là phần tử đứng ngay trước p}
while p.key<k do
begin
u:=p;
p:=p^.next;
end;
new(x);
x^.next:=p;
u^.next:=x;
x^.key:=k;







.
.




z



THEM_MOI:=x;
End;



x




Ghi chú: - Nút màu vàng là nút cuối cùng có khóa nhỏ hơn k.
- Nút màu xanh có khóa >= khoa k.
- Nút màu đỏ là nút x có khoa k.
d. Tìm kiềm khpóa trong bảng băm.
- Hàm TIM_KHOA(k,heads[k mod M]) thực hiện tìm phần tử đầu tiên có khóa k.

- Để tìm tiếp các phần tử tiếp có khóa k ta gán u:=TIM_KHOA(k,u) cho đến khi u=z.
- Giá trị của hàm là địa chỉ của phần tử tìm thấy hoặc phần tử cầm canh z.
Function TIM_KHOA(k:integer; u:ref): ref;
Var x,p:ref;
{Tìm một khóa trong ds liên kết}
Begin
Z^.key:=k; { gán k cho phần tử cầm canh}
repeat
u:=u^.next;
until k<=u^.key;
if k= u^.key then TIM_KHOA:=u
else TIM_KHOA:=z;
End;
Ví dụ. Xét ví dụ sau với M=11, giả sử các khóa được đưa vào một cách tuần tự:
Khóa: A S E A R C H I N G E X A M P L E
HASH: 1 8 5 1 7 3 8 9 3 7 5 2 1 2 5 1 5

z
A A A L z
M X z






.
.





























z










.
.



C N z
z
E E E P z
z
G R z
H S z
I z
z

3.3. Phương pháp nối kết hợp nhất.
a.Đạt vấn đề. Giả sử các khóa có giá trị hàm băm và giá trị thêm vào như sau:
Khóa: A B C D
Hash: 1 2 1 1
Khi thêm khóa A vào bảng vì H(A)=1 là vị trí trống nên ta thêm ngay tại vị trí số 2. Khi thêm
C vào bảng vì H(C)=1 trùng với H(A) nên đụng độ nên ta tìm kiếm vị trí trống đầu tiên từ
dưới lên để đặt C vào (ta đặt vào vị trí M) và cho next của vị trí 1 chỉ vào M. Khi thêm khóa
D, v ị trí của H(D)=1, vì rằng 1 đã chứa A ta đi theo vùng next của vị trí này và đến M
nhưng vì M đã chứa khoa C và vùng next của khóa này chưa có (0) nên ta thêm khóa D
vào vị trí M-1 và cho next của vị trí M chỉ đến vị trí M-1.


0
1
2



M-1
M

b. Khai báo và khởi tạo bảng băm.
- Const free= maxint;
Type=record
Key:integer;
Next:integer;
End;
Var T:aray[0 M] of Item;
r:integer;

A
B



.
.
D
C

×