детектор двоичной последовательности - PullRequest
3 голосов
/ 05 августа 2009

Кто-нибудь знает об оптимальном способе обнаружения 37-битной последовательности в порции двоичных данных? Конечно, я могу сделать сравнение методом грубой силы с использованием окон (просто сравните, начиная с индекса 0 + следующие 36 бит, инкремент и цикл, пока я его не найду), но есть ли лучший способ? Может быть, какой-нибудь поиск хэширования, который возвращает вероятность того, что последовательность лежит в двоичном фрагменте? Или я просто вытаскиваю это из моей задницы? В любом случае, я продолжаю поиск грубой силы, но мне было любопытно, было ли что-то более оптимальное. Кстати, в Си.

Ответы [ 6 ]

6 голосов
/ 05 августа 2009

Вы можете рассматривать биты как символы из алфавита {0,1} и запускать любой из нескольких относительно эффективных известных алгоритмов поиска подстроки в данных.

4 голосов
/ 05 августа 2009

Интересный вопрос. Я предполагаю, что ваша 37-битная последовательность может начинаться в любой точке байта. Допустим, ваша последовательность представлена ​​следующим образом:

ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789@

Если у нас есть алгоритм выравнивания байтов, мы могли бы видеть эти байты 32-битных последовательностей:

BCDEFGHIJKLMNOPQRSTUVWXYZ0123456 [call this pattern w_A]
CDEFGHIJKLMNOPQRSTUVWXYZ01234567 [w_B, etc.]
DEFGHIJKLMNOPQRSTUVWXYZ012345678
EFGHIJKLMNOPQRSTUVWXYZ0123456789
FGHIJKLMNOPQRSTUVWXYZ0123456789@
GHIJKLMNOPQRSTUVWXYZ0123456789@x
HIJKLMNOPQRSTUVWXYZ0123456789@xx
IJKLMNOPQRSTUVWXYZ0123456789@xxx

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

Это приводит к достаточно очевидной реализации:

unsigned char *p = ...; // input data
size_t n = ...;  // bytes available
size_t bitpos;

--n; p++;
bitpos = 0;

while (n--) {
  uint32_t word = *(uint32_t*)p; // nonportable, sorry.
  bitpos += 8; // compiler should be able to optimise this variable out completely

  if (word == w_A) {
    if ((p[4] & 0xF0 == 789@) && (p[-1] & 1 == A)) {
      // we found the data starting at the 8th bit of p-1
      found_at(bitpos-1);
    }
  } else if (word == w_B) {
    if ((p[4] & 0xE0 == 89@) && (p[-1] & 3 == AB)) {
      // we found the data starting at the 7th bit of p-1
      found_at(bitpos-2);
    }
  } else if (word == w_C} {
     ...
  }
...
}

Очевидно, есть проблемы с этой стратегией. Во-первых, он может захотеть оценить p [-1] в первый раз в цикле, но это легко исправить. Во-вторых, он выбирает слово с нечетных адресов; это не будет работать на некоторых процессорах - SPARC и 68k, например. Но это простой способ объединить 4 сравнения в одно.

Предложение kek444 позволит вам использовать алгоритм, подобный KMP, для пропуска вперед в потоке данных. Однако максимальный размер пропуска не велик, поэтому, хотя алгоритм Turbo Boyer-Moore может уменьшить число сравнений байтов на 4 или около того, это может быть не слишком выигрышным, если стоимость сравнения байтов аналогична стоимость сравнения слов.

1 голос
/ 05 августа 2009

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

XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX...
<--            N bits           -->
<--   'ugly' M bits    -->|<-- continue here

Это должно сократить его немного.

Конечно, один из наиболее эффективных методов - это анализировать входные данные с помощью конечного автомата, например DFA , но это кажется избыточным. Зависит от вашего сценария использования.

0 голосов
/ 29 августа 2009

Вот один из способов: найти вашу единственную целевую цепочку битов можно свести к поиску любого из определенного набора байтовых строк. Существуют быстрые методы решения этой проблемы, например Aho-Corasick , который выполняет поиск за один проход, никогда не создавая резервных копий, во времени, не зависящем от размера целевого набора.

(Набор байтовых строк равен каждой из 8 сдвигов цепочки битов, заполненных всеми возможными битами заполнения, где это необходимо, в первом и последнем байтах. Я думаю, что это работает до 1024 из них.)

0 голосов
/ 05 августа 2009

Учитывая любой байт B, вы хотите спросить, какие позиции, если таковые имеются, он может занимать в 37-битной последовательности. Тогда

  1. Вы сохраняете набор возможных позиций для текущего байта, который начинается пустым.
  2. Если вы видите байт с позицией 0, вы добавляете 0 к набору.
  3. Если вы видите байт с позицией 1..7, вы маскируете и проверяете предыдущий байт и, если все в порядке, добавляете текущую позицию к набору.
  4. Чтобы перейти к новому байту, проверьте каждую позицию в наборе, добавьте 8 к нему и спросите, может ли новый байт появиться в этой позиции. Когда вы достигнете позиции не менее 29, вы выиграли поездку с оплатой всех расходов на успешный поиск.

