Как работает алгоритм HyperLogLog?


172

Недавно в свободное время я изучал различные алгоритмы, и один из них, с которым я столкнулся, кажется очень интересным, называется алгоритмом HyperLogLog, который оценивает количество уникальных элементов в списке.

Это было особенно интересно для меня, потому что это вернуло меня в те дни, когда я видел MySQL, это значение «кардинальности» (которое я до недавнего времени всегда предполагал, что оно рассчитывается без оценки).

Итак, я знаю, как написать алгоритм в 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
Просто намек на номенклатуру: некоторые люди используют набор слов для описания коллекции уникальных предметов. Для них ваш вопрос мог бы иметь смысл, если бы вы использовали вместо него список или массив.
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% составляет одну восьмую часть всего потока.
Браунмагрин

111

HyperLogLog - это вероятностная структура данных . Он подсчитывает количество отдельных элементов в списке. Но по сравнению с простым способом сделать это (имея набор и добавляя элементы в набор), он делает это приблизительно.

Прежде чем посмотреть, как это делает алгоритм HyperLogLog, нужно понять, зачем он вам нужен. Проблема с простым способом состоит в том, что он потребляет O(distinct elements)пространство. Почему здесь присутствует большая буква О вместо просто отдельных элементов? Это потому, что элементы могут быть разных размеров. Один элемент может быть 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-префиксного элемента может все испортить. Один из способов улучшить это - использовать множество хеш-функций, рассчитывать max для каждой из хеш-функций и в итоге усреднять их. Это отличная идея, которая улучшит оценку, но в статье LogLog использовался немного другой подход (вероятно, потому что хеширование довольно дорогое).

Они использовали один хеш, но разделили его на две части. Один называется bucket (общее количество сегментов равно 2^x), а другой - в основном такой же, как наш хэш. Мне было трудно понять, что происходит, поэтому я приведу пример. Предположим, у вас есть два элемента и ваша хеш-функция, которая дает форму значений 0для 2^10произведенных двух значений: 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.
Отмар

21

Интуиция заключается в том, что если вы вводите большой набор случайных чисел (например, хэшированные значения), они должны распределяться равномерно по всему диапазону. Допустим, диапазон составляет до 10 бит для представления значения до 1024. Затем соблюдается минимальное значение. Скажем, это 10. Тогда мощность будет около 100 (10 × 100 ≈ 1024).

Прочитайте статью для реальной логики, конечно.

Еще одно хорошее объяснение с примером кода можно найти здесь:
Чертовы крутые алгоритмы: оценка мощности - Блог Ника


3
проголосовал за ссылку на чертовски крутые алгоритмы в блоге. это действительно помогло мне понять алгоритм.
Игорь Серебряный
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.