Tại sao hàm băm nên sử dụng mô đun số nguyên tố?


335

Cách đây rất lâu, tôi đã mua một cuốn sách cấu trúc dữ liệu ngoài bảng giá hời với giá $ 1,25. Trong đó, lời giải thích cho hàm băm nói rằng cuối cùng nó nên sửa đổi một số nguyên tố vì "bản chất của toán học".

Bạn mong đợi gì từ một cuốn sách $ 1,25?

Dù sao, tôi đã có nhiều năm để suy nghĩ về bản chất của toán học, và vẫn không thể hiểu được.

Là sự phân phối số thực sự nhiều hơn ngay cả khi có một số nguyên tố xô? Hay đây là câu chuyện của một lập trình viên cũ mà mọi người đều chấp nhận vì mọi người khác chấp nhận nó?


1
Câu hỏi hoàn toàn hợp lý: Tại sao nên có số nguyên tố xô?
Draemon

1
Câu hỏi này dường như lạc đề vì nhiều khả năng nó thuộc về Khoa học Máy tính .
Các cuộc đua nhẹ nhàng trong quỹ đạo

2
cs.stackexchange.com/a/64191/64222 một lời giải thích khác cũng được tranh luận.
Cây xanh


Đây là một lời giải thích tuyệt vời khác cho một câu hỏi có liên quan với một số con số đáng kinh ngạc - quora.com/ Kẻ
AnBisw

Câu trả lời:


242

Thông thường, hàm băm đơn giản hoạt động bằng cách lấy "các bộ phận thành phần" của đầu vào (các ký tự trong trường hợp của chuỗi) và nhân chúng với các lũy thừa của một số hằng và cộng chúng lại với nhau trong một số kiểu nguyên. Vì vậy, ví dụ, hàm băm điển hình (mặc dù không đặc biệt tốt) có thể là:

(first char) + k * (second char) + k^2 * (third char) + ...

Sau đó, nếu một chuỗi các chuỗi có cùng char đầu tiên được đưa vào, thì tất cả các kết quả sẽ là cùng một modulo k, ít nhất là cho đến khi kiểu số nguyên tràn ra.

[Ví dụ, chuỗi hashCode của Java tương tự như thế này - nó thực hiện đảo ngược các ký tự, với k = 31. Vì vậy, bạn có được các mối quan hệ nổi bật modulo 31 giữa các chuỗi kết thúc theo cùng một cách và các mối quan hệ nổi bật modulo 2 ^ 32 giữa các chuỗi giống nhau ngoại trừ gần cuối. Điều này không gây rối nghiêm trọng cho hành vi hashtable.]

Một hashtable hoạt động bằng cách lấy mô-đun của hàm băm qua số lượng xô.

Điều quan trọng trong một hashtable là không tạo ra va chạm cho các trường hợp có khả năng, vì các va chạm làm giảm hiệu quả của hashtable.

Bây giờ, giả sử ai đó đặt cả đống giá trị vào một hashtable có mối quan hệ nào đó giữa các mục, giống như tất cả đều có cùng một ký tự đầu tiên. Đây là một mô hình sử dụng khá dễ đoán, tôi muốn nói, vì vậy chúng tôi không muốn nó tạo ra quá nhiều va chạm.

Nó chỉ ra rằng "vì bản chất của toán học", nếu hằng số được sử dụng trong hàm băm và số lượng xô là đồng thời , thì các va chạm được giảm thiểu trong một số trường hợp phổ biến. Nếu họ không phải là nguyên tố cùng nhau, sau đó có một số mối quan hệ khá đơn giản giữa các đầu vào mà các va chạm không được giảm thiểu. Tất cả các giá trị băm đều xuất hiện modulo bằng nhau, yếu tố chung, có nghĩa là tất cả chúng sẽ rơi vào 1 / n của các thùng có giá trị modulo đó là yếu tố chung. Bạn nhận được gấp n lần số lần va chạm, trong đó n là yếu tố phổ biến. Vì n ít nhất là 2, nên tôi không thể chấp nhận trường hợp sử dụng khá đơn giản để tạo ra ít nhất gấp đôi số lần va chạm so với bình thường. Nếu một số người dùng sẽ phá vỡ phân phối của chúng tôi thành các thùng, chúng tôi muốn đó là một tai nạn kỳ quặc, không phải là một cách sử dụng đơn giản có thể dự đoán được.

