Có thể thực hiện bảng băm phân phối tốt mà không cần sử dụng toán tử% không?


11

Tôi đang tìm cách triển khai bảng băm phân phối nhanh trong C #. Tôi gặp khó khăn khi chọn hàm ràng buộc băm của mình, lấy mã băm tùy ý và "ràng buộc" nó để nó có thể được sử dụng để lập chỉ mục các nhóm. Có hai lựa chọn mà tôi thấy cho đến nay:

  • Một mặt, bạn có thể đảm bảo các thùng của bạn luôn có số phần tử chính và để hạn chế hàm băm, bạn chỉ cần điều chỉnh nó theo số lượng thùng. Trên thực tế, đây là những gì Từ điển của .NET làm . Vấn đề với phương pháp này là việc sử dụng% cực kỳ chậm so với các hoạt động khác; nếu bạn nhìn vào các bảng hướng dẫn Sương mù Agner , idiv(mã lắp ráp được tạo cho%) có độ trễ lệnh ~ 25 chu kỳ cho các bộ xử lý Intel mới hơn. Hãy so sánh này lên khoảng 3 cho mul, hoặc 1 cho ops Bitwise như and, orhoặc xor.

  • Mặt khác, bạn có thể có số lượng xô luôn là lũy thừa là 2. Bạn vẫn sẽ phải tính mô-đun của hàm băm để bạn không cố gắng lập chỉ mục bên ngoài mảng, nhưng lần này sẽ ít tốn kém hơn . Vì quyền hạn của 2 % Nchỉ là & (N - 1), nên ràng buộc được giảm xuống thành một hoạt động mặt nạ chỉ mất 1-2 chu kỳ. Điều này được thực hiện bởi thưa thớt của Google . Nhược điểm của điều này là chúng tôi đang dựa vào người dùng để cung cấp băm tốt; che giấu băm về cơ bản cắt bỏ một phần của hàm băm, vì vậy chúng tôi không còn đưa tất cả các bit của hàm băm vào tài khoản. Nếu hàm băm của người dùng được phân phối không đều, ví dụ chỉ có các bit cao hơn được điền vào hoặc các bit thấp hơn luôn giống nhau, thì phương pháp này có tỷ lệ va chạm cao hơn nhiều.

Tôi đang tìm kiếm một thuật toán mà tôi có thể sử dụng có cả hai thế giới tốt nhất: nó đưa tất cả các bit của hàm băm vào tài khoản, và cũng nhanh hơn so với sử dụng%. Nó không nhất thiết phải là một mô-đun, chỉ cần một cái gì đó được đảm bảo trong phạm vi 0..N-1(trong đó N là chiều dài của các thùng) và có phân phối đồng đều cho tất cả các vị trí. Liệu một thuật toán như vậy tồn tại?

Cảm ơn đã giúp đỡ.


1
Tra cứu hiệu ứng tuyết lở , cũng như lời giải thích trong murmurhash3 (smhasher) . Tuy nhiên, điểm cơ bản trong câu hỏi của bạn không được giải quyết bằng cách áp dụng hàm băm tốt hơn. Thay vào đó, đó là một câu hỏi về lý do tại sao người dùng không áp dụng chức năng băm tốt hơn ở nơi đầu tiên và chào mời các biện pháp đối phó (như thể người dùng lười biếng một cách độc hại).
rwong


Để có modulo nhanh (2^N +/- 1), hãy xem stackoverflow.com/questions/763137/ từ
rwong

@rwong Tôi xin lỗi, nhưng tôi không chắc bình luận của bạn có liên quan gì đến bài viết của tôi. Tôi không kiểm soát hàm băm do người dùng cung cấp, vì vậy tôi không tìm kiếm hàm băm tốt hơn. Tôi cũng không hiểu ý của bạn là "người dùng lười biếng độc hại".
James Ko

4
Nếu hàm băm kém, thì người triển khai bảng băm không thể làm gì để "sửa" phân phối kém. Modulo một số nguyên tố không sửa chữa một hàm băm kém. Hãy xem xét một hàm băm sản xuất như đầu ra, bội số của một số nguyên tố. Tôi đã thấy một vấn đề như vậy trong mã sản xuất thực sự.
Frank Hileman

