Tôi đang cố gắng hiểu bảng băm - ai đó có thể giải thích cho tôi - rõ ràng không?


25

Tôi muốn hiểu cách sử dụng và thực hiện đúng các bảng băm trong php (xin lỗi).

Tôi đọc ở đâu đó rằng một lập trình viên có kinh nghiệm đã tạo ra một bảng băm và sau đó lặp qua nó. Bây giờ, tôi hiểu tại sao điều đó là sai nhưng tôi hoàn toàn không có kiến ​​thức đầy đủ để biết liệu sự hiểu biết của tôi có đúng không (nếu bạn hiểu ý tôi là gì).

Vì vậy, ai đó có thể giải thích cho tôi cách triển khai bảng băm trong php (có lẽ là mảng kết hợp) và có lẽ quan trọng hơn là làm thế nào để truy cập các giá trị 'bằng hàm băm' và điều đó thực sự có nghĩa là gì?

Câu trả lời:


37

Tổng quan về bảng băm đơn giản

Là một trình làm mới, bảng băm là một cách để lưu trữ một giá trị dưới một khóa cụ thể trong cấu trúc dữ liệu. Chẳng hạn, tôi có thể lưu trữ giá trị "a"dưới khóa 1, và sau đó lấy lại nó bằng cách tra cứu khóa 1trong bảng băm.

Ví dụ đơn giản nhất về bảng băm mà tôi có thể nghĩ ra khỏi đỉnh đầu là bảng băm chỉ có thể lưu trữ số nguyên, trong đó khóa cho mục nhập bảng băm cũng là giá trị được lưu trữ. Giả sử bảng của bạn có kích thước 8 và về cơ bản, đó là một mảng trong bộ nhớ:

---------------------------------
|   |   |   |   |   |   |   |   |
---------------------------------
  0   1   2   3   4   5   6   7  

Hàm băm

Các hàm băm cung cấp cho bạn một chỉ mục về nơi lưu trữ giá trị của bạn. Một hàm băm khá đơn giản cho bảng này sẽ là thêm 1 vào giá trị bạn muốn lưu trữ, sau đó sửa đổi nó bằng 8 (kích thước bảng). Nói cách khác, hàm băm của bạn là (n+1)%8, nsố nguyên bạn muốn lưu trữ ở đâu.

Chèn

Nếu bạn muốn chèn một giá trị vào bảng băm này, bạn gọi hàm băm của mình (trong trường hợp này (n+1)%8) trên giá trị bạn muốn chèn để cung cấp cho bạn một chỉ mục. Chẳng hạn, nếu chúng ta muốn chèn 14, chúng ta sẽ gọi (14 + 1) % 8và lấy chỉ mục 7, vì vậy chúng ta sẽ chèn giá trị đó vào chỉ mục 7.

---------------------------------
|   |   |   |   |   |   |   |14 |
---------------------------------
  0   1   2   3   4   5   6   7  

Tương tự, chúng ta có thể chèn 33, 82 và 191 như vậy:

---------------------------------
|191|   |33 |82 |   |   |   |14 |
---------------------------------
  0   1   2   3   4   5   6   7  

Va chạm

Nhưng điều gì xảy ra nếu chúng ta cố gắng chèn một cái gì đó sẽ va chạm với một mục? 2 nên đi theo chỉ mục 3, nhưng nó được thực hiện bởi 82. Có nhiều cách để giải quyết vấn đề này, cách đơn giản nhất là gọi hàm băm của chúng ta nhiều lần cho đến khi chúng ta tìm thấy một khoảng trống.

Vì vậy, logic như sau:

  1. (2 + 1)% 8 = 3
  2. Chỉ số 3 đã đầy
  3. Cắm 3 trở lại vào chức năng băm của chúng tôi. ( 3 + 1)% 8 = 4 , trống.
  4. Đặt giá trị của chúng tôi vào chỉ số 4 .

Bây giờ bảng băm trông như thế này, với giá trị 2 được lưu trữ tại chỉ mục 4.

---------------------------------
|191|   |33 |82 |2  |   |   |14 |
---------------------------------
  0   1   2   3   4   5   6   7  

Nhược điểm của giải pháp này là khá sớm, bảng của chúng tôi sẽ đầy! Nếu bạn biết rằng kích thước dữ liệu của bạn bị giới hạn, thì đây không phải là vấn đề miễn là bảng của bạn đủ lớn để chứa tất cả các giá trị có thể. Nếu bạn muốn có thể giữ nhiều hơn, bạn có thể xử lý các va chạm khác nhau. Hãy quay trở lại nơi chúng ta đã ở trước khi chèn 2.

---------------------------------
|191|   |33 |82 |   |   |   |14 |
---------------------------------
  0   1   2   3   4   5   6   7  