Bây giờ, việc triển khai hashtable rõ ràng không có quyền kiểm soát đối với các mục được đưa vào chúng. Họ không thể ngăn họ liên quan. Vì vậy, điều cần làm là đảm bảo rằng hằng số và số lượng xô là nguyên tố cùng nhau. Bằng cách đó, bạn không chỉ dựa vào thành phần "cuối cùng" để xác định mô đun của thùng đối với một số yếu tố chung nhỏ. Theo như tôi biết thì họ không cần phải thành thạo để đạt được điều này, chỉ là đồng thời.

Nhưng nếu hàm băm và hàm băm được viết độc lập, thì hàm băm không biết hàm băm hoạt động như thế nào. Nó có thể được sử dụng một hằng số với các yếu tố nhỏ. Nếu bạn may mắn, nó có thể hoạt động hoàn toàn khác và là phi tuyến. Nếu băm là đủ tốt, thì bất kỳ số lượng xô là tốt. Nhưng một hashtable hoang tưởng không thể đảm nhận chức năng băm tốt, vì vậy nên sử dụng số nguyên tố lớn nhất. Tương tự, hàm băm hoang tưởng nên sử dụng hằng số nguyên tố lớn, để giảm khả năng ai đó sử dụng một số nhóm xảy ra có một yếu tố chung với hằng số.

Trong thực tế, tôi nghĩ việc sử dụng sức mạnh bằng 2 là số lượng xô là khá bình thường. Điều này là thuận tiện và tiết kiệm phải tìm kiếm xung quanh hoặc chọn trước một số nguyên tố có độ lớn phù hợp. Vì vậy, bạn dựa vào hàm băm không sử dụng nhiều số nhân, mà nói chung là một giả định an toàn. Nhưng bạn vẫn có thể có các hành vi băm không thường xuyên dựa trên các hàm băm như ở trên và số lượng nguyên tố có thể giúp thêm.

Đặt ra nguyên tắc rằng "mọi thứ phải là chính" theo như tôi biết là một điều kiện đủ nhưng không phải là điều kiện cần thiết để phân phối tốt trên các hashtag. Nó cho phép mọi người tương tác với nhau mà không cần phải cho rằng những người khác đã tuân theo quy tắc tương tự.

[Chỉnh sửa: có một lý do khác, chuyên biệt hơn để sử dụng số lượng lớn các nhóm, đó là nếu bạn xử lý các va chạm với thăm dò tuyến tính. Sau đó, bạn tính toán một bước tiến từ mã băm và nếu bước tiến đó là một yếu tố của số lượng xô thì bạn chỉ có thể thực hiện (buck_count / stride) trước khi bạn quay lại nơi bạn bắt đầu. Trường hợp bạn muốn tránh nhất là stride = 0, tất nhiên, phải là trường hợp đặc biệt, nhưng để tránh trường hợp đặc biệt xô_count / stride bằng một số nguyên nhỏ, bạn chỉ có thể tạo số nguyên tố xô_count và không quan tâm điều gì sải chân được cung cấp không phải là 0.]


Cũng như một ghi chú bên lề: một cuộc thảo luận về sự lựa chọn hợp lý của yếu tố k cho hashCodes có ở đây: stackoverflow.com/q/1835976/21499
Hans-Peter Störr

9
đây là một câu trả lời tuyệt vời bạn có thể vui lòng giải thích thêm về vấn đề này không " Tôi đặc biệt không hiểu phần 2 ^ 32
bình thường

2
Lưu ý bổ sung để làm cho mọi thứ rõ ràng hơn về điều này: lấy các chuỗi có cùng ký tự đầu tiên, hàm băm% k sẽ giống nhau cho các chuỗi này. Nếu M là kích thước của hàm băm và g là gcd của M và k, thì (băm% k)% g bằng băm% g (vì g chia k) và do đó băm% g cũng sẽ giống nhau cho các chuỗi này. Bây giờ hãy xem xét (băm% M)% g, điều này bằng với băm% g (vì g chia M). Vì vậy (băm% M)% g bằng với tất cả các chuỗi này.
Quark

1
@DanielMcLaury Joshua Bloch đã giải thích lý do tại sao cho Java - nó được đề xuất trong hai cuốn sách phổ biến (K & R, cuốn sách Dragon) và hoạt động tốt với các va chạm thấp trong từ điển tiếng Anh. Nó rất nhanh (sử dụng phương pháp của Horner ). Rõ ràng ngay cả K & R cũng không nhớ nó đến từ đâu. Chức năng tương tự là dấu vân tay Rabin từ thuật toán Rabin-Karp (1981) nhưng K & R (1978) có trước đó.
Bain

