официальное название этого подхода программирования для вычисления объединения и пересечения - PullRequest
8 голосов
/ 06 января 2010

Я [конечно, заново] изобрел это [колесо], когда хотел вычислить объединение, а также пересечение и разность двух множеств (сохраненных в виде списков) одновременно. Исходный код (не самый короткий):

dct = {}
for a in lst1:
  dct[a] = 1
for b in lst2:
  if b in dct:
    dct[b] -= 1
  else:
    dct[b] = -1

union = [k for k in dct]
inter = [k for k in dct if dct[k] == 0]
oneminustwo = [k for k in dct if dct[k] ==  1]
twominusone = [k for k in dct if dct[k] ==  -1]

Тогда я понял, что я должен использовать 00, 01, 10 и 11 вместо -1, 1, 0, ... Таким образом, бит в позиции n указывает членство в наборе n.

Это может быть обобщено до 32 наборов, использующих 32-битное целое число, или до любого количества наборов, использующих битовый массив или строку. Таким образом, вы предварительно вычисляете этот словарь один раз, а затем используете очень быстрые O (n) запросов для извлечения элементов, представляющих интерес. Например, все 1 означает пересечение всех множеств. Все 0 - особые - не произойдут.

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

Ответы [ 4 ]

7 голосов
/ 06 января 2010

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

Сформулируйте хеш-функцию, которая показывает, чем она отличается от массива:

int bitset_hash(int n) {
  // domain of this function is only non-negative ints
  return 1 << n;
}

Обратите внимание, что bitset_hash (3) имеет значение 0b1000, что соответствует 4-му элементу (смещение / индекс 3) при использовании C int и побитовых операций. (Из-за деталей реализации хранилища побитовые операции также используются для манипулирования конкретным элементом из хеша.)

Расширение подхода к использованию побитового и / -or / -xor для операций над множествами распространено и не требует какого-либо специального имени, кроме "операций над множествами" или, если вам нужно модное слово , "теория множеств".

Наконец, вот еще один пример его использования в простом сите (я использовал этот код в решениях Project Euler):

class Sieve(object):
  def __init__(self, stop):
    self.stop = stop
    self.data = [0] * (stop // 32 // 2 + 1)
    self.len = 1 if stop >= 2 else 0
    for n in xrange(3, stop, 2):
      if self[n]:
        self.len += 1
        for n2 in xrange(n * 3, stop, n * 2):
          self[n2] = False

  def __getitem__(self, idx):
    assert idx >= 2
    if idx % 2 == 0:
      return idx == 2
    int_n, bit_n = divmod(idx // 2, 32)
    return not bool(self.data[int_n] & (1 << bit_n))

  def __setitem__(self, idx, value):
    assert idx >= 2 and idx % 2 != 0
    assert value is False
    int_n, bit_n = divmod(idx // 2, 32)
    self.data[int_n] |= (1 << bit_n)

  def __len__(self):
    return self.len

  def __iter__(self):
    yield 2
    for n in xrange(3, self.stop, 2):
      if self[n]:
        yield n
4 голосов
/ 06 января 2010

Да, иногда он используется в базах данных, например PostgreSQL. Как упоминает Википедия:

Некоторые системы баз данных, которые не предложить использование постоянных растровых индексов растровые изображения внутри, чтобы ускорить запрос обработка. Например, PostgreSQL версии 8.1 и позже реализуют оптимизация «сканирования растрового индекса» в ускорить произвольно сложный логический операции между доступными индексами на одном столе.

(из http://en.wikipedia.org/wiki/Bitmap_index)

2 голосов
/ 06 января 2010

Очень часто используется целое число для представления набора маленьких целых чисел; его часто называют bitset или bitvector . Здесь вы используете целое число для представления «набора входных последовательностей, содержащих это значение».

Операция, которую вы делаете, напоминает мне о обращении к мультикарте .

В вашем случае вход представляет собой список списков:

[["a", "b"], ["a", "c", "d"]]

Но вместо этого вы можете думать об этом как о сумке упорядоченных пар, как это:

0, "a"
0, "b"
1, "a"
1, "c"
1, "d"

Вы просто создаете таблицу, содержащую перевернутые пары

"a", 0
"b", 0
"a", 1
"c", 1
"d", 1

, который выглядит так:

{"a": [0, 1],
 "b": [0],
 "c": [1],
 "d": [1]}

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

Исходная структура данных (список списков) позволяла легко перебирать все значения для данного списка. Обращенная структура данных (словарь списков) позволяет легко найти все списки с заданным значением.

1 голос
/ 06 января 2010

Является ли идея битового поля тем, что вы ищете? Каждый бит вашего ... поля (из-за отсутствия лучшего слова) представляет флаг. В этом случае вашим флагом является членство в наборе N.

Редактировать - мне кажется, я неправильно понял, на какую идею вы ссылались. Игнорирование

...