2010-08-07 2 views
9

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

Мне нужно найти очень большой объем данных для подстроки, которая будет примерно в миллиард раз меньше (10 байт в 10 миллиардов байт). Стог сена не меняется, поэтому я могу при необходимости провести большую предварительную компрессию. Мне просто нужно, чтобы поисковая часть была как можно быстрее.

я нашел алгоритмы, которые принимают О (п) времени (п = размер стога, м = размер иглы), и наивный поиск принимают O (N + M). Поскольку m в этом конкретном случае был бы очень мал, есть ли какой-либо другой алгоритм, который я мог бы изучить?

Редактировать: Спасибо всем за ваши предложения! Дополнительная информация - Данные могут считаться случайными битами, поэтому я не думаю, что возможно любое индексирование/сортировка. Данные, которые нужно искать, могут быть любыми, а не английскими словами или предсказуемыми.

+2

Это зависит от характера данных. Не могли бы вы отсортировать его, индексировать, ...? (вы можете посмотреть в таблицы радуги, так как для этого вы обычно ищете 10-байтовую строку в 10 миллиардов байт) – mvds

+0

btw большой O, о котором вы говорите, не учитывает, что 10 миллиардов байтов могут не помещаться в память - сколько у вас есть? Disk i/o * действительно * медленный по сравнению с RAM. – mvds

+0

И эта 10-байтовая строка поиска, это случайная или имеется ограниченный диапазон строк поиска? вам действительно нужно поделиться некоторыми подробностями. – mvds

ответ

7

Вы ищете структуру данных, называемую Trie или "префиксное дерево". Короче говоря, эта структура данных кодирует все возможные префиксы строк, которые можно найти в вашем корпусе.

Here is a paper, который ищет последовательности ДНК для небольшой подстроки, используя дерево префикса. Я думаю, это может помочь вам, так как ваше дело звучит похоже.

Если вы знаете определенный предел длины строки ввода, вы можете ограничить рост своего Trie, чтобы он не хранил никаких префиксов дольше, чем эта длина max. Таким образом, вы можете установить Trie, представляющий все 10G, в менее чем 10G памяти. Для сильно повторяющихся данных любой вид Trie представляет собой сжатое представление данных. (Или должно быть быть, если оно реализовано.) Ограничение глубины Trie до строки ввода максимального ввода позволяет еще больше ограничить потребление памяти.

+0

Обычно при поиске вы хотите получить не только ответ «да/нет», но и место матча. Из-за этого обычно требуется дополнительная память для хранения местоположений подстрочных совпадений. Поэтому, даже если вы сможете хранить 10G данных в памяти менее 10G, вам потребуется гораздо больше, чтобы хранить информацию о местоположении. –

+0

Крис, что заставляет вас думать, что префиксные деревья не дают вам этого? Все зависит от того, как вы выполняете «префиксное завершение». Если вы реализуете это как список удаленных мест, все готово. Это реализация, используемая в цитированной мною статье. Я знаю, что деревья суффикса работают так же хорошо, как деревья префикса - оба дают решение O (m). Итак, я дал вам +1. –

+0

Ты абсурдно прав! Извините, я просто искал тот факт, что есть скрытые затраты с индексацией, за исключением того, что они хранят исходный текст, который не обязательно отображается в сложности пространства. Другими словами, хранение дополнительного списка мест с каждым префиксом не является бесплатным :) –

0

Если вы можете позволить себе пространство (много места!), Чтобы создать индекс, то определенно стоило бы вам индексировать небольшие куски (например, четыре байтовых блока) и хранить их со своими смещениями в стоге сена - тогда поиск 10 байтов включает в себя поиск всех четырех байтовых блоков для первых четырех байтов поискового термина и проверку следующих шести байтов.

3

Стоит посмотреть на suffix arrays и trees. Они оба требуют предварительной вычисления и значительной памяти, но они лучше, чем обратные индексы, в том смысле, что вы можете искать произвольные подстроки в O(m) (для деревьев суффиксов) и O(m + log n) (для массивов суффиксов с наименьшей общей информацией о префиксах). не

Если у вас есть много времени на ваших руках, вы можете посмотреть в compressed suffix arrays и малообъемных ДПМ, которые сжимаются версии ваших данных, которые также являются самостоятельной индексации (т.е. данные также индекс, нет внешний индекс). Это действительно лучший из всех миров, потому что у вас нет сжатой версии ваших данных (вы можете выбросить исходные данные), но она также индексируется! Проблема заключается в понимании исследовательских работ и перевода их в код :)

Если вам не нужны идеальные подстроки, но, скорее, общие возможности поиска, посмотрите Lucene.

+0

Из интереса, Крис, сколько памяти потребуется для использования такого подхода? –

