Оптимизирован поиск в Python по списку

1

Проблема:

Учитывая список из n объектов (n порядка величины 10 ^ 5), найдите данный элемент очень быстро с минимальным компромиссом пространства-времени. Текущее, неоптимизированное и прототипное решение занимает слишком много времени и потребляет слишком много ОЗУ (оптимизация не является преждевременной, то есть).

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

o1 => f, g, h
o2 => f, g, i
o3 => f, j, k
o4 => k, j, m

На сегодняшний день решением были вложенные фильтры:

filter(test1, filter(test2, filter(test3, the_list)))

Но это было медленным, так как оно включает в себя операции n * (n - 1) * (n - 2), которые приближаются к скорости O (n ^ 3) и, по крайней мере, n * 2 дополнительных списков ссылок.

В качестве примечания было бы весьма желательно провести поиск на месте.

Я не нашел стандартную библиотеку для обработки этого. Каково типичное решение этой проблемы?

  • 0
    Являются ли ваши значения уникальными или они могут встречаться в списке несколько раз?
  • 1
    Какова природа test1 т. Д.?
Показать ещё 10 комментариев
Теги:
optimization

5 ответов

1
filter(test1, filter(test2, filter(test3, the_list)))

Во-первых, это время O (n), а не O (n ^ 3). Время добавляется не умножается. Единственное, что может быть хуже, это то, что если test3/test2/test1 делают что-то странное, в котором мы должны смотреть на них.

Если мы предположим, что каждый тест? функция принимает 10 мс, то мы имеем 10 * 3 * 10 ^ 5 мс = 50 минут. Если бы это было n ^ 3, то мы имели бы (10 * 10 ^ 5) ^ 3 = 31 миллион лет. Я уверен, что вы всего лишь одно линейное время, у вас просто тонна данных.

Замените фильтр с помощью itertools.ifilter, это позволит избежать создания списка. Вместо этого python вытаскивает один элемент из списка за один раз, передает его через три теста и передает его вам, если и только если он пройдет. Это позволит избежать необходимости в памяти и, вероятно, быстрее.

Вы не сможете улучшить время O (n), если не используете некоторые методы индексирования. Однако применимость методов индексирования зависит от того, что вы делаете внутри функций test1/test2/test3. Если вам нужна помощь в этом, покажите пример для этих функций.

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

  • 0
    O (n): я не убежден. Каждый фильтр - это O (n), нет? Так что это тройной вложенный цикл; но удаления удаляют кучу данных ... нет?
  • 0
    @Paul, фильтры не вложенные. Они запускаются один за другим. Фильтр принимает входной список, просматривает список, проверяет результаты теста и возвращает новый список с результатом. Этот список затем передается следующей функции.
Показать ещё 1 комментарий
0

Мне кажется, что одним из типичных решений будет использование запроса к базе данных. Либо SQL (raw или с каким-то ORM), либо какая-то база данных объектов, возможно, MongoDB?

  • 0
    Есть два уровня: один слой в памяти и один слой хранения. Уровень хранения почти наверняка не может быть базой данных из-за бизнес-ограничений. Слой in-memory - это то, на чем я сосредоточен: sqlite in-memory может быть подходящим способом.
0

Если ваши данные находятся в CSV файле, вы можете попробовать sql2csv: https://sourceforge.net/projects/sql2csv/.

РЕДАКТИРОВАТЬ: Простите мою раннюю начальную старость, я имел в виду этот проект: https://github.com/ccoffey/sql4csv/wiki/Examples.

  • 0
    это интересно, но это инструмент .net.
  • 0
    Уф! Я имел в виду этот проект: github.com/ccoffey/sql4csv/wiki/Examples , который, я уверен, является чистым Python.
0

10 ^ 5 - это не очень большое количество объектов, даже в памяти. littletable - это небольшой модуль, который я написал как эксперимент для моделирования запросов, опорных точек и т.д., используя только Python dicts. Одна приятная вещь о нерешимых запросах заключается в том, что результат любого запроса или соединения сам по себе является новой таблицей с неограниченным доступом. Индексы сохраняются в виде dicts объектов keys-> table, а индексные ключи могут быть определены как уникальные или нет.

Я создал таблицу из 140K объектов с 3 однобуквенными клавишами, а затем запросил конкретный ключ. Время для создания самой таблицы было самым длинным, индексирование и запрос довольно быстро.

from itertools import product
from littletable import Table,DataObject

objects = Table()
alphas = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
alphas += alphas.lower()
import time

print "building table", time.time()
objects.insert_many(
    DataObject(k1=k1, k2=k2, k3=k3, created=time.time())
        for k1,k2,k3 in product(alphas.upper(),alphas,alphas)
    )
print "table complete", time.time()
print len(objects)

print "indexing table", time.time()
for k in "k1 k2 k3".split():
    objects.create_index(k)
print "index complete", time.time()

print "get specific row", time.time()
matches = objects.query(k1="X", k2="k", k3="W")
for o in matches:
    print o
print time.time()

Печать:

building table 1309377011.63
table complete 1309377012.52
140608
indexing table 1309377012.52
index complete 1309377012.98
get specific row 1309377012.98
{'k3': 'W', 'k2': 'k', 'k1': 'X', 'created': 1309377011.9960001}
{'k3': 'W', 'k2': 'k', 'k1': 'X', 'created': 1309377012.4260001}
1309377013.0
0

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

  • 0
    Какова стоимость пространства хэш-таблицы с 100 000 элементов?
  • 0
    Не нужно все это делать. Используйте set , который использует хеширование и имеет свой собственный способ борьбы со столкновениями. Набор по сути является хеш-таблицей. Единственная проблема заключается в том, что ваши элементы не являются хэшируемыми (что вы можете узнать, попытавшись поместить их в набор).
Показать ещё 2 комментария

Ещё вопросы

Сообщество Overcoder
Наверх
Меню