Nếu trong bảng ta chưa một lần nào thực hiện phép toán loại, thì chúng ta xem xét các dữ liệu chứa trong mảng tại vị trí i và các vị trí tiếp theo trong dãy thăm dò, chúng ta sẽ pháthi[r]
(1)Chương III
Bảng Băm Mục tiêu
Trong chương này, nghiên cứu bảng băm Bảng băm cấu trúc liệu sử dụng để cài đặt KDL từ điển Nhớ lại rằng, KDL từ điển tập đối tượng liệu xem xét đến với ba phép tốn tìm kiếm, xen vào loại bỏ Đương nhiên cài đặt từ điển danh sách, tìm kiếm nhị phân Tuy nhiên bảng băm phương tiện hiệu để cài đặt từ điển
Kiến thức cần thiết
Để học tốt chương sinh viên cần phải nắm vững kỹ lập trình như: - Cấu trúc mảng, danh sách
- Các cấu trúc điều khiển, lệnh vòng lặp - Lập trình hàm, thủ tục, cách gọi hàm Nội dung
Trong chương này, đề cập tới vấn đề sau đây: - Phương pháp băm hàm băm
- Các chiến lược giải va chạm - Cài đặt KDL từ điển bảng băm I Phương pháp băm
Vấn đề đặt là, có tập liệu, cần đưa cấu trục liệu (CTDL) cài đặt tập liệu cho phép toán tìm kiếm, xen, loại thực hiệu Trong chương trước, trình bày phương pháp cài đặt KDL tập động (từ điển trường hợp riêng tập động mà quan tâm tới ba phép tốn tìm kiếm, xen, loại) Sau trình bày kỹ thuật để lưu giữ tập liệu, phương pháp băm
Nếu giá trị khoá liệu số nguyên không âm nằm khoảng [0 SIZE-1], sử dụng mảng data có cỡ SIZE để lưu tập liệu Dữ liệu có khố k lưu thành phần data[k] mảng Bởi mảng cho phép ta truy cập trực tiếp tới thành phần mảng theo số, phép tốn tìm kiếm, xen, loại thực thời gian O(1) Song đáng tiếc là, khố khơng phải số ngun, thơng thường khố cịn số thực, ký tự xâu ký tự Ngay khố số ngun, giá trị khố nói chung khơng chạy khoảng [0 SIZE-1]
(2)được vị trí mảng liệu lưu giữ Nếu đưa cách tính số mảng lưu liệu lưu tập liệu mảng theo sơ đồ Hình III.1
Hình III.1 Lược đồ phương pháp băm
Trong lược đồ Hình III.1, cho liệu có khố k, tính địa theo k ta thu số i, <= i <= SIZE-1, liệu lưu thành phần mảng T[i]
Một hàm ứng với giá trị khoá liệu với địa (chỉ số) liệu mảng gọi hàm băm (hash function) Phương pháp lưu tập liệu theo lược đồ gọi phương pháp băm (hashing) Trong lược đồ II.1, mảng T gọi bảng băm (hash table)
Như vậy, hàm băm ánh xạ h từ tập giá trị khoá liệu vào tập số nguyên {0,1,…, SIZE-1}, SIZE cỡ mảng dùng để lưu tập liệu, tức là:
h : K Ỉ {0,1,…,SIZE-1}
với K tập giá trị khoá Cho liệu có khố k, h(k) gọi giá trị băm khoá k, liệu lưu T[h(k)]
(3)1 Tính dễ dàng nhanh địa ứng với khố
2 Đảm bảo xảy va chạm
II. Các hàm băm
Trong hàm băm đưa đây, ký hiệu k giá trị khoá SIZE cỡ bảng băm Trước hết xét trường hợp giá trị khoá số nguyên không âm Nếu trường hợp (chẳng hạn, giá trị khoá xâu ký tự), cần chuyển đổi giá trị khố thành số ngun khơng âm, sau băm chúng phương pháp cho trường hợp khố số ngun
Có nhiều phương pháp thiết kế hàm băm đề xuất, sử dụng nhiều thực tế phương pháp trình bày sau đây:
1 Phương pháp chia
Phương pháp đơn giản lấy phần dư phép chia khoá k cho cỡ bảng băm SIZE làm giá trị băm: h(k) = k mod SIZE
Bằng cách này, giá trị băm h(k) số 0,1,…, SIZE-1 Hàm băm cài đặt C++ sau:
unsigned int hash(int k, int SIZE) {
return k % SIZE; }
Trong phương pháp này, để băm khoá k cần phép chia, hạn chế phương pháp để hạn chế xảy va chạm, cần phải biết cách lựa chọn cỡ bảng băm Các phân tích lý thuyết rằng, để hạn chế va chạm, sử dụng phương pháp băm nên lựa chọn SIZE số nguyên tố, tốt số nguyên tố có dạng đặc biệt, chẳng hạn có dạng 4k+3 Ví dụ, chọn SIZE = 811, 811 số nguyên tố 811 = 202 +
2 Phương pháp nhân
Phương pháp chia có ưu điểm đơn giản dễ dàng tính giá trị băm, song va chạm lại nhạy cảm với cỡ bảng băm Để hạn chế va chạm, sử dụng phương pháp nhân, phương pháp có ưu điểm phụ thuộc vào cỡ bảng băm
Phương pháp nhân tính giá trị băm khố k sau Đầu tiên, ta tính tích khoá k với số thực α, < α <1 Sau lấy phần thập phân tích αk nhân với SIZE, phần nguyên tích lấy làm giá trị băm khoá k Tức là:
(4)Chú ý rằng, phần thập phân tích αk, tức αk - ⎣ ⎦αk , số thực dương nhỏ Do tích phần thập phân với SIZE số dương nhỏ SIZE Từ đó, giá trị băm h(k) số nguyên 0,1,…, SIZE-
Để phân phối giá trị khoá vào vị trí bảng băm, thực tế người ta thường chọn số α sau:
Chẳng hạn, cỡ bảng băm SIZE = 1024 số α chọn trên, với k = 1849970, ta có:
3 Hàm băm cho giá trị khoá xâu ký tự
Để băm xâu ký tự, trước hết chuyển đổi xâu ký tự thành số
nguyên Các ký tự bảng mã ASCII gồm 128 ký tự đánh số từ đến 127, đo xâu ký tự xem số hệ đếm số 128 Áp dụng phương pháp chuyển đổi số hệ đếm sang số hệ đếm số 10, chuyển đổi xâu ký tựthành số nguyên Chẳng hạn, xâu “NOTE” chuyển thành số nguyên sau:
“NOTE” Ỉ ‘N’.1283 + ‘O’.1282 + ‘T’.128 + ‘E’ =
78.1283 + 79.1282 + 84.128 + 69 Vấn đề nảy sinh với cách chuyển đổi là, cần tính luỹ thừa 128, với xâu ký tự tương đối dài, kết nhận số nguyên cực lớn vượt khả biểu diễn máy tính
Trong thực tế, thông thường xâu ký tự tạo thành từ 26 chữ 10 chữ số, vài ký tự khác Do thay 128 37 tính số nguyên ứng với xâu ký tự theo luật Horner Chẳng hạn, số nguyên ứng với xâu ký tự “NOTE” tính sau:
“NOTE” Ỉ 78.373 + 79.372 + 84.37 + 69 =
((78.37 + 79).37 +84).37 +69
Sau chuyển đổi xâu ký tự thành số nguyênbằng phương pháp trên, áp dụng phương pháp chia để tính giá trị băm Hàm băm xâu ký tự cài đặt sau:
unsigned int hash(const string &k, int SIZE) {
unsigned int value = 0;
(5)III Các phương pháp giải va chạm
Trong mục II.2 trình bày phương pháp thiết kế hàm băm nhằm hạn chế xẩy va chạm Tuy nhiên ứng dụng, va chạm không tránh khỏi Chúng ta thấy rằng, cách giải va chạm ảnh hưởng trực tiếp đến hiệu phép toán từ điển bảng băm Trong mục trình bày hai phương pháp giải va chạm Trong phương pháp thứ nhất, xảy va chạm, tiến hành thăm dị để tìm vị trí cịn trống bảng đặt liệu vào Một phương pháp khác là, tạo cấu trúc liệu lưu giữ tất liệu băm vào vị trí bảng “gắn” cấu trúc liệu vào vị trí bảng
1 Phương pháp định địa mở
Trong phương pháp này, liệu lưu thành phần mảng, thành phần chứa liệu Vì thế, cần xen liệu với khoá k vào mảng, vị trí h(k) chứa liệu, tiến hành thăm dò số vị trí khác mảng để tìm vị trí cịn trống đặt liệu vào vị trí Phương pháp tiến hành thăm dị để phát vị trí trống gọi phương pháp định địa mở (open addressing)
Giả sử vị trí mà hàm băm xác định ứng với khố k i, i=h(k) Từ vị trí xem xét vị trí i0 , i1 , i2 ,…, im ,…
Trong i0 = i, im(m=0,1,2,…) vị trí thăm dị lần thứ m Dãy vị trí gọi dãy thăm dò Vấn đề đặt là, xác định dãy thăm dò nào? Sau trình bày số phương pháp thăm dị phân tích ưu khuyết điểm phương pháp
Thăm dị tuyến tính
Đây phương pháp thăm dị đơn giản dễ cài đặt Với khố k, giả sử vị trí xác định hàm băm i=h(k), dãy thăm dị i , i+1, i+2 , …
Như thăm dò tuyến tính có nghĩa xem xét vị trí tiếp liền kể từ vị trí ban đầu xác định hàm băm Khi cần xen vào liệu với khoá k, vị trí i = h(k) bị chiếm ta tìmđến vị trí liền sau đó, gặp vị trí cịn trống đặt liệu vào
(6)Hình III.2 Bảng băm sau xen vào liệu 38, 130, 13, 14 926
Bây xét xem, lưu tập liệu mảng phương pháp định địa mở phép tốn tìm kiếm, xen, loại tiến hành Các kỹ thuật tìm kiếm, xen, loại trình bày sử dụng cho phương pháp thăm dò Trước hết cần lưu ý rằng, để tìm, xen, loại phải sử dụng phương pháp thăm dò, chẳng hạn thăm dị tuyến tính Giả sử cần tìm liệu với khố k Đầu tiên cần băm khoá k, giả sử h(k)=i Nếu bảng ta chưa lần thực phép toán loại, xem xét liệu chứa mảng vị trí i vị trí dãy thăm dò, pháthiện liệu cần tìm vị trí dãy thăm dị, gặp vị trí trống dãy thăm dị dừng lại kết luận liệu cần tìm khơng có mảng Chẳng hạn muốn tìm xem mảng Hình III.2 có chứa liệu với khố 47? Bởi h(47) = 3, liệu lưu theo phương pháp thăm dị tuyến tính, nên xem xét vị trí 3, 4, Các vị trí chứa liệu khác với 47 Đến vị trí 6, mảng trống Vậy ta kết luận 47 khơng có mảng
Để loại liệu với khoá k, trước hết cần áp dụng thủ tục tìm kiếm trình bày để định vị liệu mảng Giả sử liệu lưu mảng vị trí p Loại liệu vị trí p cách nào? Nếu đặt vị trí p vị trí trống, tìm kiếm thăm dị gặp vị trí trống ta dừng đưa kết luận liệu khơng có mảng Chẳng hạn, mảng Hình III.2, ta loại liệu 388 cách xem vị trí trống, sau ta tìm liệu 926, h (926) = T[2] khơng chứa 926, tìmđến vị trí trống, ta khơng thể kết luận 926 khơng có mảng Thực tế 926 vị trí 5, lúc đưa 926 vào mảng vị trí 2, 3, bị chiếm Vì để đảm bảo thủ tục tìm kiếm trình bày cịn cho trường hợp thực phép toán loại, loại liệu vị trí p đặt vị trí p vị trí loại bỏ Như vậy, quan niệm vị trí i mảng (0 <= i <= SIZE-1) vị trí trống (EMPTY), vị trí loại bỏ (DELETED), vị trí chứa liệu (ACTIVE) Đương nhiên xen vào liệu mới, đặt vào vị trí loại bỏ Việc xen vào mảng liệu tiến hành cách xem xét vị trí dãy thăm dị ứng với khố liệu, gặp vị trí trống vị trí loại bỏ đặt liệu vào
Sau hàm thăm dị tuyến tính
int Probing (int i, int m, int SIZE) // SIZE cỡ mảng
(7)return (i+ m) % SIZE; }
Phương pháp thăm dò tuyến tính có ưu điểm cho phép ta xem xét tất vị trí mảng, phép tốn xen vào ln ln thực được, trừ mảng đầy Song nhược điểm phương pháp liệu tập trung thành đoạn, trình xen liệu vào, đoạn gộp thành đoạn dài Điều làm cho phép toán hiệu quả, chẳng hạn i = h(k) đầu đoạn, để tìm liệu với khoá k cần xem xét đoạn dài
Thăm dị bình phương
Để khắc phục tình trạng liệu tích tụ thành cụm phương pháp thăm dị tuyến tính, khơng thăm dị vị trí liền nhau, mà thăm dò bỏ chỗ theo quy luật
Trong thăm dị bình phương, vị trí ứng với khố k i = h(k), dãy thăm dị i , i+ 12 , i+ 22 ,… , i+ m2 ,…
Ví dụ Nếu cỡ mảng SIZE = 11, i = h(k) = 3, thăm dị bình phương cho phép ta tìmđến địa 3, 4, 7, 1,
Phương pháp thăm dị bình phương tránh tích tụ liệu thành đoạn tránh tìm kiếm đoạn Tuy nhiên nhược điểm khơng cho phép ta tìm đến tất vị trí mảng, chẳng hạn ví dụ trên, số 11 vị trí từ 0, 1, 2, …, 10, ta tìm đến vị trí 3, 4, 7, 1, Hậu điều là, phép tốn xen vào khơng thực được, mảng cịn vị trí khơng chứa
Băm kép
Phương pháp băm kép (double hashing) có ưu điểm thăm dị bình phương hạn chế tích tụ liệu thành cụm; chọn cỡ mảng số ngun tố, băm kép cịn cho phép ta thăm dị tới tất vị trí mảng Trong thăm dị tuyến tính thăm dị bình phương, vị trí thăm dị cách vị trí xuất phát khoảng cách hoàn toàn xác định trước khoảng cách khơng phụ thuộc vào khố Trong băm kép, sử dụng hai hàm băm h1 h2:
- Hàm băm h1 đóng vai trị hàm băm h phương pháp trước, xác định vị trí thăm dị
- Hàm băm h2 xác định bước thăm dò
Điều có nghĩa là, ứng với khố k, dãy thăm dò là: h1(k) + m h2(k), với m= 0, 1, 2, …
(8)Có thể chứng minh rằng, cỡ mảng bước thăm dị h2(k) ngun tố phương pháp băm kép cho phép ta tìm đến tất vị trí mảng Khẳng định lựa chọn cỡ mảng số nguyên tố
Ví dụ Giả sử SIZE = 11, hàm băm xác định sau: h1(k) = k % 11
h2(k) = + (k % 7)
với k = 58, bước thăm dò h2(58) = + = 3, dãy thăm dị là: h1(58) = 3, 6, 9, 1, 4, 7, 10, 2, 5, 8, với k = 36, bước thăm dị h2(36) = + = 2, dãy thăm dò 3, 5, 7, 9, 0, 2, 4, 6, 8, 10
Trong ứng dụng, chọn cỡ mảng SIZE số nguyên tố chọn M số nguyên tố, M < SIZE, sử dụng hàm băm
h1(k) = k % SIZE h2(k) = + (k % M)
2 Phương pháp tạo dây chuyền
Một cách tiếp cận khác để giải va chạm tạo cấu trúc liệu để lưu tất liệu băm vào vị trí mảng Cấu trúc liệu thích hợp danh sách liên kết (dây chuyền) Khi thành phần bảng băm T[i], với i = 0, 1, …, SIZE – 1, chứa trỏ trỏ tới đầu DSLK Cách giải va chạm gọi phương pháp tạo dây chuyền (separated chaining) Lược đồ lưu tập liệu bảng băm sử dụng phương pháp tạo dây chuyền mô tả Hình III.3
Hình III.3 Phương pháp tạo dây chuyền
(9)hiện phép toán tập động khác, chẳng hạn phép tốn Min (tìm liệu có khố nhỏ nhất), phép tốn DeleteMin (loại liệu có khoá nhỏ nhất), phép duyệt liệu Sau gọi bảng băm với giải va chạm phương pháp định địa mở bảng băm địa mở, bảng băm giải va chạm cách tạo dây chuyền bảng băm dây chuyền
IV Cài đặt bảng băm địa mở
Trong mục nghiên cứu cài đặt KDL từ điển bảng băm địa mở Chúng ta giả thiết rằng, liệu từ điển có kiểu Item đó, chúng chứa trường dùng làm khố tìm kiếm (trường key), giá trị khố có kiểu int Ngồi để đơn giản cho viết ta giả thiết rằng, truy cập trực tiếp trường key Như thảo luận mục III.1, bảng băm T, thành phần T[i], <= i <= SIZE -1, chứa hai biến: biến data để lưu liệu biến state để lưu trạng thái vị trí i, trạng thái vị trí i rỗng (EMPTY), chứa liệu (ACTIVE), loại bỏ (DELETED) Chúng ta cài đặt KDL lớp OpenHash phụ thuộc tham biến kiểu Item, lớp sử dụng hàm băm Hash hàm thăm dò Probing cung cấp Lớp OpenHash khai báo sau:
const int SIZE = 811;
enum stateType {ACTIVE, EMPTY, DELETED}; struct Entry
{
int data; stateType state; }
Entry T[SIZE];
void OpenHash(); // khởi tạo bảng băm rỗng bool Search(int k, Item & I) const;
// Tìm liệu có khố k
// Hàm trả true (false) tìm thấy (khơng tìm thấy) // Nếu tìm kiếm thành cơng, biếnI ghi lại liệu cần tìm void Insert(const Item & object, bool & Suc)
// Xen vào liệu object biến Suc nhận giá trị true // phép xen thành công, false thất bại void Delete(int k);
// Loại khỏi bảng băm liệu có khố k bool Find(int k, int & index, int & index1) const;
(10)// Nếu thành công, hàm trả true biến index ghi lại // số chứa liệu
// Nếu thất bại, hàm trả false biến index1 ghi lại // sốở trạng thái EMPTY DELETED thăm dò // phát
Để khởi tạo bảng băm rỗng sau:
for ( int i= ; i < SIZE ; i++ ) T[i].state = EMPTY;
Chú ý rằng, phép tốn tìm kiếm, xen, loại cần phải thực thăm dò để phát liệu cần tìm để phát vị trí rỗng (hoặc bị trí loại bỏ) để đưa vào liệu Sử dụng hàm Find ta dễ dàng cài đặt hàm Search, Insert Delete Trước hết cài đặt hàm Find Trong hàm Find mà q trình thăm dị phát vị trí rỗng có nghĩa bảng khơng chứa liệu cần tìm, song trước đạt tới vị trí rỗng ta phát vị trí loại bỏ, biến index1 ghi lại vị trí loại bỏ phát Cịn phát vị trí rỗng, trước ta khơng gặp vị trí loại bỏ nào, biến index1 ghi lại vị trí rỗng Hàm Find cài đặt sau:
bool Find(int k, int & index, int & index1) {
int i = Hash(k); index = 0; index1 = i;
for (int m= ; m< SIZE ; m++) {
int n = Probing(i,m); // vị trí thăm dị lần thứ m if (T[n].state = = ACTIVE && T[n].data= = k ) {
index = n; return true; }
else if (T[n].state = = EMPTY) {