Thuật toán HyperLogLog hoạt động như thế nào?


171

Gần đây tôi đã tìm hiểu về các thuật toán khác nhau trong thời gian rảnh rỗi và một thuật toán mà tôi tình cờ thấy có vẻ rất thú vị được gọi là thuật toán HyperLogLog - ước tính có bao nhiêu mục duy nhất trong danh sách.

Điều này đặc biệt thú vị đối với tôi vì nó đã đưa tôi trở lại thời MySQL của tôi khi tôi thấy giá trị "Cardinality" (mà tôi luôn cho rằng gần đây rằng nó được tính toán không được ước tính).

Vì vậy, tôi biết làm thế nào để viết một thuật toán trong O ( n ) sẽ tính toán có bao nhiêu mục duy nhất trong một mảng. Tôi đã viết điều này bằng JavaScript:

function countUniqueAlgo1(arr) {
    var Table = {};
    var numUnique = 0;
    var numDataPoints = arr.length;
    for (var j = 0; j < numDataPoints; j++) {
        var val = arr[j];
        if (Table[val] != null) {
            continue;
        }
        Table[val] = 1;
        numUnique++;
    }
    return numUnique;
}

Nhưng vấn đề là thuật toán của tôi, trong khi O ( n ), sử dụng rất nhiều bộ nhớ (lưu trữ giá trị trong Table).

Tôi đã đọc bài báo này về cách đếm các bản sao trong một danh sách trong thời gian O ( n ) và sử dụng bộ nhớ tối thiểu.

Nó giải thích rằng bằng cách băm và đếm bit hoặc thứ gì đó người ta có thể ước tính trong một xác suất nhất định (giả sử danh sách được phân bổ đều) số lượng các mục duy nhất trong danh sách.

Tôi đã đọc bài báo, nhưng dường như tôi không thể hiểu nó. Ai đó có thể đưa ra lời giải thích của một giáo dân hơn? Tôi biết băm là gì, nhưng tôi không hiểu chúng được sử dụng như thế nào trong thuật toán HyperLogLog này.


4
Bài viết này ( research.google.com/pubs/pub40671.html ) cũng tóm tắt các thuật toán HyperLogLog và một số cải tiến. Tôi nghĩ nó dễ hiểu hơn so với bài báo gốc.
zhanxw

11
Chỉ là một gợi ý về danh pháp: Một số người sử dụng bộ từ để mô tả một bộ sưu tập các mặt hàng độc đáo . Đối với họ, câu hỏi của bạn có thể có ý nghĩa tốt hơn nếu bạn sử dụng danh sách thuật ngữ hoặc mảng thay thế.
Paddy3118

Câu trả lời:


153

Thủ thuật chính đằng sau thuật toán này là nếu bạn, quan sát một luồng các số nguyên ngẫu nhiên, hãy xem một số nguyên mà biểu diễn nhị phân bắt đầu bằng một số tiền tố đã biết, có nhiều khả năng số lượng của luồng là 2 ^ (kích thước của tiền tố) .

Nghĩa là, trong một dòng số nguyên ngẫu nhiên, ~ 50% số (ở dạng nhị phân) bắt đầu bằng "1", 25% bắt đầu bằng "01", 12,5% bắt đầu bằng "001". Điều này có nghĩa là nếu bạn quan sát một luồng ngẫu nhiên và thấy "001", khả năng cao hơn là luồng này có số lượng là 8.

(Tiền tố "00..1" không có ý nghĩa đặc biệt. Nó chỉ vì dễ dàng tìm thấy bit đáng kể nhất trong một số nhị phân trong hầu hết các bộ xử lý)

Tất nhiên, nếu bạn chỉ quan sát một số nguyên, khả năng giá trị này sai là rất cao. Đó là lý do tại sao thuật toán phân chia luồng trong các luồng phụ độc lập "m" và giữ độ dài tối đa của tiền tố "00 ... 1" đã thấy của mỗi luồng. Sau đó, ước tính giá trị cuối cùng bằng cách lấy giá trị trung bình của mỗi dòng.