Nếu bạn nhớ lại, (2+1)%8cung cấp cho chúng tôi chỉ số 3, được thực hiện. Nếu bạn không muốn bảng băm của mình lấp đầy, bạn có thể sử dụng từng chỉ mục bảng làm danh sách liên kết và nối vào danh sách tại chỉ mục đó. Vì vậy, thay vì gọi lại hàm băm, chúng ta chỉ cần thêm vào danh sách tại index 3:

            -----
            | 2 |
---------------------------------
|191|   |33 |82 |   |   |   |14 |
---------------------------------
  0   1   2   3   4   5   6   7  

Danh sách này sau đó có thể phát triển nhiều như bộ nhớ sẽ cho phép. Tôi có thể chèn 18 và nó sẽ được thêm vào 2:

            -----
            |18 |
            -----
            | 2 |
---------------------------------
|191|   |33 |82 |   |   |   |14 |
---------------------------------
  0   1   2   3   4   5   6   7  

Tra cứu

Tra cứu các giá trị trong bảng băm của bạn rất nhanh, với điều kiện là bảng băm của bạn có kích thước khá lớn. Bạn chỉ cần gọi hàm băm của bạn và lấy chỉ mục. Giả sử bạn muốn xem 82 có trong bảng của bạn không. Hàm tra cứu sẽ gọi (82+1)%8= 3và xem mục trong chỉ mục 3và trả lại cho bạn. Nếu bạn nhìn lên 16, chức năng tra cứu sẽ tìm trong chỉ mục 1và thấy rằng nó không tồn tại.

Tra cứu cũng cần xử lý Va chạm!

Nếu bạn cố gắng tra cứu giá trị 2, bảng băm của bạn sẽ phải sử dụng logic va chạm tương tự mà nó đã sử dụng để lưu trữ dữ liệu như để truy xuất dữ liệu. Tùy thuộc vào cách thức hoạt động của bảng băm của bạn, bạn sẽ băm khóa nhiều lần cho đến khi bạn tìm thấy mục bạn đang tìm kiếm (hoặc tìm một khoảng trống), hoặc bạn sẽ lặp qua danh sách được liên kết của mình cho đến khi bạn tìm thấy mục (hoặc đã đến cuối danh sách)

Tóm lược

Vì vậy, bảng băm là một cách tốt để lưu trữ và truy cập các cặp khóa-giá trị một cách nhanh chóng. Trong ví dụ này, chúng tôi đã sử dụng cùng khóa với giá trị, nhưng trong bảng băm trong thế giới thực, các khóa không bị giới hạn. Các hàm băm sẽ hoạt động trên các khóa để tạo một chỉ mục và sau đó khóa / giá trị có thể được lưu trữ tại chỉ mục đó. Các bảng băm không thực sự có nghĩa là được lặp đi lặp lại, mặc dù có thể làm như vậy. Như bạn có thể thấy, các bảng băm có thể có nhiều khoảng trống và việc lặp qua chúng sẽ gây lãng phí thời gian. Ngay cả khi bảng băm có logic để bỏ qua các tra cứu không gian trống trong trình lặp của nó, bạn sẽ phù hợp hơn khi sử dụng cấu trúc dữ liệu được thiết kế cho các trình lặp, như các danh sách được liên kết.


2
FTII nghệ thuật FTW!
Anto

2
Câu trả lời chính xác. Điều đáng nói là phương pháp mà mỗi chỉ mục là một danh sách được liên kết được gọi là chuỗi.
alexn

+1 Câu trả lời tuyệt vời, bật ra gần như mọi nghi ngờ ra khỏi đầu tôi. Cần hỏi thêm một câu nữa. Có phải mọi thực hiện sử dụng băm để lưu trữ số nguyên? hoặc điều này được sử dụng cho các trường hợp cụ thể? Nếu có, thì những trường hợp đó là gì?
0decimal0

@PHIfounder Tôi không chắc là tôi đã hiểu hoàn toàn câu hỏi của bạn chưa, nhưng hàm băm được thực hiện trên khóa được thiết kế chung chung, không chỉ áp dụng cho một loại dữ liệu cụ thể như số nguyên. Nếu chúng ta đang nói về mã C, bảng băm có thể được thiết kế để chấp nhận (void *) cho khóa và giá trị và thực hiện phép tính băm trên giá trị con trỏ của khóa.
Jeff

@Jeff thực sự tôi có thể là một kẻ ngốc khi hỏi điều này, nhưng tôi đang nói về cấu trúc bên trong của một máy tính; liệu mọi máy tính có sử dụng cấu trúc dữ liệu như bảng băm để lưu trữ lưu trữ tham chiếu đến số nguyên hay không bên trong?
0decimal0

7

Hãy tưởng tượng một thư viện với hàng ngàn cuốn sách. Bạn cần sắp xếp các cuốn sách để bạn có thể tìm thấy từng tiêu đề càng nhanh càng tốt.