Câu trả lời:


9

Việc triển khai bảng băm hiện đại không sử dụng hàm modulo. Họ thường sử dụng sức mạnh của hai bảng có kích thước và cắt các bit không cần thiết. Một hàm băm lý tưởng sẽ cho phép điều này. Việc sử dụng modulo kết hợp với kích thước bảng số nguyên tố phát sinh trong những ngày mà hàm băm nói chung là kém, vì chúng thường được phát triển .net. Tôi khuyên bạn nên đọc về SipHash , một hàm băm hiện đại, sau đó đọc về một số hàm hiện đại khác, chẳng hạn như xxHash .

Tôi nên giải thích tại sao các hàm băm .net thường kém. Trong .net, các lập trình viên thường bị buộc phải thực hiện các hàm băm bằng cách ghi đè GetHashcode. Nhưng .net không cung cấp các công cụ cần thiết để đảm bảo các chức năng được lập trình viên tạo ra có chất lượng cao, cụ thể là:

  • đóng gói trạng thái băm trong một cấu trúc hoặc lớp
  • hàm băm "thêm", thêm dữ liệu mới vào trạng thái băm (ví dụ thêm một mảng byte hoặc gấp đôi)
  • một hàm "hoàn thiện" hàm băm, để tạo ra tuyết lở
  • đóng gói kết quả băm - trong .net bạn có một lựa chọn, số nguyên có chữ ký 32 bit.

Để biết thêm thông tin về việc sử dụng kết quả hàm băm dưới dạng chỉ mục bảng băm, vui lòng xem định nghĩa về các hình thức băm phổ biến trong bài viết này: Băm phổ biến 64 bit nhanh hơn bằng cách sử dụng phép nhân không mang theo


3

Để sử dụng AND trong khi vẫn giữ tất cả các bit, hãy sử dụng XOR.

Ví dụ , temp = (hash & 0xFFFF) ^ ( hash >> 16); index = (temp & 0xFF) ^ (temp >> 8);.

Trong ví dụ này, không có modulo và tất cả 32 bit hashcó hiệu lực 8 bit index. Tuy nhiên, việc nó có nhanh hơn DIV hay không là điều phụ thuộc vào quá nhiều yếu tố và nó có thể dễ dàng chậm hơn DIV trong một số trường hợp (ví dụ: hàm băm lớn và chỉ số nhỏ).


Điều này sẽ luôn nhanh hơn DIV / IDIV, tuy nhiên tôi không nghĩ nó trả lời câu hỏi của tôi-- indexsẽ nằm trong phạm vi [0..255]. Tôi cần một cái gì đó trong phạm vi [0..n-1], nsố xô là bao nhiêu.
James Ko

@JamesKo Nhưng nếu bạn đang thực hiện một từ điển, bạn cũng kiểm soát số lượng thùng (ở một mức độ nhất định). Vì vậy, thay vì số nguyên tố, bạn có thể chọn lũy thừa của hai. (Cho dù làm như vậy sẽ thực sự là một ý tưởng tốt, tôi không thể nói với bạn.)
svick

@svick Để có quyền hạn 2 chúng ta có thể thực hiện thao tác mặt nạ đơn giản. Như đã đề cập trong câu hỏi, tôi đang tìm kiếm một cách rẻ tiền để làm điều này với các số nguyên tố để ngay cả các giá trị băm phân phối kém cũng được sử dụng.
James Ko

1

Bạn có thể tận dụng thực tế là nhiều số nguyên tố có nghịch đảo mô đun nhân. Xem bài viết này . Bạn đã thỏa mãn một trong những ràng buộc bằng cách làm cho chỉ số xô của bạn trở thành số nguyên tố và mô đun 2 ^ n, vốn đã tương đối nguyên tố.

Bài viết mô tả thuật toán để tìm một số nhân với số đó và bỏ qua tràn, sẽ mang lại kết quả tương tự như khi bạn chia cho kích thước chỉ mục xô.

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.