Як працює алгоритм HyperLogLog?


172

Нещодавно я дізнався про різні алгоритми у вільний час, і той, який мені траплявся, який, як видається, дуже цікавий, називається алгоритмом HyperLogLog - який оцінює кількість унікальних елементів у списку.

Це було особливо цікаво для мене, тому що воно повернуло мене до моїх MySQL днів, коли я побачив, що значення "Cardinality" (яке я до недавнього часу завжди вважав, що воно розраховане не оцінюється).

Тому я знаю, як написати алгоритм в O ( n ), який обчислить, скільки унікальних елементів є в масиві. Я написав це в 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;
}

Але проблема полягає в тому, що мій алгоритм, хоча O ( n ), використовує багато пам'яті (зберігає значення у Table).

Я читав цю статтю про те, як рахувати дублікати у списку за O ( n ) час та використовувати мінімальну пам'ять.

Це пояснює, що шляхом хешування та підрахунку бітів або чогось можна оцінити з певною вірогідністю (якщо припустити, що список рівномірно розподілений) кількість унікальних елементів у списку.

Я прочитав документ, але, здається, не розумію. Чи може хтось дати більш пояснене пояснення? Я знаю, що таке хеші, але я не розумію, як вони використовуються в цьому алгоритмі HyperLogLog.


4
У цьому документі ( research.google.com/pubs/pub40671.html ) також узагальнено алгоритм HyperLogLog та деякі вдосконалення. Я думаю, що це легше зрозуміти, ніж оригінальний папір.
zhanxw

11
Лише натяк на номенклатуру: Деякі люди використовують набір слів для опису колекції унікальних предметів. Для них ваше питання може мати кращий сенс, якщо ви замість цього використали термін list або масив.
Paddy3118

Відповіді:


153

Основна хитрість цього алгоритму полягає в тому, що якщо ви, спостерігаючи за потоком випадкових цілих чисел, бачите ціле число, яке двійкове представлення починається з якогось відомого префікса, є більший шанс, що кардинальність потоку дорівнює 2 ^ (розмір префікса) .

Тобто у випадковому потоці цілих чисел ~ 50% чисел (у двійкових) починається з "1", 25% починається з "01", 12,5% починається з "001". Це означає, що якщо ви спостерігаєте випадковий потік і бачите "001", більше шансів на те, що цей потік має кардинальність 8.

(Префікс "00..1" не має особливого значення. Він існує лише тому, що в більшості процесорів легко знайти найзначніший біт у двійковій кількості)

Звичайно, якщо ви спостерігаєте лише одне ціле число, велика ймовірність, що це значення неправильне. Ось чому алгоритм ділить потік на "m" незалежні підпотоки і зберігає максимальну довжину побаченого префікса "00 ... 1" кожного підпотоку. Потім оцінюється кінцеве значення, беручи середнє значення кожного підпотоку.

Ось основна ідея цього алгоритму. Є деякі недоліки (наприклад, виправлення низьких оціночних значень), але все це добре написано в роботі. Вибачте за жахливу англійську.


"є більший шанс, що цей потік має кардинальність 8". Чи можете ви пояснити, чому 000 означає очікувану кількість випробувань 2 ^ 3. Я спробував обчислити математичне очікування кількості випробувань, припускаючи, що у нас є принаймні один пробіг з 3 нулями, а не пробіг із 4 нулями ...
Юра

5
Я не зовсім зрозумів папір, поки я не прочитав це. Тепер це має сенс.
Джосія

5
@yura Я знаю, що це дуже старий коментар, але він може бути корисним для інших людей. Він сказав: "Тобто у випадковому потоці цілих чисел (...) 12,5% починається з" 001 "." Ймовірність кардинальності становить 8, оскільки 12,5% становить одну восьму частину всього потоку.
braunmagrin

111

HyperLogLog - це вірогідна структура даних . Він підраховує кількість різних елементів у списку. Але порівняно з прямим способом його виконання (маючи набір і додаючи елементи до набору), він робить це приблизно.

Перш ніж подивитися, як це робить алгоритм HyperLogLog, треба зрозуміти, для чого це потрібно. Проблема прямого шляху полягає в тому, що він витрачає O(distinct elements)місце. Чому тут існує велика O позначення замість просто різних елементів? Це тому, що елементи можуть бути різного розміру. Один елемент може бути 1іншим елементом "is this big string". Тож якщо у вас є величезний список (або величезний потік елементів), це займе багато пам’яті.


Імовірнісний підрахунок