Вы можете сделать это быстро с помощью поиска в таблице, хотя точные структуры данных, которые будут использоваться, открыты для экспериментов. Поскольку у вас есть 256 байтов и 8 начальных позиций, вы можете сохранить начальные позиции в массиве из 256 байтов, надеясь, что общий случай всех нулей встречается часто. Это должно сделать стоимость для шагов 2 и 3 либо O (1) , либо O (8) , в любом случае небольшая константа.

Для более поздних проверок позиции я думаю, что вы хотите индексировать по позиции, а не по байту, поэтому вам понадобится массив из 29 байтов (по одному на каждую позицию из 8..36). Эта проверка в O (1) раз превышает количество активных в данный момент позиций.

Это похоже на веселье; дайте нам знать, как вы делаете.

0 голосов
/ 05 августа 2009

Если шаблон, который вы ищете, является фиксированным, вы можете построить серию массивов, которые являются сдвигами в масках, чтобы сделать сравнения. Для сравнения используйте функцию xor и, если возвращается 0, она совпадает. Любое другое значение не совпадает. Это позволит проверять байты в строке, если в массиве осталось не менее 2 байтов. Если осталось 2 байта, вы не сможете увеличить целые восемь битов. Пример для 17 бит приведен ниже, но это та же идея. (Я ищу все, с которыми было легко работать, сдвигая биты для демонстрации)

/* Data is passed in, and offset is the number of bits offset from the first
   bit where the mask is located
   returns true if match was found.
*/
bool checkData(char* data, int* offset)
{
    /* Mask to mask off the first bits  not being used or examined*/
    static char firstMask[8] = { 0xFF, 0x7F, 0x3F, 0x1F, 0x0F, 0x07, 0x03, 0x01 };

    /* Mask to mask off the end bits not used  or examined*/
    static char endMask[8] = { 0x80, 0xC0, 0xE0, 0x0F, 0xF8, 0xFC, 0xFE, 0xFF };

    /* Pattern which is being search, with each row being the about shifted and 
       columns contain the pattern to be compared.  for example index 0 is a 
       shift of 0 bits in the pattern and 7 is a shift of seven bits
       NOTE: Bits not being used are set to zero.  
    */
    static char pattern[8][3] = { { 0xFF, 0xFF, 0x80 },  /* Original pattern */
                                  { 0x8F, 0xFF, 0xC0 },  /* Shifted by one */
                                  { 0x3F, 0xFF, 0xE0 },  /* Shifted by two */
                                  { 0x1F, 0xFF, 0xF0 },
                                  { 0x0F, 0xFF, 0xF8 },
                                  { 0x07, 0xFF, 0xFC },
                                  { 0x03, 0xFF, 0xFE },
                                  { 0x01, 0xFF, 0xFF }}; /* shifted by seven */

    /* outer loop control variable */
    int lcv;

    /* inter loop control variable */
    int lcv2;

    /* value to to contain the value results */
    char value;

    /* if there is no match, pass back a negative number to indicate no match */
    *offset = -1;

    /* Loop through the shifted patterns looking for a match */
    for ( lcv = 0; lcv < 8 ; lcv++ ) 
    {
        /* check the first part of the pattern.  
           mask of part that is not to be check and xor it with the 
           first part of the pattern */

        value = (firstMask[lcv] & *data) ^ pattern[lcv][0];
        /* if value is not zero, no match, so goto the next */
        if ( 0 != value ) 
        {
            continue;
        }

        /* loop through the middle of the pattern make sure it matches
           if it does not, break the loop
           NOTE:  Adjust the condition to match 1 less then the number 
                  of 8 bit items  you are comparing
        */
        for ( lcv2 = 1; lcv2 < 2; lcv2++)
        {
            if ( 0 != (*(data+lcv2)^pattern[lcv][lcv2]))
            {
                break;
            }
        }

        /* if the end of the loop was not reached, pattern 
           does not match, to continue to the next one
           NOTE: See note above about the condition 
        */   
        if ( 2 != lcv2)
        {
          continue;
        }

        /* Check the end of the pattern to see if there is a match after masking
           off the bits which are not being checked.
        */  
        value = (*(data + lcv2) & endMask[lcv]) ^ pattern[lcv][lcv2];

        /* if value is not zero, no match so continue */
        if ( 0 != value ) 
        {
          continue;
        }
    }
    /* If the end of the loop was not reached, set the offset as it 
       is the number of bits the pattern is offset in the byte and 
       return true
    */
    if ( lcv < 8 ) 
    {
        *offset = lcv ;
        return true;
    }
    /* No match was found */
    return false;
}

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

Если в шаблоне нет совпадения на ранней стадии, он не будет проверять оставшиеся биты, что должно помочь в поиске.

Эта реализация должна быть достаточно переносимой, но для 37-битной обработки потребуется некоторая переделка.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...