Làm thế nào để chiếu đồng nhất một hàm băm vào một số lượng thùng cố định


11

Xin chào các nhà thống kê,

Tôi có một băm tạo nguồn (ví dụ: tính toán một chuỗi có dấu thời gian và các thông tin khác và băm với md5) và tôi muốn chiếu nó vào một số lượng cố định (ví dụ 100).

mẫu băm: 0fb916f0b174c66fd35ef078d861a367

Điều tôi nghĩ lúc đầu là chỉ sử dụng ký tự đầu tiên của hàm băm để chọn một nhóm, nhưng điều này dẫn đến một phép chiếu không đồng đều cực kỳ (nghĩa là một số chữ cái xuất hiện rất hiếm và rất thường xuyên khác)

Sau đó, tôi đã cố gắng chuyển đổi chuỗi hexa này thành một số nguyên bằng cách sử dụng tổng các giá trị char, sau đó lấy modulo để chọn một nhóm:

import sys

for line in sys.stdin:
    i = 0
    for c in line:
        i += ord(c)
    print i%100

Nó dường như hoạt động trong thực tế, nhưng tôi không biết liệu có bất kỳ ý nghĩa thông thường hoặc kết quả lý thuyết nào có thể giải thích tại sao và mức độ này là đúng không?

[Chỉnh sửa] Sau một số suy nghĩ, tôi đã đi đến kết luận sau: Về lý thuyết, bạn có thể chuyển đổi hàm băm thành số nguyên (rất lớn) bằng cách hiểu nó là một số: i = h [0] + 16 * h [1] + 16 * 16 * h [2] ... + 16 ^ 31 * h [31] (mỗi chữ cái đại diện cho một số thập lục phân). Sau đó, bạn có thể điều chỉnh số lượng lớn này để chiếu nó vào không gian xô. [/Biên tập]

Cảm ơn !


3
Một hàm băm thực sự không nên cho kết quả không đồng nhất như vậy. Bạn có chắc chắn thuật toán băm được thực hiện chính xác?
whuber

Tôi nghi ngờ có một lỗi trong chính thuật toán băm. Nhưng tôi nghi ngờ các ký tự của hex digest không được thống nhất nghiêm ngặt và phân phối độc lập.
oDDsKooL

1
Đó là điều tôi nghi ngờ: hàm băm "an toàn về mặt mật mã" như MD5 nên có phân phối đồng đều cho tất cả các chữ số, trừ khi có điều gì đó rất đặc biệt về phân phối đầu vào ("đặc biệt" có nghĩa là liên kết mật thiết với thuật toán MD5). Giải pháp đề xuất của bạn có giá trị để băm lại hàm băm, điều này hoàn toàn không cần thiết.
whuber

1
Ký tự đầu tiên của hàm băm Md5 phải đồng nhất. Nhưng bạn chỉ nhận được 16 giá trị (đó là mã hóa thập lục phân)
leonbloy

1
Cảm ơn vì đã nhấn mạnh vào điểm đó, tôi đã chạy lại việc đếm chữ cái đầu tiên của băm và có vẻ như nó thực sự được phân phối thống nhất: {'a': 789, 'c': 769, 'b': 755, 'e': 730, 'd': 804, 'f': 749, '1': 716, '0': 758, '3': 734, '2': 735, '5': 787, '4': 756, '7': 771, '6': 721, '9': 764, '8': 765}. Do đó, câu hỏi của tôi ít nhiều được trả lời vì tôi chỉ cần chiếu trình tạo ngẫu nhiên 16 trạng thái này sang không gian 100 trạng thái, có thể được thực hiện bằng cách sử dụng 2 chữ cái đầu tiên của hàm băm để tạo ra một số nguyên phạm vi [0,16+ 16 * 16] và sửa đổi nó thành 100. Hãy nhớ nếu tôi trả lời câu hỏi của chính mình;)?
oDDsKooL

Câu trả lời:


13

Lưu ý: đưa ra câu trả lời xuất hiện từ các cuộc thảo luận trong các bình luận để dễ đọc hơn cho những người quan tâm

(phiên bản cập nhật)

Giả sử chúng ta có một nguồn tạo ra các sự kiện độc lập mà chúng ta muốn phân phối đồng đều vào các thùngB