Đó là ý tưởng chính của thuật toán này. Có một số chi tiết bị thiếu (ví dụ, hiệu chỉnh cho các giá trị ước tính thấp), nhưng tất cả đều được viết tốt trong bài báo. Xin lỗi vì tiếng anh khủng khiếp.


"có khả năng cao hơn là luồng này có số lượng thẻ là 8" Bạn có thể giải thích tại sao 000 có nghĩa là số lượng thử nghiệm dự kiến ​​2 ^ 3. Tôi đã cố gắng tính toán kỳ vọng toán học về số lượng thử nghiệm giả sử chúng tôi có ít nhất một lần chạy với 3 số không và không có lần chạy nào với 4 số không ...
yura

5
Tôi không hiểu bài báo cho đến khi tôi đọc nó. Bây giờ nó có ý nghĩa.
josiah

5
@yura Tôi biết đó là một nhận xét rất cũ, nhưng nó có thể hữu ích cho những người khác. Ông nói "Đó là, trong một dòng số nguyên ngẫu nhiên, (...) 12,5% bắt đầu bằng" 001 "." Cardinality có thể xảy ra là 8 vì 12,5% đại diện cho một phần tám của toàn bộ luồng.
braunmagrin

111

HyperLogLog là một cấu trúc dữ liệu xác suất . Nó đếm số lượng các yếu tố riêng biệt trong một danh sách. Nhưng so với cách làm đơn giản (có một tập hợp và thêm các phần tử vào tập hợp) thì nó thực hiện điều này một cách gần đúng.

Trước khi xem làm thế nào thuật toán HyperLogLog làm điều này, người ta phải hiểu tại sao bạn cần nó. Vấn đề với một cách đơn giản là nó tiêu tốn O(distinct elements)không gian. Tại sao có một ký hiệu O lớn ở đây thay vì chỉ các yếu tố riêng biệt? Điều này là do các yếu tố có thể có kích cỡ khác nhau. Một yếu tố có thể là 1một yếu tố khác "is this big string". Vì vậy, nếu bạn có một danh sách lớn (hoặc một luồng lớn các yếu tố), nó sẽ chiếm rất nhiều bộ nhớ.


Đếm xác suất

Làm thế nào người ta có thể có được một ước tính hợp lý của một số yếu tố duy nhất? Giả sử rằng bạn có một chuỗi độ dài mbao gồm {0, 1}xác suất bằng nhau. Xác suất mà nó sẽ bắt đầu bằng 0, với 2 số không, với số 0 là bao nhiêu? Nó là 1/2, 1/41/2^k. Điều này có nghĩa là nếu bạn đã gặp một chuỗi có ksố không, bạn đã xem qua 2^kcác phần tử. Vì vậy, đây là một điểm khởi đầu tốt. Có một danh sách các yếu tố được phân bổ đồng đều giữa 02^k - 1bạn có thể đếm số lượng tiền tố lớn nhất của các số 0 trong biểu diễn nhị phân và điều này sẽ cho bạn một ước tính hợp lý.

