Более быстрое тестирование членства в python, чем set()

Мне нужно проверить наличие миллионов элементов (20-30 букв str) в списке, содержащем 10-100 тыс. этих элементов. Есть ли более быстрый способ сделать это в python, чем set()?

import sys
#load ids
ids = set( x.strip() for x in open(idfile) )

for line in sys.stdin:
    id=line.strip()
    if id in ids:
        #print fastq
        print id
        #update ids
        ids.remove( id )

Ответ 1

set работает так же быстро, как и он.

Однако, если вы переписываете свой код для создания set один раз и не меняете его, вы можете использовать встроенный тип frozenset. Это точно так же, кроме непреложного.

Если у вас все еще есть проблемы со скоростью, вам нужно ускорить вашу программу другими способами, например, используя PyPy вместо cPython.

Ответ 2

Как я уже отмечал в своем комментарии, что, вероятно, замедлило то, что вы последовательно проверяете каждую строку из sys.stdin для членства в вашем "master". Это будет действительно, очень медленно и не позволит вам использовать скорость заданных операций. В качестве примера:

#!/usr/bin/env python

import random

# create two million-element sets of random numbers
a = set(random.sample(xrange(10000000),1000000))
b = set(random.sample(xrange(10000000),1000000))
# a intersection b
c = a & b
# a difference c
d = list(a - c) 
print "set d is all remaining elements in a not common to a intersection b"
print "length of d is %s" % len(d)

Вышеуказанное работает в ~ 6 секундах секунд на моей пятилетней машине, и оно тестирует членство в более крупных наборах, чем вам нужно (если только я не понял вас). Большая часть этого времени фактически занята созданием наборов, поэтому у вас даже не будет этих накладных расходов. Тот факт, что строки, на которые вы ссылаетесь, длинны, здесь не уместен; создание набора создает хэш-таблицу, как объясняет agf. Я подозреваю (хотя, опять же, неясно, из вашего вопроса), что если вы можете получить все свои входные данные в набор до того, как будете проходить тестирование на членство, это будет намного быстрее, в отличие от чтения его в одном элементе на время, затем проверка наличия членства

Ответ 3

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

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

Это будет выглядеть так:

ids = {}
for id in open(idfile):
    ids.setdefault(id[0], set()).add(id)

for line in sys.stdin:
    id=line.strip()
    if id in ids.get(id[0], set()):
       #print fastq
       print id
       #update ids
       ids[id[0]].remove( id )

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

Это первый шаг, вы можете сделать то же самое со вторым символом и т.д., поиск будет просто идти по дереву с каждой буквой...

Ответ 4

Как упоминалось urschrei, вы должны "векторизовать" чек. Быстрее проверять наличие миллиона элементов один раз (как это делается на C), чем проверять один элемент на миллион раз.