+0

Для массивов суффиксов это 'O (n)', но константы могут быть значительными после того, как вы начнете хранить вспомогательную информацию для ускорения поиска (например, информацию LCP). Для деревьев суффиксов это 'O (n^2)', я считаю, что, вероятно, было бы непомерно на 10-гигабайтном наборе данных. Последние исследования в области поиска информации, которые я видел, имеют индексы для сжатой версии данных, что, как представляется, дает значительную экономию. –

+1

На 10G очень повторяющихся данных (я подозреваю, что мы говорим о ДНК), даже не сжатое суффиксное дерево будет, по сути, сжимать данные. –

3

Учитывая, что Knuth-Morris-Pratt или Boyer-Moore вы не собираетесь делать ничего лучше, то, что вы должны рассмотреть, это распараллеливание процесса поиска.

+2

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

+0

При правильной индексации вы можете сделать намного лучше, чем любой из этих алгоритмов - даже если у вас есть 10G физической памяти, чтобы выполнять их без попадания на диск! –

+4

@ Chris, @ Will: Исправьте меня, если я ошибаюсь, но что делают индексы, связанные с поиском арбинных строк? Кажется, задача такова: нужно найти короткий блок данных в большом блоке неструктурированной части данных.Создание индекса для каждой возможной строки может привести к тому, что индекс будет содержать большой блок данных. Я думаю, что это случай, когда люди прыгают впереди требований, никто из вас не работает для CA или MS случайно? :) – 2010-08-07 23:54:06

3

Префикс/суффикс дерева, как правило, стандартный, лучший и самый осторожный решение для такого рода вещей, на мой взгляд. Вы не можете ошибиться с ними.

Но вот другая идея, которая относится к Bloom filters. Вероятно, вы знаете, что это такое, но на всякий случай (и для других людей, читающих этот ответ): Bloom-фильтры - очень маленькие, очень компактные битовые векторы, которые приближенно устанавливают включение. Если у вас есть множество S и фильтр Блума для этого множества B (S), затем

x ∈ S ⇒ x ∈ B(S) 

но взаимность ложно. Это то, что вероятностно относительно структуры: существует вероятность (quantifiable), что фильтр Bloom вернет ложный положительный результат. Но приближение включения с фильтром Блума дико быстрее, чем тестирование его точно на множестве.

(Простой случай использования: в большом количестве приложений, фильтр Bloom используется, ну, как фильтр Проверка кэша дорого, потому что вы должны сделать доступ жесткого диска, так что программы, как. Squid сначала проверит небольшой фильтр Bloom в памяти, и если фильтр Bloom вернет положительный результат, Squid пойдет проверять кеш. Если он был ложным, то это нормально, потому что Squid обнаружит это при фактическом посещении кеша --- но преимущество заключается в том, что фильтр Bloom пощадил Squid, чтобы проверить кеш для большого количества запросов, где это было бы бесполезно.)

Цветные фильтры использовались с некоторым успехом в строке поиск. Вот эскиз (я могу вспомнить некоторые неправильные детали) этого приложения. Текстовым файлом является последовательность из N строк. Вы ищете слово, состоящее из букв M (и ни одно слово не может быть распространено по двум строкам). Фаза предварительной обработки будет строить ONE Bloom-фильтр для каждой строки, добавив каждую подпоследовательность линии к фильтру Bloom; например, для этой линии

Touching this dreaded sight, twice seene of vs, 

И соответствующий Bloom фильтр будет создан с «Т», «К», «Тоу» ... «о», «НУ», ... «против, "," s "," s ",", ". (Возможно, эта часть может быть неправильной. Или вы можете оптимизировать.)

Затем, при поиске подслова размера M, просто выполните очень быструю проверку на каждом из фильтров Bloom, и когда есть удар, например, изучить линию близко к алгоритму KMP. На практике, если вы хорошо настраиваете фильтры Bloom, компромисс замечателен. Поиск невероятно быстрый, потому что вы устраняете все бесполезные линии.

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

  • либо вырезать ваши данные установки во многих блоках размером K (каждый со своим фильтром Блума, как строки в предыдущем примере);

  • или используйте разновидность дихотомии, в которой вы разбиваете набор на два подмножества, каждый с фильтром Блума, затем каждое подмножество разбивается на два подмножества с собственным фильтром Блума и т. Д. (Если вы собираетесь добавьте все подстроки, как было предложено с помощью описанного метода, эта вторая идея была бы немного запретительной - за исключением того, что вам не нужно добавлять все подстроки, только подстроки размером от 1 до 10).

Обе идеи могут быть объединены изобретательскими способами для создания многослойных схем.

+0

+1 это так здорово учиться, спасибо. –

Смежные вопросы