1. 1
  2. 2
  3. 3
  4. 4
  5. 5
  6. 6
  7. 7
  8. 8

# теория · шаг 5 из 8

Минимальные хеши: число различных и сходство множеств

Коротко

KMV хранит k наименьших различных значений хеша. При идеальном равномерном хеше на (0,1) и k≥2 оценка числа различных равна (k-1)/uₖ, где uₖ - сохранённый максимум; пока элементов меньше k, число известно точно. MinHash решает другую задачу: вероятность совпадения минимального элемента двух множеств в общей случайной перестановке равна их сходству Жаккара |A∩B|/|A∪B|. Для оценки сходства используют несколько независимых перестановок. Повторы элементов не должны менять ни одну из этих сводок.

Если различаешь оценку мощности и оценку сходства, листай до «Теперь сам».

Как думаешь, можно ли сравнить два списка без их хранения

Два сервиса видели миллионы идентификаторов, многие повторялись. Нужно узнать, сколько различных идентификаторов было вместе и насколько пересекались аудитории. Счётчик событий даст завышенное число, а знакомый HyperLogLog оценивает мощность, но сам по себе не возвращает сходство двух множеств. Минимальные хеши дают другой набор операций и гарантий.

На что это похоже

Представь, что каждому уникальному посетителю выдали случайный билет с числом от нуля до единицы. Чем больше посетителей, тем меньше обычно оказывается десятый по величине снизу билет. Храня только несколько победителей такого розыгрыша, можно судить о размере всей аудитории. Для сходства интересен один общий победитель: он совпадёт у двух аудиторий, если принадлежит их пересечению.

Механизм: порядок вместо частоты

При N независимых непрерывных равномерных билетах k-я порядковая статистика лежит примерно около k/N. Но просто обратить среднее нельзя. Точная несмещённая оценка при N≥k и k>1 использует (k-1)/uₖ. Независимость и непрерывность здесь - идеальная модель; конечный хеш допускает коллизии и требует их учитывать. k=1 в этой формуле не подходит.

Новый элемент добавляется лишь если его хеш входит в нижние k; повтор уже сохранённого элемента отбрасывается. Слияние сводок объединения берёт нижние k из объединения сохранённых пар. Это корректно при общем хеше: элемент, не попавший в нижние k своего исходного набора, не сможет попасть в нижние k объединения. Разные хеши уничтожают общий порядок и делают такое слияние бессмысленным.

Для MinHash вообразим равновероятную перестановку элементов объединения. Самый ранний элемент равновероятно выбирается из A∪B. Минимумы A и B совпадают ровно тогда, когда он лежит в A∩B. Значит вероятность совпадения - отношение размеров. При фиксированном числе независимых перестановок оценкой служит доля совпадений. Пустые множества обрабатываются отдельным договором, а не попыткой взять минимум пустого набора.

Правило

Для объединения KMV нужен единый порядок хешей; для MinHash - единая перестановка внутри одной пары и независимые перестановки между повторами. Один минимум не даёт точного сходства: он даёт один случайный признак совпадения.

Разбор: точная проверка вероятности на маленькой вселенной

kmv_minhash.py
from hashlib import blake2b
from itertools import permutations
from fractions import Fraction


def rank(x):
    raw = blake2b(str(x).encode(), digest_size=8).digest()
    return int.from_bytes(raw, "big")


def kmv(items, k):
    # Для учебной ясности используется полная сортировка.
    return sorted(set(items), key=lambda x: (rank(x), x))[:k]


def estimate(summary, k):
    if len(summary) < k:
        return float(len(summary))
    u = (rank(summary[-1]) + 0.5) / 2**64
    return (k - 1) / u


a = {1, 2, 3}
b = {2, 3, 4, 5}
k = 3
left, right = kmv(a, k), kmv(b, k)
merged = kmv(left + right, k)
assert merged == kmv(a | b, k)
assert kmv([1, 2, 2, 2, 3], k) == kmv(a, k)
print("Слияние:", merged)
print("Оценка объединения:", f"{estimate(merged, k):.3f}")
hits = trials = 0
for order in permutations(sorted(a | b)):
    position = {x: i for i, x in enumerate(order)}
    x = min(a, key=position.get)
    y = min(b, key=position.get)
    hits += x == y
    trials += 1
actual = Fraction(hits, trials)
expected = Fraction(len(a & b), len(a | b))
assert actual == expected
print("Перестановок:", trials)
print("Вероятность совпадения:", actual)
↳  выводpython3 kmv_minhash.py
Слияние: [2, 4, 5]
Оценка объединения: 3.675
Перестановок: 120
Вероятность совпадения: 2/5

Перебор всех перестановок не применяется к большой аудитории. Здесь он является независимой проверкой равенства вероятностей на маленьком наборе. Доля совпадений получается точно равной Жаккару. Для KMV программа отдельно проверяет сливаемость и нечувствительность к повторам.

Разбор: одинаковый размер не означает сходство

Множества {a,b,c} и {d,e,f} имеют одинаковую мощность, но Жаккар нулевой. Множества {a,b,c} и {a,b,d} также одинакового размера, а Жаккар равен половине. Значит мощность нельзя использовать вместо сходства. И наоборот, оценка Жаккара сама по себе не сообщает число уникальных посетителей: для него нужна мощность или дополнительная сводка.

В идеальной модели увеличение k уменьшает относительный шум KMV примерно как 1/√k. Это плата памятью за точность. У MinHash число независимых повторов играет похожую роль, но наблюдаемая величина и формула ошибки другие: усредняются нули и единицы совпадения.

Что ломается без этого

Если хранить минимумы потока с повторениями без удаления дублей, один частый ключ займёт всю сводку. Если сравнивать только значения конечного хеша, коллизия может выдать разных победителей за одного; сохраняй идентификатор или обеспечь пренебрежимо малую вероятность коллизии. В демонстрации хеш фиксирован для повторимости, но это не доказательство его идеальной независимости.

Зачем это в работе

Нужно ежедневно сливать статистику разных узлов и сравнивать аудитории документов. KMV естественно объединяется при общем хеше, MinHash позволяет искать похожие наборы признаков. В отличие от списка всех посетителей сводка мала, но результат статистический. Для решений, где ошибка недопустима, оценку нужно дополнять точной проверкой кандидатов.

Теперь сам

A={1,2,3}, B={2,3,4,5}. Какова вероятность совпадения MinHash в идеальной модели? Что произойдёт, если добавить тысячу повторений двойки?

Ответ. Объединение содержит пять элементов, пересечение два: вероятность 2/5. Повторы не меняют множества и не должны менять сводку.

Главное

Общий случайный порядок позволяет хранить малую часть множества, но мощность и сходство остаются разными запросами с разными оценками.

Комментарии

Пока нет комментариев. Будь первым!

Оставить комментарий

Комментарий появится после проверки. Email не публикуется. Войти, чтобы не вводить имя каждый раз.