Các bước chính là:

  1. băm mỗi sự kiện đến một số nguyên có kích thướci 2 Nei2N
  2. dự án vào asp = iR×[0,1[p=i2N
  3. tìm xô phù hợp sao chob ibibiBp<bi+1B

Đối với 1. một giải pháp phổ biến là sử dụng MurmurHash để tạo số nguyên 64 hoặc 128 bit.

Cho 3. một giải pháp đơn giản là lặp lại trên và kiểm tra xem có trongp [ b jj=1..Bp[bjB,bj+1B[

Trong mã giả (python), quy trình tổng thể có thể là:

def hash_to_bucket(e, B):
    i = murmurhash3.to_long128(str(e))
    p = i / float(2**128)
    for j in range(0, B):
        if j/float(B) <= p and (j+1)/float(B) > p:
            return j+1
    return B

(phiên bản trước, thực sự không tối ưu)

Quan sát đầu tiên là chữ cái thứ n của hàm băm phải được phân phối đồng đều với bảng chữ cái (ở đây dài 16 chữ cái - cảm ơn @leonbloy đã chỉ ra điều đó).

Sau đó, để chiếu nó tới [0,100 [phạm vi, mẹo là lấy 2 chữ cái từ hàm băm (ví dụ: vị trí thứ 1 và thứ 2) và tạo một số nguyên với:

int_value = int(hash[0])+16*int(hash[1])

Giá trị này cuộc sống trong khoảng [0,16+ (16-1) * 16 [, do đó chúng tôi chỉ phải modulo nó đến 100 để tạo ra một cái xô trong khoảng [0, 100 [: Như đã chỉ ra trong các ý kiến, làm do đó tác động đến tính đồng nhất của phân phối vì chữ cái đầu tiên có ảnh hưởng nhiều hơn chữ cái thứ hai.

bucket = int_value % 100

Về lý thuyết, bạn có thể chuyển đổi toàn bộ hàm băm thành số nguyên (rất lớn) bằng cách hiểu nó là một số: i = h [0] + 16 * h [1] + 16 * 16 * h [2] ... + 16 ^ 31 * h [31] (mỗi chữ cái đại diện cho một số thập lục phân). Sau đó, bạn có thể điều chỉnh số lượng lớn này để chiếu nó vào không gian xô. Sau đó, người ta có thể lưu ý rằng việc sử dụng modulo của i có thể được phân tách thành một hoạt động phân phối và phụ gia:

imodN=((h0modN)+(16modN×h1modN)+...+(1631modN×h31modN))modN

Bất kỳ cải tiến cho câu trả lời này đều được chào đón.
oDDsKooL

Đây không phải là một giải pháp tốt vì khi "bất kỳ hai chữ cái" nào được "phân phối đồng đều", các nhóm từ đến thường sẽ nhận được nhiều lượt truy cập hơn 50% cho mỗi nhóm so với các nhóm từ đến . Trong thực tế, bạn đang sử dụng một hàm băm khủng khiếp trong nỗ lực băm chính hàm băm thành 100 thùng. Tại sao không chỉ sử dụng một hàm băm tốt được biết đến cho mục đích đó? 55 56 990555699
whuber

Tôi đồng ý. Một giải pháp cuộn bằng tay tốt hơn sẽ là lấy một đoạn của chuỗi hex có thể chuyển thành một số nguyên không gian 16 bit. Sau đó chia giá trị thực cho giá trị nguyên 16 bit tối đa, nhân với trăm và vòng.
spdrnl

Nếu bạn sử dụng một số nhóm ở dạng , bạn chỉ có thể lấy bit cuối cùng của hàm băm (và nó tương đương với các ký tự hex). Bằng cách này, kết quả của phép toán modulo sẽ hoàn toàn giống như khi tính toán nó trên toàn bộ chuyển đổi thành số nguyên. Nó cũng có thể hoạt động tốt nếu bạn sử dụng một số thùng không phải là sức mạnh . n 22nn2
alesc

@whuber Tôi đồng ý rằng điều này không hoàn toàn tối ưu và chiếu tới một khoảng [0,1 [liên tục sẽ tốt hơn nhiều. Tôi cũng đã xác minh điều đó bằng thực nghiệm. Tôi sẽ chỉnh sửa câu trả lời để phản ánh quan điểm đó.
oDDsKooL

0

Tôi đã có một vấn đề tương tự và đưa ra một giải pháp khác có thể nhanh hơn và dễ thực hiện hơn trong bất kỳ ngôn ngữ nào.

Suy nghĩ đầu tiên của tôi là gửi các mặt hàng một cách nhanh chóng và thống nhất trong một số lượng thùng cố định, và để có thể mở rộng, tôi nên bắt chước ngẫu nhiên.

Vì vậy, tôi đã mã hóa hàm nhỏ này trả về số float trong [0, 1 [đã cho một chuỗi (hoặc bất kỳ loại dữ liệu nào trong thực tế).

Ở đây trong Python:

import math
def pseudo_random_checksum(s, precision=10000):
    x = sum([ord(c) * math.sin(i + 1) for i,c in enumerate(s)]) * precision
    return x - math.floor(x)

Dĩ nhiên nó không phải là ngẫu nhiên, thực tế nó thậm chí không phải là giả ngẫu nhiên, cùng một dữ liệu sẽ luôn trả về cùng một tổng kiểm tra. Nhưng nó hoạt động như ngẫu nhiên và nó khá nhanh.

Bạn có thể dễ dàng gửi và truy xuất các mục trong N xô bằng cách chỉ định mỗi mục cho số xô math.floor (N * pseudo_random_checksum (mục)).


Bạn có trực giác hoặc bằng chứng rằng nó sẽ đặt các mẫu thống nhất trong [0,1] không?
sud_

@sud_ Chức năng này được thảo luận tại đây: stackoverflow.com/a/19303725/1608467
fbparis

@sud_ Ngoài ra, tôi đã chạy một số thử nghiệm để so sánh nó với một trình tạo số ngẫu nhiên hợp pháp và nó vẫn ổn trong mọi trường hợp tôi đã thử nghiệm.
fbparis
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.