Як можна отримати розумну оцінку кількості унікальних елементів? Припустимо, що у вас є рядок довжиною, mякий складається {0, 1}з однаковою ймовірністю. Яка ймовірність того, що він розпочнеться з 0, з 2 нулями, з k нулями? Це є 1/2, 1/4і 1/2^k. Це означає, що якщо ви стикалися з рядком із kнулями, ви приблизно проглядали 2^kелементи. Тож це хороший вихідний пункт. Маючи список елементів, розподілених рівномірно, 0і 2^k - 1ви можете порахувати максимальну кількість найбільшого префіксу нулів у двійковому поданні, і це дасть вам розумну оцінку.

Проблема полягає в тому, що припущення про рівномірне розподілення чисел від 0t 2^k-1надто важко досягти (дані, з якими ми стикалися, здебільшого не є числами, майже ніколи не розподіляються рівномірно, і можуть бути між будь-якими значеннями. Але, використовуючи хорошу функцію хешування, ви можете припустити, що вихідні біти будуть рівномірно розподілені, і більшість хеширующих функцій мають виходи між 0і 2^k - 1( SHA1 дає вам значення між 0і 2^160). Отже, що ми домоглися поки що, ми можемо оцінити кількість унікальних елементів з максимальною простотою kбітів, зберігаючи лише одна кількість log(k)бітів розміру . Мінусом є те, що ми маємо величезну дисперсію в нашій оцінці. Класна річ, яку ми майже створилиВірогідний підрахунок документа 1984 року (він трохи розумніший з оцінкою, але ми все ще близькі).

LogLog

Перш ніж рухатися далі, ми повинні зрозуміти, чому наша перша оцінка не така велика. Причина в тому, що одна випадкова поява високочастотного 0-префіксного елемента може зіпсувати все. Одним із способів її вдосконалення є використання багатьох хеш-функцій, підрахунок макс для кожної з хеш-функцій і врешті-решт їх середнє значення. Це відмінна ідея, яка поліпшить оцінку, але папір LogLog використовує дещо інший підхід (можливо, тому що хешування - це дорого коштує).

Вони використовували один хеш, але розділили його на дві частини. Одне називається відро (загальна кількість відра є 2^x), а інше - в основному те саме, що і наш хеш. Мені було важко зрозуміти, що відбувається, тому я наведу приклад. Припустимо , у вас є два елементи , і ваш хеш - функція , яка дає значення форми 0для 2^10виробництва 2 -х значень: 344і 387. Ви вирішили мати 16 відер. Отже, у вас є:

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

Маючи більше відра, ви зменшуєте дисперсію (ви використовуєте трохи більше місця, але вона все ще крихітна). За допомогою математичних навичок вони змогли кількісно оцінити помилку (яка є 1.3/sqrt(number of buckets)).

HyperLogLog

HyperLogLog не вводить нових ідей, але в основному використовує багато математики для покращення попередньої оцінки. Дослідники встановили, що якщо ви вилучите з відра 30% найбільшої кількості, ви значно поліпшите оцінку. Вони також використовували інший алгоритм для усереднення чисел. Папір математично важкий.


І я хочу закінчити останнім документом, де показана вдосконалена версія алгоритму hyperLogLog (до цього часу я не встиг його повністю зрозуміти, але, можливо, пізніше я вдосконалю цю відповідь).


2
Я припускаю, що теоретично k zeroesце не особлива річ. ви можете замість цього шукати, k onesі логіка була б однаковою, або навіть шукати k lengthрядок, {0,1}але візьміть один такий рядок і дотримуйтесь його? тому що всі вони мають однакову ймовірність 1/2 ^ k у випадку таких бінарних рядків?
user881300

3
HyperLogLog не видаляє 30% найбільшого числа. Така ідея алгоритму SuperLogLog також описана в статті LogLog. Основна ідея алгоритму HyperLogLog - це середня потужність двох, використовуючи середнє гармонічне, а не середнє геометричне, як використовується SuperLogLog та LogLog.
otmar

21

Інтуїція полягає в тому, що якщо ваш вхід є великим набором випадкових чисел (наприклад, хешованих значень), вони повинні розподілятись рівномірно по діапазону. Скажімо, діапазон до 10 біт, щоб представляти значення до 1024. Потім спостерігали мінімальне значення. Скажімо, це 10. Тоді кардинальність оцінюється приблизно в 100 (10 × 100 ≈ 1024).

Прочитайте папір, щоб зрозуміти справжню логіку.

Ще одне добре пояснення із зразковим кодом можна знайти тут:
Чортові круті алгоритми: Оцінка кардинальності - Блог Ніка


3
вимагає посилання на проклятий прохолодний алгоритм публікації блогу. це дійсно допомогло мені зрозуміти алгоритм.
Ігор Серебряний
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.