Tại sao sử dụng số nguyên tố trong hashCode?


173

Tôi chỉ tự hỏi tại sao các số nguyên tố đó được sử dụng trong hashCode()phương thức của một lớp ? Ví dụ, khi sử dụng Eclipse để tạo hashCode()phương thức của tôi , luôn có số nguyên tố 31được sử dụng:

public int hashCode() {
     final int prime = 31;
     //...
}

Người giới thiệu:

Đây là một mồi tốt về Hashcode và bài viết về cách băm hoạt động mà tôi tìm thấy (C # nhưng các khái niệm có thể chuyển nhượng được): Nguyên tắc và quy tắc của Eric Lippert cho GetHashCode ()



Đây ít nhiều là một bản sao của stackoverflow.com/questions/1145217/ .
Hans-Peter Störr

1
Vui lòng kiểm tra câu trả lời của tôi tại stackoverflow.com/questions/1145217/ Từ Nó liên quan đến các thuộc tính của đa thức trên một trường (không phải là một vòng!), Do đó là số nguyên tố.
TT_

Câu trả lời:


103

Bởi vì bạn muốn số lượng bạn nhân với và số lượng xô bạn đang chèn vào để có các thừa số nguyên tố trực giao.

Giả sử có 8 thùng để chèn vào. Nếu số bạn đang sử dụng để nhân với một số bội số của 8, thì nhóm được chèn vào sẽ chỉ được xác định bởi mục nhập ít quan trọng nhất (số không được nhân lên nhiều). Các mục tương tự sẽ va chạm. Không tốt cho một hàm băm.

31 là một số nguyên tố đủ lớn để số lượng xô không thể chia hết cho nó (và trên thực tế, các triển khai java HashMap hiện đại giữ cho số lượng xô ở mức 2).


9
Sau đó, hàm băm nhân với 31 sẽ thực hiện không tối ưu. Tuy nhiên, tôi sẽ xem xét việc triển khai bảng băm như vậy được thiết kế kém, với mức độ phổ biến là 31 như một số nhân.
ILMTitan

11
Vì vậy, 31 được chọn dựa trên giả định rằng những người triển khai bảng băm biết rằng 31 thường được sử dụng trong mã băm?
Steve Kuo

3
31 được chọn dựa trên ý tưởng rằng hầu hết các triển khai có các yếu tố của các số nguyên tố tương đối nhỏ. 2s, 3s và 5s thường. Nó có thể bắt đầu từ 10 và tăng gấp 3 lần khi quá đầy. Kích thước hiếm khi hoàn toàn ngẫu nhiên. Và ngay cả khi nó là, 30/31 không phải là tỷ lệ cược xấu khi có thuật toán băm được đồng bộ hóa tốt. Nó cũng có thể dễ dàng để tính toán như những người khác đã nêu.
ILMTitan

8
Nói cách khác ... chúng ta cần biết một vài điều về tập hợp các giá trị đầu vào và tính đều đặn của tập hợp, để viết một hàm được thiết kế để loại bỏ chúng theo các quy tắc đó, vì vậy các giá trị trong tập hợp không va chạm vào nhau thùng băm. Nhân / Chia / Điều chỉnh theo số nguyên tố đạt được ảnh hưởng, bởi vì nếu bạn có LOOP với các mục X và bạn nhảy Y-space trong vòng lặp, thì bạn sẽ không bao giờ quay lại cùng một vị trí cho đến khi X trở thành nhân tố của Y Vì X thường là số chẵn hoặc lũy thừa bằng 2, nên bạn cần Y là số nguyên tố nên X + X + X ... không phải là một yếu tố của Y, vì vậy 31 yay! : /
Triynko

3
@FrankQ. Đó là bản chất của số học mô-đun. (x*8 + y) % 8 = (x*8) % 8 + y % 8 = 0 + y % 8 = y % 8
ILMTitan

135

Số nguyên tố được chọn để phân phối tốt nhất dữ liệu giữa các nhóm băm. Nếu phân phối đầu vào là ngẫu nhiên và trải đều, thì việc lựa chọn mã băm / mô đun không thành vấn đề. Nó chỉ có tác động khi có một mẫu nhất định cho các đầu vào.

Đây thường là trường hợp khi làm việc với các vị trí bộ nhớ. Ví dụ: tất cả các số nguyên 32 bit được căn chỉnh theo các địa chỉ chia hết cho 4. Kiểm tra bảng dưới đây để hình dung các tác động của việc sử dụng mô đun nguyên tố so với không chính:

Input       Modulo 8    Modulo 7
0           0           0
4           4           4
8           0           1
12          4           5
16          0           2
20          4           6
24          0           3
28          4           0

Lưu ý phân phối gần như hoàn hảo khi sử dụng mô đun nguyên tố so với mô đun không chính.

Tuy nhiên, mặc dù ví dụ trên phần lớn bị chiếm đoạt, nguyên tắc chung là khi xử lý một mô hình đầu vào , sử dụng mô đun số nguyên tố sẽ mang lại phân phối tốt nhất.


17
Không phải chúng ta đang nói về hệ số nhân được sử dụng để tạo mã băm chứ không phải modulo được sử dụng để sắp xếp các mã băm đó thành các nhóm?
ILMTitan

3
Nguyên tắc tương tự. Về mặt I / O, hàm băm đưa vào hoạt động modulo của bảng băm. Tôi nghĩ vấn đề là nếu bạn nhân các số nguyên tố, bạn sẽ nhận được nhiều đầu vào được phân phối ngẫu nhiên hơn đến điểm mà modulo thậm chí không quan trọng. Vì hàm băm thu được sự chậm chạp trong việc phân phối đầu vào tốt hơn, khiến chúng ít thường xuyên hơn, chúng ít có khả năng va chạm, bất kể modulo được sử dụng để đặt chúng vào thùng.
Triynko

9
Loại câu trả lời này rất hữu ích vì nó giống như dạy ai đó cách câu cá, thay vì bắt chúng cho chúng. Nó giúp mọi người nhìnhiểu nguyên tắc cơ bản đằng sau việc sử dụng các số nguyên tố cho băm ... đó là phân phối đầu vào không đều để chúng rơi đồng đều vào các thùng sau khi được điều chỉnh :).
Triynko

