Мы рады представить вашему вниманию заметку, написанную инженером Qrator Labs Дмитрием @dimak24 Камальдиновым. Если вы хотите быть частью нашей команды Core и заниматься подобными задачами — пишите нам на hr@qrator.net.
1 Введение
При реализации потоковых алгоритмов часто возникает задача подсчёта каких-то событий: приход пакета, установка соединения; при этом доступная память может стать узким местом: обычный -битный счётчик позволяет учесть не более событий. Одним из способов обработки большего диапазона значений, используя то же количество памяти, является вероятностный подсчёт. В этой статье будет предложен обзор известного алгоритма Морриса, а также некоторых его обобщений.
Другой способ уменьшить количество бит, необходимое для хранения значения счётчика, — использование распада. Об этом подходе мы рассказываем здесь, а также собираемся в ближайшее время опубликовать ещё одну заметку по теме.
Мы начнём с разбора простейшего алгоритма вероятностного подсчёта, выделим его недостатки (раздел 2). Затем (раздел 3) опишем алгоритм, впервые преложенный Робертом Моррисом в 1978 году, укажем его важнейшие свойства и приемущества. Для большинства нетривиальных формул и утверждений в тексте присутствуют наши доказательства — интересующийся читатель сможет найти их под спойлером. В трёх последующих разделах мы изложим полезные расширения классического алгоритма: вы узнаете, что общего у счётчиков Морриса и экспоненциального распада, как можно уменьшить ошибку, пожертвовав максимальным значением, и как эффективно обрабатывать взвешенные события.
2 Вероятностный подсчёт, проблемы тривиального подхода
Основная идея вероятностного подсчёта — учёт очередного события с некоторой вероятностью. Рассмотрим сначала пример с постоянной вероятностью обновления:
Где через обозначено значение счётчика после -ой попытки обновления. Несложно понять, что задаёт количество успехов среди независимых испытаний Бернулли. Иначе говоря, имеет биномиальное распределение:
В качестве оценки числа событий естественно использовать . Ясно, что такая оценка несмещённая: .
Описанный подход имеет несколько недостатков: во-первых, он позволяет учесть лишь в раз больше событий по сравнению с простым инкрементальным счётчиком, чем заметно уступает счётчикам Морриса, как мы увидим далее. Во-вторых, относительная ошибка оценки велика при маленьких . Например, при она вообще равна 100%. Оценить относительную ошибку можно с помощью коэффициента вариации:
3 Счётчики Морриса
Счётчик Морриса обновляется с вероятностью, зависящей от текущего значения: первое обновление происходит с вероятностью 1, следующее с вероятностью 1/2, потом 1/4 и т.д.:
Как по значению счётчика оценить, сколько было событий? Обновление счётчика с на происходит в среднем за итераций, отсюда оценка числа событий:
Важнейшими свойствами этой оценки являются
её несмещённость, то есть значение оценки после вероятностных обновлений в среднем равно ,
Доказательство
В самом начале значение счётчика равно 0: , тогда , а значит . Итого .
а также независимость её относительной ошибки от .
Доказательство
Сначала посчитаем дисперсию . Согласно известной формуле, . Мы уже знаем, что . Осталось получить . Заметим, что
Таким образом, остаётся узнать, чему равно .
Итого
Таким образом, относительная ошибка оценки числа событий при использовании счётчика Морриса ограничена, примерно постоянна и почти не зависит от .
Оценить вероятность больших отклонений от среднего можно с помощью неравенства Чебышёва:
отметим здесь также, что покрываемый диапазон количества событий при использовании бит памяти будет . Иначе говоря, для учёта событий достаточно бит памяти!
4 Взвешенные обновления
Счётчики Морриса можно использовать для подсчёта взвешенных событий: например, для суммирования размеров приходящих пакетов. Простейший способ — обновление счётчика <вес события> раз — приводит к линейной по весу сложности обновления.
Заметим, что в каждый момент времени мы знаем, как распределено количество неудач до ближайшего изменения счётчика, — это геометрическое рапределение:
Учитывая этот факт, мы можем ускорить алгоритм, сразу пропуская все подряд идущие неудачные попытки обновления:
5 Распад
Часто возникает потребность учитывать самые новые события с большим весом. Например, при поиске интенсивных ключей в потоке интереснее понять, какой ключ интенсивен прямо сейчас, а не имеет высокую среднюю интенсивность за счёт своего бурного прошлого. В статье мы рассказывали о распадных счётчиках, спроектированных специально для решения этой задачи. Другой предпосылкой для некоего распада является то, что несмотря на суперэкономное использование памяти, когда-то и счётчик Морриса переполнится. Оказывается, счётчики Морриса тоже могут распадаться, причём очень похожим на EMA способом, но гораздо эффективнее.
Идея очень проста: будем периодически вычитать из счётчика. Напомним, что значение оценивает количество событий как
Соответственно, после вычитания оценка станет. Иначе говоря, вычитание уменьшает оценку в раза! Мы можем добиться распада ровно в раза, обновив с вероятностью счётчик сразу после вычитания :
6 Мантисса и экспонента
Рассмотрим следующее обобщение счётчика Морриса: вероятность обновления уменьшается в раза не на каждое успешное обновление, а на каждые . Возможной реализацией такого подхода будет разделение битов счётчика на две части: экспоненту, отвечающую за вероятность, и мантиссу, считающую количество успешных обновлений при текущей вероятности (чем-то похоже на представление чисел с плавающей точкой).
Введём обозначения для мантиссы и экспоненты:
Теперь правило обновления и оценка числа событий записываются так:
Например, на рисунке 2 значение счётчика , мантисса и экспонента равны и соответственно. А значит число событий оценивается как .
При таком разбиении биты экспоненты будут отвечать за покрываемый диапазон количества событий, а биты мантиссы за точность оценки (далее мы поясним подробнее, что это значит). Наилучшее распределение битов счётчика зависит от задачи; мы предлагаем отдавать как можно больше мантиссе, но чтобы экспоненциальных битов хватало для покрытия интересующего диапазона значений.
Описанный вариант счётчиков Морриса обладает следующими свойствами:
покрываемый диапазон значений равен
Доказательство
Покрываемый диапазон получить несложно, подставив в формулу для максимальные возможные значения и :
новая оценка также несмещённая: ;
Доказательство
Аналогично доказательству утверждения для обычных счётчиков Морриса посчитаем условное матожидание . Причём сделаем это отдельно для (вероятность обновления изменится в случае успеха) и .
В обоих случаях
коэффициент вариации (и относительная ошибка) становятся ещё меньше! Мы не знаем его точное значение, но имеется такая оценка (сравните её с аналогичной для классического счётчика Морриса):
Алгоритмы взвешенных обновлений и распада тоже немного изменятся.
Аналогично подходу, описанному в разделе 4, чтобы эффективно выполнить взвешенное обновление, нужно понять, через сколько попыток обновления вероятность изменится. При отсутствии мантиссы вероятность меняется после каждого успешного обновления, теперь же после каждых . А значит нам нужно распределение количества испытаний Бернулли (искомое число событий), среди которых ровно успешных. Такое распределение называется отрицательным биномиальным.
Замечание. Существует несколько определений отрицательного биномиального распределения. Будем придерживаться написанного в русскоязычной википедии, где сказано, что это распределение задаёт не общее число событий, а число неудач. Тогда чтобы получить число событий, нужно к случайной величине добавить количество успехов:
Чтобы не генерировать отрицательное биномиальное распределение, можно загрубить алгоритм, всегда выбирая среднее этого распределения:
Чтобы выполнить распад, будем вычитать из экспоненциальной части (иначе говоря, из значения счётчика). Тогда
Заметим, что при (а именно этот случай изучается в этом разделе) . Поэтому исправить оценку можно, вероятностно увеличив счётчик на :
7 Заключение
Счётчики Морриса — это очень круто! Пользуйтесь!
Последний раздел не содержит полезных на практике результатов, но...
8 Обобщённые вероятностные счётчики
В общем виде правило обновления счётчика определяется функцией вероятности :
В случае счётчиков Морриса . При изучении вероятностных счётчиков мы попробовали взять линейную функцию вероятности:
где— некоторая константа, максимальное значение счётчика. Это привело к некоторым интересным результатам, которые будут кратко изложены в этом разделе.
Прежде чем представить эти результаты, напомним о двух математических сущностях. Полный однородный симметрический многочлен степени от переменных — это сумма всех возможных одночленов степени от этих переменных с коэффициентами :
Другая конструкция, которая нам понадобится, — число Стирлинга второго рода. Один из способов определить это число — комбинаторный: число Стирлинга второго рода — количество разбиений -элементного множества на непустых подмножеств. Числа Стирлинга связаны с полными симметрическим многочленами:
С помощью можно записать в общем виде вероятность того, что после попыток обновления значение счётчика равно :
Эту формулу легко доказать по индукции, но также она несложно следует из простых комбинаторных рассуждений: фактически в ней записано, что для того, чтобы получить значение , нужны успешных обновлений: с до , с до и т.д., — за это отвечает множитель , и неудачных, которые разбиваются на групп: неудачных обновлений с до , с до и т.д., что выражено .
Наконец, при использовании линейной функции вероятности вероятность принимает вид
Это и есть анонсированный выше интересный результат. Мы заметили число Стирлинга в значении вероятности достаточно случайно и до сих пор не знаем, можно ли обосновать его присутствие комбинаторынми рассуждениями (опираясь на его комбинаторное определение). Другой вопрос — можно ли красиво записать аналогичную вероятность для счётчиков Морриса.
Математическое ожидание и дисперсия (доказательства опускаем, но их несложно получить, используя формулу для и свойства чисел Стирлинга):
Оценить число событий можно по методу моментов:
или аналогично счётчикам Морриса, учитывая, что :
Список литературы
[1] Morris, R.Counting large numbers of events in small registersCommunications of the ACM, 1978 21(10), 840-842.
[2] http://gregorygundersen.com/blog/2019/11/11/morris-algorithm/ − хороший обзор счётчиков Морриса на английском языке
[3] https://habr.com/ru/company/qrator/blog/334354/ − наша статья про EMA-счётчики
[4] https://habr.com/ru/post/208268/ − сторонний материал про счётчики Морриса