2009-08-26 1 views
8

У меня есть набор больших длинних строк, которые я хочу делать для поиска существования. Мне не нужна вся строка, которую нужно сохранить. Насколько я могу судить, set() фактически сохранил строку, которая поедает много моей памяти.Python: Установить только проверку наличия?

Существует ли такая структура данных?

done = hash_only_set() 
while len(queue) > 0 : 
    item = queue.pop() 
    if item not in done : 
     process(item) 
     done.add(item) 

(Моя очередь постоянно заполнена другими потоками, так что я не имею никакого способа dedupping его в начале).

+0

Кстати, вы имеете отношение к * the * Tarjan? – yairchu

+0

+1, этот вопрос привел очень интересные ответы. –

+0

Насколько велика партия? – u0b34a0f6ae

ответ

10

Это, конечно, можно сохранить набор только хэшей:

done = set() 
while len(queue) > 0 : 
    item = queue.pop() 
    h = hash(item) 
    if h not in done : 
     process(item) 
     done.add(h) 

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

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

А альтернативно: если вы не можете принять, чтобы сохранить строки в памяти, сохранить их в базе данных или создать файлы в каталоге с тем же именем, что и строка.

+0

Ваш метод сохранит хэш дважды? один раз в качестве ключа в таблице и один раз в качестве значения? –

+0

Использование хэша ... Очень умно :-) –

+1

Используя встроенный хэш, у вас будет много шансов найти столкновений (это всего лишь 32 бита). – tonfa

4

Для этой цели можно использовать структуру данных Bloom Filter. Реализация Python может быть найдена here.

EDIT: Важные примечания:

  1. Ложноположительные является возможными в этой структуре данных, то есть проверка на наличие строки может вернуть положительный результат, даже если он был не хранится ,
  2. Ложные негативы (получение отрицательного результата для сохраненной строки) невозможны.

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

+0

С фильтром цветения возможны ложные срабатывания, которые, я думаю, вызывают это из-за того, чего хочет OP. Я уверен, что он не захочет, чтобы один из его предметов не обрабатывался из-за ложного позитива. –

+1

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

+0

Спасибо за литературу. – u0b34a0f6ae

2

Вам нужно будет подумать о том, как выполнить поиск, поскольку для этого необходимы два метода, __hash__ и __eq__.

Хэш - это «свободная часть», которую можно отнять, но __eq__ не является свободной частью, которую вы можете сохранить; вы должны иметь две строки для сравнения.

Если вам нужно только отрицательное подтверждение (этот элемент не является частью набора), вы можете заполнить набор, который вы внедрили самими своими строками, затем вы «завершаете» набор, удалив все строки, кроме тех, которые имеют столкновений (они хранятся для экв тестов), и вы обещаете не добавлять в свой комплект больше объектов. Теперь у вас есть эксклюзивный тест. Вы можете узнать, не является ли объект не в вашем Комплекте. Вы не можете быть уверены, что «obj in Set == True» является ложным положительным или нет.

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

Edit2: Это мой 3-минутный цветение фильтр:

class BloomFilter (object): 
    """ 
    Let's make a bloom filter 
    http://en.wikipedia.org/wiki/Bloom_filter 

    __contains__ has false positives, but never false negatives 
    """ 
    def __init__(self, hashes=(hash,)): 
     self.hashes = hashes 
     self.data = set() 
    def __contains__(self, obj): 
     return all((h(obj) in self.data) for h in self.hashes) 
    def add(self, obj): 
     self.data.update(h(obj) for h in self.hashes) 
3

Вы должны знать всю строку, чтобы иметь 100% уверенность. Если у вас много строк со схожими префиксами, вы можете сэкономить место, используя trie для хранения строк. Если ваши строки длинны, вы также можете сэкономить место, используя большую хеш-функцию, такую ​​как SHA-1, чтобы сделать возможной хеш-коллизии настолько удаленной, чтобы быть неактуальной.

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

+0

Это очень хорошее предложение. Вы можете сохранить всю строку памяти только для внешней (промилле или менее?) Дополнительной стоимости процессора. – u0b34a0f6ae

4

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

Метод builtin __hash__() не гарантирует, что у вас не будет дубликатов (и поскольку он использует только 32 бита, очень вероятно, что вы найдете некоторые).

+0

Если хэш-код строки Python поддерживается, может быть разумным использовать хеш-строку с <65000 строк: http://stackoverflow.com/questions/1303021/shortest-hash-in-python-to-name-cache-files/ 1303619 # 1303619 – u0b34a0f6ae

+0

Использование безопасного хэша не требуется. Secure! = Низкая вероятность столкновения. Безопасное просто означает, что невозможно сделать определенный хэш с «неправильными» данными. – truppo

+1

@truppo Если вы посмотрите на http://en.wikipedia.org/wiki/Cryptographic_hash_function, вы увидите, что низкая вероятность столкновения является частью свойств идеального криптографического хэша. – tonfa

0

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

Модуль zlib Python предоставляет встроенные возможности сжатия строк и может использоваться для предварительной обработки строк перед их помещением в ваш набор. Обратите внимание, однако, что строки должны быть довольно длинными (что вы намекаете на то, что они есть) и имеют минимальную энтропию, чтобы сэкономить много места в памяти. Другие варианты сжатия могут обеспечить лучшую экономию пространства, а некоторые версии на основе Python можно найти. here