29

Để biết giá trị của nó, Java phiên bản 2 hiệu quả sẽ giải quyết vấn đề toán học và chỉ nói rằng lý do để chọn 31 là:

  • Bởi vì đó là một số nguyên tố kỳ lạ và sử dụng các số nguyên tố "truyền thống"
  • Nó cũng là một ít hơn một sức mạnh của hai, cho phép tối ưu hóa bitwise

Đây là trích dẫn đầy đủ, từ Mục 9: Luôn ghi đè hashCodekhi bạn ghi đèequals :

Giá trị 31 được chọn vì đó là số nguyên tố lẻ. Nếu nó là số chẵn và phép nhân bị tràn, thông tin sẽ bị mất, vì phép nhân với 2 tương đương với sự dịch chuyển. Ưu điểm của việc sử dụng một số nguyên tố là ít rõ ràng, nhưng nó là truyền thống.

Một đặc tính tốt của 31 là phép nhân có thể được thay thế bằng một ca ( §15.19 ) và phép trừ để có hiệu suất tốt hơn:

 31 * i == (i << 5) - i

Máy ảo hiện đại thực hiện loại tối ưu hóa này tự động.


Mặc dù công thức trong mục này mang lại các hàm băm khá hợp lý, nhưng nó không mang lại các hàm băm hiện đại, cũng như các thư viện nền tảng Java cung cấp các hàm băm như vậy kể từ phiên bản 1.6. Viết các hàm băm như vậy là một chủ đề nghiên cứu, tốt nhất để lại cho các nhà toán học và các nhà khoa học máy tính lý thuyết.

Có lẽ một bản phát hành sau này của nền tảng sẽ cung cấp các hàm băm tối tân cho các lớp và phương thức tiện ích của nó để cho phép các lập trình viên trung bình xây dựng các hàm băm như vậy. Trong khi đó, các kỹ thuật được mô tả trong mục này phải phù hợp với hầu hết các ứng dụng.

Khá đơn giản, có thể nói rằng việc sử dụng một số nhân với nhiều ước số sẽ dẫn đến nhiều va chạm băm hơn . Vì để băm hiệu quả, chúng tôi muốn giảm thiểu số lần va chạm, chúng tôi cố gắng sử dụng hệ số nhân có ít ước số hơn. Một số nguyên tố theo định nghĩa có chính xác hai ước số riêng biệt, tích cực.

Câu hỏi liên quan


4
Ơ, nhưng có nhiều số nguyên tố phù hợp là 2 ^ n + 1 (còn gọi là số nguyên tố Fermat ), tức là 3, 5, 17, 257, 65537hoặc 2 ^ n - 1 ( số nguyên tố Mersenne ) : 3, 7, 31, 127, 8191, 131071, 524287, 2147483647. Tuy nhiên 31(và không, nói, 127) được chọn.
Dmitry Bychenko

4
"bởi vì đó là một số nguyên tố kỳ lạ" ... chỉ có một số nguyên tố chẵn: P
Martin Schneider