Một cách (phổ biến) để làm điều này là sắp xếp các cuốn sách theo thứ tự abc. Nếu tiêu đề của bạn bắt đầu bằng chữ "G", bạn tìm thấy khu vực "G", sau đó tìm chữ cái thứ hai, nói "ö", sau đó "d", "e", "l", thu hẹp tìm kiếm của bạn, v.v. , cho đến khi bạn tìm thấy cuốn sách. Tuy nhiên, điều này có thể mất nhiều thời gian và bên cạnh đó, khi sách mới đến, đôi khi bạn cần sắp xếp lại bố cục của mình để nhường chỗ cho những người mới đến.

Đó là tìm kiếm nhị phân. Thật tốt

Tuy nhiên, có một cách nhanh hơn để làm điều này. Giả sử bạn liệt kê tất cả các tủ sách và giá sách, và sau đó với mỗi cuốn sách bạn tính một số đặc biệt, hy vọng là duy nhất, ánh xạ tới một kệ sách / kệ nơi tìm thấy cuốn sách. Cách bạn tính toán "chìa khóa" không quan trọng miễn là nó mang lại một con số trông ngẫu nhiên. Ví dụ: bạn có thể thêm mã ký tự của tất cả các chữ cái trong tiêu đề và sau đó chia số đó cho một số nguyên tố (có thể không phải là phương pháp tốt nhất, nhưng dù sao cũng hoạt động).

Đó là băm. Nó nhanh hơn nhiều, vì bạn không cần phải đi qua toàn bộ tủ sách và kệ tìm kiếm chữ cái tiếp theo trong tiêu đề. Băm thường là thao tác một lần, trừ khi bạn có "va chạm" khi hai hoặc nhiều sách giải quyết cùng một khóa. Nhưng không sao, bạn biết rằng chúng nằm cạnh nhau và tùy thuộc vào chất lượng của hàm băm, không nên có quá nhiều khóa dưới cùng một khóa.

Các bảng băm có một số hạn chế và ý tưởng bất chợt (cải tiến / thay đổi kích thước), giúp duy trì tìm kiếm nhị phân như một đối thủ cạnh tranh khả thi. Nó không phải là tất cả các màu đen và trắng liên quan đến phương pháp nào là tốt hơn. Nhưng đó là một câu chuyện khác nhau.

PS Xin lỗi vì đã không trả lời trực tiếp câu hỏi của bạn (viết bảng băm bằng PHP), nhưng đó là chi tiết và nó được gọi là "lập trình";)


2
Tôi thích giải thích không liên quan đến máy tính cho các vấn đề liên quan đến máy tính. +1
gablin

1

Bảng băm trong PHP, theo như kiến ​​thức của tôi, được thực hiện đơn giản thông qua:

$my_hash = array(
    1 => "Bob",
    2 => "Alice",
    3 => "Jack"
);

Sau đó, bạn truy cập dữ liệu qua các cuộc gọi như:

echo $my_hash[2]; // Will echo "Alice"

Bạn sử dụng hàm foreach () để lặp lại nội dung của mảng.

Cách tốt nhất để hiểu các bảng băm là đọc một cái gì đó như http://en.wikipedia.org/wiki/Hash_table , nhưng đại khái là nó hiểu rõ điều này: phía bên trái của mỗi dòng trong lệnh gọi mảng đó là các phím . Các khóa này sẽ được đưa vào thông qua tính toán băm và kết quả là băm. Bạn có thể đã thấy băm MD5 hoặc SHA trước đây, nó trông khá giống với điều này. Một phần cụ thể của hàm băm này, thường là các ký tự X đầu tiên nhưng đôi khi là hàm băm hoàn chỉnh, sẽ được sử dụng để xác định cái gọi là 'xô', là vùng lưu trữ cho các giá trị (phía bên tay phải).

Sau đó, bất cứ khi nào bạn truy cập hashtable của mình, bạn sử dụng khóa để lấy giá trị. Khóa được tính lại một hàm băm một lần nữa và hàm băm được sử dụng để nhanh chóng tra cứu giá trị liên quan. Vì vậy, bảng băm cho phép tìm kiếm nhanh hơn thay vì chỉ tìm kiếm tuyến tính nếu mọi thứ chỉ được lưu trữ. Nhược điểm duy nhất là một số triển khai hàm băm bị va chạm, đó là hàm băm được tính toán giống nhau cho hai khóa khác nhau. Nói chung, đó không phải là điều bạn phải lo lắng nhiều.

Tôi hy vọng điều này cung cấp một số nền tảng, nhưng vui lòng cố gắng đọc thêm về chủ đề nếu bạn quan tâm đến nó. Lời giải thích của tôi rất thô sơ và tôi chắc chắn có đủ lỗ hổng trong đó, nhưng nó đủ để giải thích nhanh chóng.

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.