1
@SteveJessop, xin vui lòng bạn có thể giải thích "mối quan hệ nổi bật modulo 2 ^ 32 giữa các chuỗi giống nhau ngoại trừ gần cuối."? Cảm ơn.
Khanna111

29

Điều đầu tiên bạn làm khi chèn / truy xuất từ ​​bảng băm là tính toán mã băm cho khóa đã cho và sau đó tìm nhóm chính xác bằng cách cắt mã băm theo kích thước của hàm băm bằng cách thực hiện hàm hashCode% table_length. Dưới đây là 2 'tuyên bố' mà bạn có lẽ đã đọc ở đâu đó

  1. Nếu bạn sử dụng công suất 2 cho table_length, việc tìm kiếm (hashCode (khóa)% 2 ^ n) đơn giản và nhanh chóng như (hashCode (khóa) & (2 ^ n -1)). Nhưng nếu chức năng tính toán mã băm của bạn cho một khóa nhất định không tốt, bạn chắc chắn sẽ phải chịu nhiều cụm khóa trong một vài nhóm băm.
  2. Nhưng nếu bạn sử dụng các số nguyên tố cho table_length, hashCodes được tính toán có thể ánh xạ vào các nhóm băm khác nhau ngay cả khi bạn có hàm hashCode hơi ngu ngốc.

Và đây là bằng chứng.

Nếu giả sử hàm hashCode của bạn dẫn đến các mã băm sau trong số các hàm khác {x, 2x, 3x, 4x, 5x, 6x ...}, thì tất cả những thứ này sẽ được nhóm lại chỉ trong m số xô, trong đó m = table_length / GreatestCommonFactor (bảng_length, x). (Việc xác minh / rút ra điều này là không quan trọng). Bây giờ bạn có thể thực hiện một trong những cách sau để tránh phân cụm

Đảm bảo rằng bạn không tạo quá nhiều mã băm là bội số của mã băm khác như trong {x, 2x, 3x, 4x, 5x, 6x ...}. Nhưng điều này có thể khó khăn nếu hashTable của bạn được cho là có hàng triệu mục. Hoặc đơn giản là làm cho m bằng với bảng_length bằng cách tạo GreatestCommonFactor (table_length, x) bằng 1, tức là bằng cách tạo bảng tương ứng với bảng_length với x. Và nếu x có thể chỉ là về bất kỳ số nào thì hãy chắc chắn rằng bảng_length là số nguyên tố.

Từ - http://srinvis.blogspot.com/2006/07/hash-table-lengths-and-prime-numbers.html


11

http://computinglife.wordpress.com/2008/11/20/why-do-hash-fifts-use-prime-numbers/

Giải thích khá rõ ràng, với hình ảnh quá.

Chỉnh sửa: Tóm lại, các số nguyên tố được sử dụng vì bạn có cơ hội tốt nhất để có được một giá trị duy nhất khi nhân giá trị với số nguyên tố được chọn và cộng tất cả chúng lên. Ví dụ, được cung cấp một chuỗi, nhân mỗi giá trị chữ cái với số nguyên tố và sau đó thêm tất cả các giá trị đó sẽ cung cấp cho bạn giá trị băm của nó.

Một câu hỏi tốt hơn sẽ là, tại sao chính xác là số 31?


5
Mặc dù, tôi nghĩ rằng một bản tóm tắt sẽ hữu ích, trong trường hợp trang web đó đã chết, một số nội dung còn lại của nó sẽ được lưu ở đây trên SO.
Thomas Owens

2
Bài báo không giải thích lý do tại sao, nhưng nói rằng "Các nhà nghiên cứu nhận thấy rằng việc sử dụng số nguyên tố 31 giúp phân phối tốt hơn cho các phím và không có va chạm. Không ai biết tại sao ..." Hài hước, hỏi cùng một câu hỏi như tôi. .
theschmitzer

> Một câu hỏi tốt hơn sẽ là, tại sao chính xác là số 31? Nếu bạn muốn nói tại sao số 31 được sử dụng, thì bài viết bạn chỉ cho bạn biết lý do tại sao, vì nó nhanh chóng được nhiều người kiểm tra và cos cho thấy nó là bài tốt nhất để sử dụng. Hệ số nhân phổ biến khác mà tôi đã thấy là 33 có trọng số cho lý thuyết rằng vấn đề tốc độ (ít nhất là ban đầu) là một yếu tố quan trọng. Nếu bạn có ý nghĩa, những gì về 31 làm cho nó tốt hơn trong các bài kiểm tra, thì tôi sợ tôi không biết.
sgmoore

