Tại sao một tra cứu hashtable (ít va chạm) thực sự là O (1)?


10

Tuyên bố miễn trừ trách nhiệm: Tôi biết có những câu hỏi nghe có vẻ tương tự đã có ở đây và trên Stackoverflow. Nhưng tất cả chúng là về sự va chạm, đó không phải là điều tôi đang yêu cầu.

Câu hỏi của tôi là: tại sao đầu tiên lại ít va chạm - tra cứu O(1)?

Giả sử tôi có hashtable này:

Hash  Content
-------------
ghdjg Data1
hgdzs Data2
eruit Data3
xcnvb Data4
mkwer Data5
rtzww Data6

Bây giờ tôi đang tìm khóa kmà hàm băm h(k)cung cấp h(k) = mkwer. Nhưng làm thế nào để tra cứu "biết" rằng hàm băm mkwerở vị trí 5? Tại sao nó không phải cuộn qua tất cả các phím O(n)để tìm nó? Băm không thể là một số loại địa chỉ phần cứng thực sự vì tôi mất khả năng di chuyển dữ liệu xung quanh. Và theo như tôi biết, hashtable không được sắp xếp trên các giá trị băm (ngay cả khi nó là, tìm kiếm cũng sẽ mất O(log n))?

Làm thế nào để biết một hàm băm giúp tìm đúng vị trí trong bảng?

Câu trả lời:


24

Hàm băm không trả về một số chuỗi như mkwer. Nó trực tiếp trả về vị trí của mục trong mảng. Ví dụ, nếu bảng băm của bạn có mười mục, hàm băm sẽ trả về một số nguyên trong phạm vi 0.


1
Cảm ơn. :) Sai lầm của tôi là nghĩ về hàm băm có thể băm như MD5 hoặc SHA. Nhưng một hàm băm tất nhiên có thể là một vị trí nguyên, điều mà tôi không nghĩ tới. Bây giờ tôi biết phải tìm gì, tôi thậm chí đã nhanh chóng tìm thấy một ví dụ hay: hàm băm của PHP: github.com/php/php-src/blob/PHP-5.6.10/Zend/zend_hash.h#L237
Foo Bar

13
@FooBar: MD5 và SHA cũng tính toán các số đơn lẻ từ đầu vào, thật quá phổ biến để nói về các giá trị băm ở dạng hex. Cũng giống như địa chỉ bộ nhớ hiếm khi được xem xét trong thập phân.
nperson325681

4
Thêm vào đó, MD5, vv quá dài để được sử dụng trực tiếp như một chỉ mục mảng. Có thể sử dụng một phần của hàm băm, như các bit n thấp hơn .
chirlu

6

Hàm băm tính toán vị trí mảng từ chuỗi đã cho . Nếu đây là hàm băm hoàn hảo, điều đó có nghĩa là chắc chắn không có va chạm, mảng có lẽ lớn nhất ít nhất gấp đôi số lượng phần tử.


x=0;
x=xmod52

Hàm băm rất đơn giản này (hạn chế và dễ bị va chạm) khác với các giá trị băm khác trong cơ chế băm, không xem xét đầu vào đã cho. Trong sơ đồ nâng cao hơn, hàm băm là số lớn hơn, được điều chỉnh theo số phần tử. Băm hoàn hảo được tạo ra cho tất cả các đầu vào để đảm bảo không có va chạm.

O(1)

h(k)

nthn(sizeofelement)


1
Và làm thế nào để tra cứu biết vị trí trong bảng là băm? Nó không được đặt hàng hay địa chỉ phần cứng.
Foo Bar

h("xcnvb")=8

Nhưng không phải mọi chỉ số sẽ được lấp đầy. Nếu tôi có hàm băm 1, 4, 8, 90 và 223 chứa đầy dữ liệu, làm thế nào để tra cứu tìm đúng vị trí? Trong trường hợp thsi, chỉ số "90" ở vị trí 4 vì hầu hết các chỉ mục khác không tồn tại. Và một hashtable trống không có kích thước vô hạn có tất cả các vị trí có thể!?
Foo Bar

HaHa(h("xcnvb"))=Ha[90]

Hàm băm không trả về một chỉ mục vào mảng. Thay vào đó, nó trả về một số dự đoán có thể được ánh xạ vào mảng. Điều đó thường được thực hiện bằng cách sử dụng toán tử mô đun với số lượng bảng băm như toán hạng khác.
Christopher Schultz

3

Để mở rộng câu trả lời của David Richerby, thuật ngữ " hàm băm " hơi quá tải. Thông thường, khi chúng ta nói về một hàm băm, chúng ta nghĩ về MD5, SHA-1 hoặc một cái gì đó giống như .hashCode()phương thức của Java , biến một số đầu vào thành một số duy nhất. Tuy nhiên, miền của số này (nghĩa là giá trị tối đa) rất khó có thể có cùng kích thước với hàm băm mà bạn đang cố lưu trữ dữ liệu. (MD5 là 16 byte, SHA-1 là 20 byte và .hashCode()int- 4 byte).

Vì vậy, câu hỏi của bạn là về bước tiếp theo - một khi chúng ta có hàm băm có thể ánh xạ các đầu vào tùy ý thành các số, làm thế nào để chúng đưa chúng vào cấu trúc dữ liệu có kích thước cụ thể? Với một chức năng khác, còn được gọi là "hàm băm"!

Một ví dụ tầm thường của hàm như vậy là modulo ; bạn có thể dễ dàng ánh xạ một số kích thước tùy ý đến một chỉ mục cụ thể trong một mảng với modulo. Điều này được giới thiệu trong CLRS là "phương pháp phân chia":

kmkm

h(k)=km

...

mmm=2ph(k)pk

~ Giới thiệu về Thuật toán, §11.3.1 - CLRS

m

Java HashMapsử dụng một phiên bản sửa đổi của phương thức phân chia thực hiện bước tiền xử lý để tính đến các .hashCode()triển khai yếu để nó có thể sử dụng các mảng có kích cỡ bằng hai. Bạn có thể thấy chính xác những gì đang xảy ra trong .getEntry()phương thức (ý kiến ​​là của tôi):

 // hash() transforms key.hashCode() to protect against bad hash functions
 int hash = (key == null) ? 0 : hash(key.hashCode());
 // indexOf() converts the resulting hash to a value between 0 and table.length-1
 for (Entry<K,V> e = table[indexFor(hash, table.length)];
     ...

Java 8 mang theo một bản viết lại HashMapthậm chí còn nhanh hơn, nhưng khó đọc hơn một chút. Tuy nhiên, nó sử dụng cùng một nguyên tắc chung để tra cứu chỉ mục.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.