2013-08-03 5 views
4

У меня есть очень большой список под названием «данные», и я должен отвечать на запросы, эквивалентныеБыстрого членства в срезах списков

if (x in data[a:b]): 

для различных значений а, Ь, х.

Можно ли предварительные обработка данных, чтобы сделать эти запросы быстро

+4

Возможно, вам будет нужно добавить дополнительную информацию о возможностях 'x',' a' и 'b'. Многие разные 'x'es для одного экземпляра' [a: b] 'или' '' 'проверены на множество разных' [a: b] 'срезов ...? Кроме того, возможно ли сортировка данных? –

+0

У меня может быть много разных x для каждой пары a, b. Данные списка не отсортированы грустно. – phoenix

ответ

4

идее

вы можете создать dict. Для каждого элемента хранится отсортированный список позиций, где он встречается.

Чтобы ответить на запрос: бинарный поиск первый элемент, который больше или равен a, проверьте, если он существует, и меньше, чем b

ПСЕВДОКОД

Preprocessing:

from collections import defaultdict 

byvalue = defaultdict(list) 

for i, x in enumerate(data): 
    byvalue[x].append(i) 

Запрос:

def has_index_in_slice(indices, a, b): 
    r = bisect.bisect_left(indices, a) 

    return r < len(indices) and indices[r] < b 

def check(byvalue, x, a, b): 
    indices = byvalue.get(x, None) 
    if not indices: return False 

    return has_index_in_slice(indices, a, b) 

Сложность O(log N) за запрос здесь, если мы предположим, что list и dict имеют сложность «получить по индексу» O (1).

+0

Модуль ['bisect'] (http://docs.python.org/2/library/bisect.html) предоставляет инструменты для упрощения двоичного кода поиска и делает его намного быстрее. – user2357112

+0

@ user2357112 спасибо, добавлено. – RiaD

+2

Я отредактировал ответ, чтобы удалить прокомментированный код, и сделал его так, чтобы он только разыскивал ключ 'x' один раз, а не дважды. Кроме того, я сделал эстетические изменения, чтобы сделать ответ более питоновым (без точек с запятой, без '!', 'Defaultdict' вместо ручной проверки,' enumerate (data) 'вместо' range (len (data)) '.) – orlp

1

Да, вы могли бы эти ломтики предварительную обработку в наборы, тем самым делая членство поиск O(1) вместо O(n):

check = set(data[a:b]) 
if x in check: 
    # do something 
if y in check: 
    # do something else 
+0

-1 Это не отвечает на вопрос. Если я не ошибаюсь, вопрос хочет препроцитировать «данные» таким образом, что в будущих запросах членства на произвольных срезах быстрая, а не предварительная вычисление одного набора для одного конкретного среза. – orlp

+0

@ nightcracker: Я не уверен, так как он форматировал слово 'data' по-разному. Возможно, вы правы, и в этом случае ответ, вероятно, будет «нет». К сожалению, феникс не отреагировал на мою просьбу о разъяснении ... –

+0

А я не видел твоих разъяснений. Я думаю, что, поскольку 'a, b' являются частью запроса, они могут различаться. Но ответ, безусловно, да, см. Ответ RiaD. – orlp

0

Поместите список в базу данных и воспользоваться встроенными в индексации, оптимизации и кэширования. Например, из руководства PostgreSQL:

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

Но вы также можете использовать sqlite для простоты (и доступности в стандартной библиотеке Python). От Python's documentation, regarding indexing:

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

Он поддерживает сопоставление доступа по имени столбца и индексу, итерации, представлению, проверке равенства и len().

И в другом месте на этой странице:

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

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