Chính xác, vì vậy lý do duy nhất mà nó có thể được sử dụng như một số nhân là vì nó dễ nhân lên. (Khi tôi nói rằng tôi đã thấy 33 được sử dụng như một số nhân, ý tôi là gần đây, điều này có lẽ đã xảy ra cách đây hàng thập kỷ và có thể trước khi rất nhiều phân tích được thực hiện về băm).
sgmoore

3
@SteveJessop Số 31 dễ dàng được CPU tối ưu hóa dưới dạng hoạt động (x * 32) -1, trong đó *32là một sự thay đổi bit đơn giản, hoặc thậm chí tốt hơn là một hệ số tỷ lệ địa chỉ tức thời (ví dụ lea eax,eax*8; leax, eax,eax*4trên x86 / x64). Vì vậy, *31là một ứng cử viên tốt cho phép nhân số nguyên tố. Điều này khá đúng trong vài năm trước - bây giờ kiến ​​trúc CPU mới nhất có sự nhân lên gần như ngay lập tức - sự phân chia luôn chậm hơn ...
Arnaud Bouchez

10

tl; dr

index[hash(input)%2]sẽ dẫn đến xung đột trong một nửa số băm có thể và một loạt các giá trị. index[hash(input)%prime]dẫn đến va chạm <2 trong số tất cả các băm có thể. Việc sửa số chia cho kích thước bảng cũng đảm bảo rằng số không thể lớn hơn bảng.


1
2 là một anh chàng số nguyên tố
Ganesh Chowdhary Sadanala

8

Các số nguyên tố được sử dụng bởi vì bạn có cơ hội tốt để có được một giá trị duy nhất cho hàm băm điển hình sử dụng đa thức modulo P. Giả sử, bạn sử dụng hàm băm đó cho các chuỗi có độ dài <= N và bạn có một xung đột. Điều đó có nghĩa là 2 đa thức khác nhau tạo ra cùng một giá trị modulo P. Sự khác biệt của các đa thức đó lại là một đa thức có cùng độ N (hoặc ít hơn). Nó không có nhiều hơn N gốc (đây là bản chất của toán học, vì yêu cầu này chỉ đúng với đa thức trên một trường => số nguyên tố). Vì vậy, nếu N nhỏ hơn P rất nhiều, bạn có khả năng không bị va chạm. Sau đó, thử nghiệm có thể cho thấy 37 đủ lớn để tránh va chạm cho bảng băm có độ dài 5-10 và đủ nhỏ để sử dụng cho các phép tính.


1
Mặc dù lời giải thích dường như đã rõ ràng, nhưng nó đã đến với tôi sau khi đọc một cuốn sách của A.Shen "Lập trình: Các định lý và vấn đề" (bằng tiếng Nga), xem thảo luận về thuật toán Rabin. Không chắc chắn nếu một bản dịch tiếng Anh tồn tại.
TT_

5

Chỉ để cung cấp một quan điểm thay thế có trang web này:

http://www.codexon.com/posts/hash-fifts-the-modulo-prime-myth

Điều này cho thấy rằng bạn nên sử dụng số lượng thùng lớn nhất có thể thay vì làm tròn xuống số thùng chính. Có vẻ như một khả năng hợp lý. Theo trực giác, tôi chắc chắn có thể thấy số lượng thùng lớn hơn sẽ tốt hơn như thế nào, nhưng tôi không thể đưa ra lập luận toán học về điều này.


Số lượng thùng lớn hơn có nghĩa là ít va chạm: Xem nguyên tắc pigeonhole.
Không biết

11
@Un Unknown: Tôi không tin đó là sự thật. Vui lòng sửa lại cho tôi nếu tôi sai, nhưng tôi tin rằng việc áp dụng nguyên tắc pigeonhole cho các bảng băm chỉ cho phép bạn khẳng định rằng SILL có va chạm nếu bạn có nhiều yếu tố hơn thùng, không đưa ra bất kỳ kết luận nào về số lượng hoặc mật độ va chạm. Tuy nhiên, tôi vẫn tin rằng số lượng thùng lớn hơn là đúng tuyến.
Falaina

Nếu bạn cho rằng các va chạm là dành cho tất cả ý định và mục đích ngẫu nhiên, thì do nghịch lý sinh nhật, một không gian lớn hơn (xô) sẽ làm giảm khả năng xảy ra va chạm.
Không biết