Tôi không thích từ ngữ "ít rõ ràng hơn, nhưng nó là truyền thống" trong "Java hiệu quả". Nếu anh ta không muốn đi sâu vào các chi tiết toán học, anh ta nên viết một cái gì đó như "có lý do toán học [tương tự]". Cách anh ta viết có vẻ như chỉ có bối cảnh lịch sử :(
Qw3ry

5

Tôi nghe nói rằng 31 đã được chọn để trình biên dịch có thể tối ưu hóa phép nhân thành 5 bit dịch chuyển trái sau đó trừ đi giá trị.


Làm thế nào trình biên dịch có thể tối ưu hóa theo cách đó? x * 31 == x * 32-1 không đúng với mọi x sau đó. Những gì bạn có nghĩa là trái 5 (nhân với 32) và sau đó trừ đi giá trị ban đầu (x trong ví dụ của tôi). Trong khi điều này có thể là nhanh hơn sau đó một phép nhân (nó probaly không phải dành cho bộ vi xử lý CPU hiện đại bằng cách này), có những yếu tố quan trọng hơn cần xem xét khi lựa chọn một nhân cho một haschcode (phân phối bằng các giá trị đầu vào xô nói đến cái tâm)
Grizzly

Làm một chút tìm kiếm, đây là một ý kiến ​​khá phổ biến.
Steve Kuo

4
Ý kiến ​​chung là không liên quan.
fractor

1
@Grizzly, nó nhanh hơn so với phép nhân. IMul ​​có độ trễ tối thiểu 3 chu kỳ trên bất kỳ cpu hiện đại nào. (xem hướng dẫn sử dụng sương mù agner) mov reg1, reg2-shl reg1,5-sub reg1,reg2có thể thực hiện trong 2 chu kỳ. (Mov chỉ là một đổi tên và mất 0 chu kỳ).
Johan

3

Đây là một trích dẫn gần hơn một chút với nguồn.

Nó đun sôi xuống:

  • 31 là số nguyên tố, giúp giảm va chạm
  • 31 tạo ra một bản phân phối tốt, với
  • một sự đánh đổi hợp lý về tốc độ

3

Trước tiên, bạn tính giá trị băm modulo 2 ^ 32 (kích thước của một int), vì vậy bạn muốn một cái gì đó tương đối nguyên tố thành 2 ^ 32 (tương đối nguyên tố có nghĩa là không có ước số chung). Bất kỳ số lẻ sẽ làm điều đó.

Sau đó, đối với một bảng băm nhất định, chỉ mục thường được tính từ giá trị băm modulo kích thước của bảng băm, vì vậy bạn muốn một cái gì đó tương đối nguyên tố với kích thước của bảng băm. Thông thường kích thước của bảng băm được chọn làm số nguyên tố vì lý do đó. Trong trường hợp Java, việc triển khai Mặt trời đảm bảo rằng kích thước luôn luôn là hai lũy thừa, do đó, một số lẻ cũng đủ ở đây. Ngoài ra còn có một số thao tác xoa bóp bổ sung các phím băm để hạn chế va chạm hơn nữa.

Ảnh hưởng xấu nếu bảng băm và hệ số nhân có một yếu tố chung ncó thể là trong một số trường hợp nhất định, chỉ có 1 / n mục trong bảng băm sẽ được sử dụng.


2

Lý do tại sao các số nguyên tố được sử dụng là để giảm thiểu va chạm khi dữ liệu thể hiện một số mẫu cụ thể.

Điều đầu tiên trước tiên: Nếu dữ liệu là ngẫu nhiên thì không cần số nguyên tố, bạn có thể thực hiện thao tác mod với bất kỳ số nào và bạn sẽ có cùng số lần va chạm cho mỗi giá trị có thể của mô đun.

Nhưng khi dữ liệu không ngẫu nhiên thì những điều kỳ lạ xảy ra. Ví dụ, hãy xem xét dữ liệu số luôn là bội số của 10.

Nếu chúng tôi sử dụng mod 4, chúng tôi thấy:

10 mod 4 = 2

20 mod 4 = 0

30 mod 4 = 2

40 mod 4 = 0

50 mod 4 = 2

Vì vậy, từ 3 giá trị có thể của mô đun (0,1,2,3) chỉ có 0 và 2 sẽ có va chạm, điều đó thật tệ.

Nếu chúng ta sử dụng số nguyên tố như 7:

10 mod 7 = 3

20 mod 7 = 6

30 mod 7 = 2

40 mod 7 = 4

50 mod 7 = 1

Vân vân

Chúng tôi cũng lưu ý rằng 5 không phải là một lựa chọn tốt nhưng 5 là nguyên nhân vì tất cả các khóa của chúng tôi là bội số của 5. Điều này có nghĩa là chúng tôi phải chọn một số nguyên tố không chia các khóa của chúng tôi, chọn một số nguyên tố lớn là thường là đủ

Vì vậy, lỗi ở khía cạnh lặp đi lặp lại, lý do các số nguyên tố được sử dụng là để vô hiệu hóa hiệu ứng của các mẫu trong các khóa trong phân phối va chạm của hàm băm.


1

31 cũng đặc trưng cho Java HashMap, sử dụng kiểu dữ liệu int dưới dạng băm. Do đó, công suất tối đa là 2 ^ 32. Không có điểm nào trong việc sử dụng các số nguyên tố Fermat hoặc Mersenne lớn hơn.


0

Nó thường giúp đạt được sự lan truyền dữ liệu của bạn nhiều hơn trong các nhóm băm, đặc biệt là đối với các khóa entropy thấp.

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.