Vấn đề là giả định có số phân phối đồng đều từ 0t 2^k-1quá khó đạt được (dữ liệu chúng tôi gặp chủ yếu không phải là số, hầu như không bao giờ được phân phối đều và có thể nằm giữa bất kỳ giá trị nào. Nhưng sử dụng hàm băm tốt bạn có thể giả sử rằng các bit đầu ra sẽ được phân phối đồng đều và hầu hết hàm băm có đầu ra giữa 02^k - 1( SHA1 cung cấp cho bạn các giá trị giữa 02^160). Vì vậy, những gì chúng ta đã đạt được cho đến nay là chúng ta có thể ước tính số lượng phần tử duy nhất với số lượng tối đa của kbit bằng cách chỉ lưu trữ Một số log(k)bit kích thước . Nhược điểm là chúng tôi có một phương sai rất lớn trong ước tính của chúng tôi. Một điều tuyệt vời mà chúng tôi gần như đã tạo raƯớc tính giấy xác suất của năm 1984 (nó thông minh hơn một chút với ước tính, nhưng chúng tôi vẫn gần gũi).

Đăng nhập

Trước khi tiến xa hơn, chúng ta phải hiểu tại sao ước tính đầu tiên của chúng ta không tuyệt vời như vậy. Lý do đằng sau đó là một sự xuất hiện ngẫu nhiên của phần tử tiền tố 0 tần số cao có thể làm hỏng mọi thứ. Một cách để cải thiện nó là sử dụng nhiều hàm băm, đếm tối đa cho mỗi hàm băm và cuối cùng là trung bình chúng. Đây là một ý tưởng tuyệt vời, sẽ cải thiện ước tính, nhưng giấy LogLog đã sử dụng một cách tiếp cận hơi khác (có lẽ vì băm là loại đắt tiền).

Họ đã sử dụng một hàm băm nhưng chia nó thành hai phần. Một cái được gọi là xô (tổng số xô là 2^x) và cái khác - về cơ bản giống như hàm băm của chúng tôi. Thật khó cho tôi để có được những gì đang xảy ra, vì vậy tôi sẽ đưa ra một ví dụ. Giả sử bạn có hai phần tử và hàm băm của bạn cung cấp biểu mẫu giá trị 0để 2^10tạo ra 2 giá trị: 344387. Bạn đã quyết định có 16 thùng. Vì vậy, bạn có:

0101 011000  bucket 5 will store 1
0110 000011  bucket 6 will store 4

Bằng cách có nhiều thùng hơn, bạn giảm phương sai (bạn sử dụng nhiều không gian hơn một chút, nhưng nó vẫn còn nhỏ). Sử dụng các kỹ năng toán học, họ có thể định lượng được lỗi (đó là 1.3/sqrt(number of buckets)).

HyperLogLog

HyperLogLog không giới thiệu bất kỳ ý tưởng mới nào, nhưng chủ yếu sử dụng rất nhiều toán học để cải thiện ước tính trước đó. Các nhà nghiên cứu đã phát hiện ra rằng nếu bạn loại bỏ 30% số lượng lớn nhất khỏi các thùng, bạn sẽ cải thiện đáng kể ước tính. Họ cũng sử dụng một thuật toán khác để lấy số trung bình. Bài báo nặng về toán học.


Và tôi muốn kết thúc với một bài báo gần đây, trong đó cho thấy một phiên bản cải tiến của thuật toán hyperLogLog (cho đến bây giờ tôi không có thời gian để hiểu đầy đủ về nó, nhưng có lẽ sau này tôi sẽ cải thiện câu trả lời này).


2
Tôi cho rằng về mặt lý thuyết k zeroeskhông phải là một điều đặc biệt. thay vào đó bạn có thể tìm kiếm k onesvà logic sẽ giống hoặc thậm chí tìm k lengthchuỗi {0,1}nhưng lấy một chuỗi như vậy và gắn bó với nó? bởi vì tất cả chúng có xác suất bằng 1/2 ^ k trong trường hợp các chuỗi nhị phân như vậy?
dùng881300

3
HyperLogLog không loại bỏ 30% số lượng lớn nhất. Đây là ý tưởng của thuật toán SuperLogLog cũng được mô tả trong bài báo LogLog. Ý tưởng chính của thuật toán HyperLogLog là lấy trung bình sức mạnh của twos bằng cách sử dụng trung bình hài thay vì trung bình hình học như được sử dụng bởi SuperLogLog và LogLog.
otmar

21

Trực giác là nếu đầu vào của bạn là một tập hợp lớn số ngẫu nhiên (ví dụ: giá trị băm), chúng nên phân phối đều trên một phạm vi. Giả sử phạm vi lên tới 10 bit để biểu thị giá trị lên tới 1024. Sau đó, quan sát giá trị tối thiểu. Giả sử nó là 10. Sau đó, số lượng thẻ sẽ được ước tính là khoảng 100 (10 × 100 1024).

Đọc bài báo cho logic thực sự của khóa học.

Một lời giải thích tốt khác với mã mẫu có thể được tìm thấy ở đây:
Thuật toán tuyệt vời chết tiệt: Ước tính Cardinality - Blog của Nick


3
nâng cấp cho các liên kết đến bài viết blog thuật toán mát mẻ chết tiệt. điều đó thực sự giúp tôi nắm bắt được thuật toán.
Igor Serebryany
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.