1
@Un Unknown bạn đã bỏ lỡ rằng va chạm cũng phụ thuộc vào chính hàm băm. Vì vậy, nếu chức năng có thực sự xấu, thì dù bạn có tăng kích thước lớn đến đâu, vẫn có thể có một số lượng va chạm đáng kể
Suraj Chandran

Bài viết gốc dường như không còn nữa, nhưng có một số ý kiến ​​sâu sắc ở đây, bao gồm cả một cuộc thảo luận với tác giả ban đầu. news.ycombinator.com/item?id=650487
Adrian McCarthy

3

Số nguyên tố là số duy nhất. Chúng là duy nhất ở chỗ, sản phẩm của một số nguyên tố với bất kỳ số nào khác có cơ hội duy nhất tốt nhất (không phải là duy nhất như số nguyên tố tất nhiên) do thực tế là một số nguyên tố được sử dụng để soạn nó. Tài sản này được sử dụng trong các chức năng băm.

Được cung cấp một chuỗi Samuel Samuel, bạn có thể tạo một hàm băm duy nhất bằng cách nhân từng chữ số hoặc chữ cái cấu thành với một số nguyên tố và thêm chúng vào. Đây là lý do tại sao các số nguyên tố được sử dụng.

Tuy nhiên sử dụng số nguyên tố là một kỹ thuật cũ. Chìa khóa ở đây để hiểu rằng miễn là bạn có thể tạo một khóa đủ độc đáo, bạn cũng có thể chuyển sang các kỹ thuật băm khác. Tới đây để biết thêm về chủ đề này về http://www.azillionmonkeys.com/qed/hash.html

http://computinglife.wordpress.com/2008/11/20/why-do-hash-fifts-use-prime-numbers/


1
hahahah .... thực sự không phải sản phẩm của 2 số nguyên tố có cơ hội 'độc nhất' tốt hơn sản phẩm của một số nguyên tố và bất kỳ số nào khác?
HasaniH

@Beska Ở đây "tính duy nhất" được định nghĩa theo cách đệ quy, vì vậy tôi tin rằng "tính duy nhất" nên được định nghĩa theo cùng một cách :)
TT_

3

Nó phụ thuộc vào sự lựa chọn của hàm băm.

Nhiều hàm băm kết hợp các yếu tố khác nhau trong dữ liệu bằng cách nhân chúng với một số yếu tố modulo sức mạnh của hai tương ứng với kích thước từ của máy (mô-đun đó là miễn phí chỉ bằng cách cho phép tính toán tràn).

Bạn không muốn bất kỳ yếu tố chung nào giữa số nhân cho một yếu tố dữ liệu và kích thước của bảng băm, bởi vì sau đó có thể xảy ra việc thay đổi thành phần dữ liệu không lan truyền dữ liệu trên toàn bộ bảng. Nếu bạn chọn một số nguyên tố cho kích thước của bảng thì một yếu tố phổ biến là rất khó xảy ra.

Mặt khác, các yếu tố đó thường được tạo thành từ các số nguyên tố lẻ, do đó bạn cũng nên an toàn khi sử dụng quyền hạn hai cho bảng băm của mình (ví dụ: Eclipse sử dụng 31 khi nó tạo phương thức hashCode () của Java).


2

Giả sử kích thước bảng của bạn (hoặc số cho modulo) là T = (B * C). Bây giờ nếu hàm băm cho đầu vào của bạn giống như (N * A * B) trong đó N có thể là bất kỳ số nguyên nào, thì đầu ra của bạn sẽ không được phân phối tốt. Bởi vì mỗi khi n trở thành C, 2C, 3C, v.v., đầu ra của bạn sẽ bắt đầu lặp lại. tức là đầu ra của bạn sẽ chỉ được phân phối ở các vị trí C. Lưu ý rằng C ở đây là (T / HCF (kích thước bảng, hàm băm)).

Vấn đề này có thể được loại bỏ bằng cách tạo HCF 1. Số nguyên tố rất tốt cho điều đó.

Một điều thú vị khác là khi T là 2 ^ N. Chúng sẽ cho đầu ra giống hệt như tất cả các bit N thấp hơn của hàm băm đầu vào. Vì mọi số có thể được biểu diễn lũy thừa bằng 2, khi chúng ta lấy modulo của bất kỳ số nào bằng T, chúng ta sẽ trừ tất cả các lũy thừa của số 2 dạng, đó là> = N, do đó luôn đưa ra số mẫu cụ thể, phụ thuộc vào đầu vào . Đây cũng là một lựa chọn tồi.

