Обычный подход (по крайней мере, общий для меня) - разделить вашу битовую строку на несколько частей и запросить по этим фрагментам точное совпадение в качестве шага предварительной фильтрации. Если вы работаете с файлами, вы создаете столько файлов, сколько у вас есть чанков (например, 4 здесь), каждый чанк переставляется впереди, а затем сортируете файлы. Вы можете использовать двоичный поиск, и вы даже можете расширить область поиска выше и ниже подходящего блока для получения бонуса.
Затем вы можете выполнить побитовое вычисление расстояния Хэмминга для возвращаемых результатов, которые должны быть только меньшим подмножеством вашего общего набора данных. Это можно сделать с помощью файлов данных или таблиц SQL.
Итак, резюмируем: скажем, у вас есть куча 32-битных строк в БД или файлах и вы хотите найти каждый хэш, который находится в пределах 3-битного расстояния Хэмминга или меньше от вашей битовой строки "запроса":
создайте таблицу с четырьмя столбцами: каждый будет содержать 8-битный (в виде строки или int) срез 32-битных хэшей, от 1 до 4. Или, если вы используете файлы, создайте четыре файла, каждый из которых представляет собой перестановку срезов, имеющих по одному "кусочку" в начале каждого "ряда"
нарежьте битовую строку запроса таким же образом в qslice от 1 до 4.
запросить эту таблицу так, чтобы любой из qslice1=islice1 or qslice2=islice2 or qslice3=islice3 or qslice4=islice4
. Это дает вам каждую строку, которая находится в пределах 7 бит (8 - 1
) от строки запроса. При использовании файла выполните двоичный поиск в каждом из четырех файлов с перестановкой для получения тех же результатов.
для каждой возвращенной битовой строки вычислить точное расстояние Хэмминга попарно с вашей битовой строкой запроса (реконструкция битовых строк на стороне индекса из четырех срезов либо из БД, либо из переставленного файла)
Количество операций на шаге 4 должно быть намного меньше, чем полное попарное вычисление Хэмминга всей вашей таблицы, и на практике это очень эффективно. Кроме того, можно легко разделить файлы на файлы меньшего размера, поскольку требуется более высокая скорость, используя параллелизм.
Теперь, конечно, в вашем случае вы ищете своего рода самосоединение, то есть все значения, которые находятся на некотором расстоянии друг от друга. Тот же подход по-прежнему работает IMHO, хотя вам придется расширяться вверх и вниз от начальной точки для перестановок (с использованием файлов или списков), которые разделяют начальный фрагмент, и вычисляют расстояние Хэмминга для результирующего кластера.
Если вы работаете в памяти, а не в файлах, ваш набор данных 100M 32-битных строк будет в диапазоне 4 ГБ. Следовательно, для четырех переставленных списков может потребоваться около 16 ГБ + ОЗУ. Хотя вместо этого я получаю отличные результаты с файлами с отображением памяти и мне нужно меньше ОЗУ для наборов данных аналогичного размера.
Доступны реализации с открытым исходным кодом. Лучшее в этой области - IMHO, созданное для Simhash от Moz, C ++, но предназначенное для 64 битовые строки, а не 32-битные.
Этот подход с ограниченным расстоянием касания был впервые описан AFAIK Моисеем Чарикаром в его оригинальном "симхаше" paper и соответствующий Google патент:
- ПРИБЛИЗИТЕЛЬНЫЙ ПОИСК БЛИЖАЙШИХ СОСЕДЕЙ В КОСМИЧЕСКОМ ПРОСТРАНСТВЕ
[...]
Для заданных битовых векторов, состоящих из d бит каждый, мы выбираем N = O (n 1 / (1+)) случайных перестановок битов. Для каждой случайной перестановки σ мы поддерживаем отсортированный порядок O σ битовых векторов в лексикографическом порядке битов, переставляемых с помощью σ. Учитывая битовый вектор запроса q, мы находим приблизительного ближайшего соседа, выполнив следующие действия:
Для каждой перестановки σ мы выполняем двоичный поиск на O σ, чтобы определить местонахождение двух битовых векторов, ближайших к q (в лексикографическом порядке, полученном битами, переставленными с помощью σ). Теперь мы ищем в каждом из отсортированных порядков O σ, исследуя элементы выше и ниже позиции, возвращаемой двоичным поиском, в порядке длины самого длинного префикса, который соответствует q.
Моника Хензигер подробно рассказала об этом в своей статье " Поиск почти повторяющихся веб-страниц: масштабная оценка алгоритмов ":
3.3 Результаты для алгоритма C
Мы разделили битовую строку каждой страницы на 12 неперекрывающихся 4-байтовых частей, создав 20B частей, и вычислили C-подобие всех страниц, у которых есть хотя бы одна общая часть. Этот подход гарантированно найдет все пары страниц с разницей до 11, то есть C-подобием 373, но может пропустить некоторые из-за больших различий.
Это также объясняется в статье Обнаружение почти дубликатов для веб-сканирования от Gurmeet. Сингх Манку, Арвинд Джайн и Аниш Дас Сарма:
- ПРОБЛЕМА УБИВАЮЩЕГО РАССТОЯНИЯ
Определение: учитывая набор f -битных отпечатков пальцев и отпечаток запроса F, определить, отличается ли существующий отпечаток от F не более чем на k битов. (В версии вышеупомянутой проблемы в пакетном режиме у нас есть набор отпечатков пальцев вместо одного отпечатка запроса)
[...]
Интуиция: рассмотрим отсортированную таблицу 2d f -битных действительно случайных отпечатков пальцев. Сосредоточьтесь только на наиболее значимых d битах в таблице. Перечисление этих d-битовых чисел составляет «почти счетчик» в том смысле, что (а) существует довольно много двумерных битовых комбинаций и (б) дублируется очень мало d-битовых комбинаций. С другой стороны, младшие биты f - d «почти случайны».
Теперь выберем d так, чтобы | d - d | - маленькое целое число. Поскольку таблица отсортирована, одного зонда достаточно, чтобы идентифицировать все отпечатки пальцев, которые соответствуют F в d наиболее значимых битовых позициях. Поскольку | d - d | невелико, количество таких совпадений также ожидается невелико. Для каждого совпадающего отпечатка пальца мы можем легко выяснить, отличается ли он от F не более чем в k битовых позициях или нет (эти различия, естественно, будут ограничены f - d наименее значимыми битовыми позициями).
Описанная выше процедура помогает нам найти существующий отпечаток пальца, который отличается от F k битовыми позициями, все из которых ограничены, чтобы быть среди наименее значимых f - d битов F. Это позволяет позаботиться о значительном количестве случаев. Чтобы охватить все случаи, достаточно создать небольшое количество дополнительных отсортированных таблиц, как формально описано в следующем разделе.
Примечание. Я опубликовал аналогичный ответ на связанный вопрос только для БД
person
Philippe Ombredanne
schedule
27.11.2017