Поиск наборов, которые имеют определенные подмножества - PullRequest
4 голосов
/ 30 января 2009

Я аспирант физики, и я работаю над написанием некоторого кода, чтобы сортировать несколько сотен гигабайт данных и возвращать фрагменты этих данных, когда я их запрашиваю. Вот хитрость, я не знаю хорошего метода для сортировки и поиска данных такого рода.

Мои данные в основном состоят из большого числа наборов чисел. Эти наборы могут содержать от 1 до n чисел в них (хотя в 99,9% наборов n меньше 15), и таких наборов приблизительно 1,5–2 миллиарда (к сожалению, этот размер исключает поиск методом перебора).

Мне нужно иметь возможность указать набор с k элементами, и каждый набор с k + 1 или более элементами, который содержит указанное подмножество, возвращен мне.

Простой пример:
Предположим, у меня есть следующие наборы для моих данных:
(1,2,3)
(1,2,3,4,5)
(4,5,6,7)
(1,3,8,9)
(5,8,11)

Если бы я дал запрос (1,3), у меня были бы наборы: (1,2,3), (1,2,3,4,5) и (1,3,8,9).
Запрос (11) вернет набор: (5,8,11).
Запрос (1,2,3) вернет наборы: (1,2,3) и (1,2,3,4,5)
Запрос (50) не вернул бы наборы:

К настоящему времени картина должна быть четкой. Основное различие между этим примером и моими данными состоит в том, что наборы с моими данными больше, числа, используемые для каждого элемента наборов, варьируются от 0 до 16383 (14 битов), и есть еще много-много других наборов.

Если это важно, я пишу эту программу на C ++, хотя я также знаю java, c, некоторую сборку, немного fortran и немного perl.

У кого-нибудь есть какие-нибудь подсказки, как это осуществить?

редактировать:
Чтобы ответить на пару вопросов и добавить несколько пунктов:

1.) Данные не меняются. Все это было сделано за один длинный набор прогонов (каждый разбит на 2 гигабайта).

2.) Что касается места для хранения. Необработанные данные занимают примерно 250 гигабайт. По моим оценкам, после обработки и удаления большого количества посторонних метаданных, которые меня не интересуют, я мог бы сократить их до 36-48 гигабайт в зависимости от того, сколько метаданных я решил сохранить (без индексов). Кроме того, если при первоначальной обработке данных я обнаружу достаточно одинаковых наборов, я смогу еще больше сжать данные, добавив счетчики для повторяющихся событий, а не просто повторяя события снова и снова.

3.) Каждое число в обработанном наборе фактически содержит по меньшей мере два числа 14 битов для самих данных (обнаруженная энергия) и 7 битов для метаданных (номер детектора). Так что мне понадобится как минимум три байта на число.

4.) Мой комментарий «хотя в 99,9% комплектов, n меньше 15» вводил в заблуждение. Предварительно просматривая некоторые фрагменты данных, я обнаружил, что у меня есть наборы, которые содержат целых 22 числа, но медиана составляет 5 номеров на набор, а среднее значение составляет 6 номеров на набор.

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

6.) С точки зрения доступных мне ресурсов, я могу собрать приблизительно 300 гигабайт пространства после того, как у меня есть необработанные данные в системе (остаток моей квоты в этой системе). Система представляет собой двухпроцессорный сервер с 2 четырехъядерными процессорами и 16 гигабайтами оперативной памяти.

7.) Да, 0 может иметь место, это артефакт системы сбора данных, когда это происходит, но это может произойти.

Ответы [ 6 ]

11 голосов
/ 30 января 2009

Ваша проблема такая же, как и у поисковых систем. «У меня есть баджиллионные документы. Мне нужны те, которые содержат этот набор слов». У вас есть (очень удобно) целые числа вместо слов и небольшие документы. Решением является инвертированный индекс . Введение в поиск информации от Manning et al. (По этой ссылке) доступно бесплатно онлайн, очень читабельно и подробно расскажет о том, как это сделать.

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

2 голосов
/ 11 сентября 2009

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

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

http://www.ddj.com/184410998
и
http://www.dcs.bbk.ac.uk/~jkl/publications.html

2 голосов
/ 30 января 2009

Мое предположение следующее.

Предположим, что у каждого набора есть имя, идентификатор или адрес (4-байтовое число подходит, если их всего 2 миллиарда).

Теперь пройдитесь по всем наборам один раз и создайте следующие выходные файлы:

  • Файл, который содержит идентификаторы всех наборов, которые содержат '1'
  • Файл, который содержит идентификаторы всех наборов, которые содержат '2'
  • Файл, который содержит идентификаторы всех наборов, которые содержат '3'
  • ... и т.д ...

Если в наборе 16 записей, то в среднем каждый из этих 2 ^ 16 файлов будет содержать идентификаторы 2 ^ 20 наборов; с каждым идентификатором 4 байта для этого потребуется 2 ^ 38 байт (256 ГБ) памяти.

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

Когда вы получаете запросы, используйте эти файлы следующим образом:

  • Посмотрите на пару цифр в запросе
  • Откройте пару соответствующих индексных файлов
  • Получить список всех наборов, которые существуют в обоих этих файлах (в каждом файле только миллион идентификаторов, так что это не должно быть сложно)
  • Посмотрите, какой из этих нескольких наборов удовлетворяет оставшейся части запроса

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

2 голосов
/ 30 января 2009

Предполагая случайное распределение 0-16383, с последовательными 15 элементами на набор и двумя миллиардами наборов, каждый элемент будет отображаться приблизительно в 1,8 млн. Наборов. Рассматривали ли вы (и обладаете ли вы возможностями) создание таблицы поиска 16384x ~ 1,8M (30B записей по 4 байта каждая)? Имея такую ​​таблицу, вы можете запросить, какие наборы содержат (1) и (17) и (5555), а затем найти пересечения этих трех списков ~ 1,8M-элементов.

1 голос
/ 30 января 2009

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

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

Если ваши значения распределены равномерно, каждый индексный файл будет содержать 1/16383 входных наборов. Если ваш средний набор поиска состоит из 6 значений, вы будете выполнять линейный проход в течение 6/16383 от вашего исходного ввода. Это все еще решение O (n), но ваш n теперь немного меньше.

P.S. Ноль - невозможное значение результата, или у вас действительно есть 1638 4 возможностей?

0 голосов
/ 30 января 2009

Просто игра адвокат дьявола за подход, который включает в себя грубую силу + индекс поиска:

  1. Создайте индекс с минимальным, максимальным и без элементов множеств.
  2. Затем примените грубую силу, исключая наборы, где max min (поиск набора)
  3. В грубой силе также исключаются наборы, общее количество элементов которых меньше, чем в наборе, в котором выполняется поиск.

95% ваших запросов действительно были бы грубым принуждением к очень небольшому подмножеству. Просто мысль.

...