Tương tự, T là 10 ^ N cũng xấu vì những lý do tương tự (mẫu theo ký hiệu thập phân của số thay vì nhị phân).

Vì vậy, các số nguyên tố có xu hướng cho kết quả phân phối tốt hơn, do đó là lựa chọn tốt cho kích thước bảng.


2

Sao chép từ câu trả lời khác của tôi https://stackoverflow.com/a/43126969/917428 . Xem nó để biết thêm chi tiết và ví dụ.

Tôi tin rằng nó chỉ liên quan đến thực tế là các máy tính hoạt động với cơ sở 2. Chỉ cần nghĩ về cách thức hoạt động của cùng một thứ cho cơ sở 10:

  • 8% 10 = 8
  • 18% 10 = 8
  • 87865378% 10 = 8

Không quan trọng con số là gì: miễn là kết thúc bằng 8, modulo 10 của nó sẽ là 8.

Chọn một số đủ lớn, không có công suất hai sẽ đảm bảo hàm băm thực sự là một hàm của tất cả các bit đầu vào, chứ không phải là một tập hợp con của chúng.


1

Tôi muốn thêm một cái gì đó cho câu trả lời của Steve Jessop (Tôi không thể nhận xét về nó vì tôi không có đủ danh tiếng). Nhưng tôi tìm thấy một số tài liệu hữu ích. Câu trả lời của anh ấy rất hữu ích nhưng anh ấy đã mắc một lỗi: kích thước xô không phải là sức mạnh của 2. Tôi sẽ chỉ trích dẫn từ cuốn sách "Giới thiệu về thuật toán" của Thomas Cormen, Charles Leisersen, et al trên trang263:

Khi sử dụng phương pháp chia, chúng ta thường tránh các giá trị nhất định của m. Ví dụ, m không nên là lũy thừa của 2, vì nếu m = 2 ^ p, thì h (k) chỉ là các bit thứ tự thấp nhất của k. Trừ khi chúng ta biết rằng tất cả các mẫu p-bit có thứ tự thấp đều có khả năng như nhau, tốt hơn hết là chúng ta nên thiết kế hàm băm để phụ thuộc vào tất cả các bit của khóa. Như Bài tập 11.3-3 yêu cầu bạn hiển thị, chọn m = 2 ^ p-1 khi k là một chuỗi ký tự được hiểu theo cơ số 2 ^ p có thể là một lựa chọn kém, vì việc hoán vị các ký tự của k không thay đổi giá trị băm của nó.

Hy vọng nó giúp.


0

Đối với hàm băm, nó không chỉ quan trọng để giảm thiểu các phần tử nói chung mà còn làm cho nó không thể ở cùng một hàm băm trong khi sử dụng một vài byte.

Nói rằng bạn có một phương trình: (x + y*z) % key = xvới 0<x<key0<z<key. Nếu khóa là số nguyên tố n * y = khóa là đúng với mọi n trong N và sai cho mọi số khác.

Một ví dụ trong đó khóa không phải là ví dụ chính: x = 1, z = 2 và key = 8 Vì khóa / z = 4 vẫn là số tự nhiên, 4 trở thành một giải pháp cho phương trình của chúng tôi và trong trường hợp này (n / 2) * y = key đúng với mọi n trong N. Lượng giải pháp cho phương trình thực tế đã tăng gấp đôi vì 8 không phải là số nguyên tố.

Nếu kẻ tấn công của chúng ta đã biết rằng 8 là giải pháp khả thi cho phương trình, anh ta có thể thay đổi tệp từ sản xuất 8 thành 4 và vẫn nhận được cùng một hàm băm.


0

Tôi đã đọc trang web wordpress phổ biến được liên kết trong một số câu trả lời phổ biến ở trên. Từ những gì tôi đã hiểu, tôi muốn chia sẻ một quan sát đơn giản mà tôi đã thực hiện.

Bạn có thể tìm thấy tất cả các chi tiết trong bài viết ở đây , nhưng giả sử những điều sau đây là đúng:

  • Sử dụng số nguyên tố cho chúng ta "cơ hội tốt nhất" của một giá trị duy nhất

Một triển khai hashmap chung muốn 2 thứ là duy nhất.

  • Mã băm duy nhất cho khóa
  • Chỉ mục duy nhất để lưu trữ giá trị thực tế

Làm thế nào để chúng ta có được chỉ số duy nhất? Bằng cách làm cho kích thước ban đầu của container bên trong là một nguyên tố. Về cơ bản, Prime có liên quan vì nó sở hữu đặc điểm duy nhất này là tạo ra các số duy nhất mà cuối cùng chúng ta sử dụng cho các đối tượng ID và tìm các chỉ mục bên trong vùng chứa bên trong.

Thí dụ:

khóa = "chìa khóa"

giá trị = "giá trị" uniqueId = "k" * 31 ^ 2 + "e" * 31 ^ 1` + "y"

ánh xạ tới id duy nhất

Bây giờ chúng tôi muốn một vị trí duy nhất cho giá trị của chúng tôi - vì vậy chúng tôi

uniqueId % internalContainerSize == uniqueLocationForValue, giả sử internalContainerSizecũng là một nguyên tố.

Tôi biết điều này được đơn giản hóa, nhưng tôi hy vọng có được ý tưởng chung thông qua.


0

"Bản chất của toán học" liên quan đến các mô đun sức mạnh chính là chúng là một khối xây dựng của một trường hữu hạn . Hai khối xây dựng khác là một phép cộng và phép nhân. Tính chất đặc biệt của các mô đun nguyên tố là chúng tạo thành một trường hữu hạn với các phép toán cộng và nhân "thông thường", chỉ cần đưa vào mô đun. Điều này có nghĩa là mọi phép nhân ánh xạ tới một số nguyên modulo khác nhau, mỗi phép cộng cũng vậy.

Moduli Prime là lợi thế bởi vì:

  • Họ cho phép tự do nhất khi chọn hệ số nhân thứ cấp trong băm thứ cấp, tất cả các số nhân trừ 0 sẽ kết thúc truy cập tất cả các phần tử chính xác một lần
  • Nếu tất cả các giá trị băm nhỏ hơn mô đun thì sẽ không có va chạm nào cả
  • Các số nguyên tố ngẫu nhiên trộn tốt hơn sức mạnh của hai mô đun và nén thông tin của tất cả các bit không chỉ là một tập hợp con

Tuy nhiên, chúng có một nhược điểm lớn, chúng đòi hỏi một phân chia số nguyên, phải mất nhiều chu kỳ (~ 15-40), ngay cả trên một CPU hiện đại. Với khoảng một nửa tính toán, người ta có thể chắc chắn rằng hàm băm được trộn rất tốt. Hai phép nhân và phép toán xorshift sẽ kết hợp tốt hơn so với một moudulus chính. Sau đó, chúng ta có thể sử dụng bất kỳ kích thước bảng băm nào và giảm băm là nhanh nhất, cung cấp tổng cộng 7 thao tác cho sức mạnh của 2 kích thước bảng và khoảng 9 thao tác cho các kích thước tùy ý.

Gần đây tôi đã xem xét nhiều triển khai bảng băm nhanh nhất và hầu hết trong số chúng không sử dụng các mô đun chính.


0

Câu hỏi này được hợp nhất với câu hỏi phù hợp hơn, tại sao các bảng băm nên sử dụng các mảng có kích thước nguyên tố và không phải là sức mạnh của 2. Đối với chính hàm băm có rất nhiều câu trả lời hay ở đây, nhưng đối với câu hỏi liên quan, tại sao một số bảng băm quan trọng bảo mật , như glibc, sử dụng các mảng có kích thước nguyên tố, chưa có mảng nào.

Nói chung sức mạnh của 2 bàn nhanh hơn nhiều. Ở đó đắt tiền h % n => h & bitmask, nơi bitmask có thể được tính thông qua clz("số 0 đứng đầu") có kích thước n. Hàm modulo cần thực hiện phép chia số nguyên chậm hơn khoảng 50 lần so với logic and. Có một số mẹo để tránh modulo, như sử dụng https://lemire.me/blog/2016/06/27/a-fast-alternative-to-the-modulo-redraction/ , nhưng nói chung, bảng băm nhanh sử dụng năng lượng 2 và các bảng băm an toàn sử dụng các số nguyên tố.

Tại sao như vậy?

Bảo mật trong trường hợp này được xác định bằng các cuộc tấn công vào chiến lược giải quyết va chạm, với hầu hết các bảng băm chỉ là tìm kiếm tuyến tính trong một danh sách các xung đột được liên kết. Hoặc với các bảng địa chỉ mở nhanh hơn tìm kiếm tuyến tính trong bảng trực tiếp. Vì vậy, với sức mạnh của 2 bảng và một số kiến ​​thức bên trong của bảng, ví dụ: kích thước hoặc thứ tự của danh sách các khóa được cung cấp bởi một số giao diện JSON, bạn sẽ có được số bit đúng được sử dụng. Số lượng những người trên bitmask. Điều này thường thấp hơn 10 bit. Và trong 5-10 bit, nó va chạm mạnh mẽ với các hàm băm mạnh nhất và chậm nhất. Bạn không nhận được bảo mật đầy đủ của các hàm băm 32 bit hoặc 64 bit của mình nữa. Và vấn đề là sử dụng các hàm băm nhỏ nhanh, không phải quái vật như tiếng thì thầm hay thậm chí là siphash.

Vì vậy, nếu bạn cung cấp giao diện bên ngoài cho bảng băm của mình, như trình phân giải DNS, ngôn ngữ lập trình, ... bạn muốn quan tâm đến việc lạm dụng những người thích sử dụng các dịch vụ như vậy của DOS. Những người như vậy thường dễ dàng hơn để đóng cửa dịch vụ công cộng của bạn bằng các phương pháp dễ dàng hơn nhiều, nhưng nó đã xảy ra. Vì vậy, mọi người đã quan tâm.

Vì vậy, các lựa chọn tốt nhất để ngăn chặn các cuộc tấn công va chạm như vậy là

1) để sử dụng các bảng nguyên tố, bởi vì sau đó

  • tất cả 32 hoặc 64 bit có liên quan để tìm thùng, không chỉ một vài.
  • chức năng thay đổi kích thước bảng băm là tự nhiên hơn chỉ là gấp đôi. Chức năng tăng trưởng tốt nhất là chuỗi Wikipedia và các số nguyên tố đến gần hơn gấp đôi.

2) sử dụng các biện pháp tốt hơn để chống lại cuộc tấn công thực tế, cùng với sức mạnh nhanh với 2 kích cỡ.

  • đếm các va chạm và hủy bỏ hoặc ngủ trên các cuộc tấn công được phát hiện, đó là các số va chạm với xác suất <1%. Giống như 100 với bảng băm 32 bit. Đây là những gì, ví dụ như trình giải quyết dns của djb.
  • chuyển đổi danh sách các va chạm được liên kết thành cây với tìm kiếm O (log n) chứ không phải O (n) khi phát hiện một cuộc tấn công va chạm. Đây là những gì ví dụ java làm.

Có một huyền thoại phổ biến rộng rãi rằng các hàm băm an toàn hơn giúp ngăn chặn các cuộc tấn công như vậy, đó là sai như tôi đã giải thích. Không có bảo mật chỉ với các bit thấp. Điều này sẽ chỉ hoạt động với các bảng có kích thước nguyên tố, nhưng điều này sẽ sử dụng kết hợp hai phương pháp chậm nhất, băm chậm cộng với modulo nguyên tố chậm.

Các hàm băm cho các bảng băm chủ yếu cần phải nhỏ (có thể inlinable) và nhanh. Bảo mật chỉ có thể đến từ việc ngăn chặn tìm kiếm tuyến tính trong các va chạm. Và không sử dụng các hàm băm xấu tầm thường, như các hàm không nhạy cảm với một số giá trị (như \ 0 khi sử dụng phép nhân).

Sử dụng hạt giống ngẫu nhiên cũng là một lựa chọn tốt, mọi người bắt đầu với hạt giống đó trước tiên, nhưng với đủ thông tin của bảng, ngay cả một hạt giống ngẫu nhiên cũng không giúp được gì nhiều, và các ngôn ngữ động thường khiến việc lấy hạt giống thông qua các phương thức khác, vì nó được lưu trữ trong vị trí bộ nhớ đã biết.


-1
function eratosthenes(n) {

    function getPrime(x) {
        var middle = (x-(x%2))/2;
        var arr_rest = [];
        for(var j=2 ; j<=middle;j++){
            arr_rest.push(x%j);
        }

        if(arr_rest.indexOf(0) == -1) {
            return true
        }else {
            return false
        }

    }
    if(n<2)  {
        return []
    }else if(n==2){
        return [2]
    }else {
        var arr = [2]
        for(var i=3;i<n;i++) {
            if(getPrime(i)){
                arr.push(i)
            }
        }
    }

    return arr;
}

2
Bạn có thể thêm ý kiến ​​để giải thích giải pháp của bạn, xin vui lòng?
pom421
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.