在二进制数组中搜索模式

时间:2019-06-08 15:10:34

标签: arrays search binary pattern-matching

我正在编写一个在二进制数组(位图文件)中搜索位模式的函数。模式的大小为5到8位长。我已经实现了此功能,用于测试数组和模式中的每个位。但是,它并没有达到应有的效率。

首先,我想在C语言中实现此代码。



Point* FindPattern(imgInfo* pImg, int pSize, int* ptrn, Point* pDst, int* fCnt)
{
    int i, j, k, l;
    int mask;
    int rx = pSize >> 16;
    int ry = pSize & 0xFFFF;

    *fCnt = 0;
    for (i=0; i < pImg->height - ry; ++i)
        for (j=0; j < pImg->width - rx; ++j)
        {
            // for a rectangle with upper lefr corner in (i,j)
            // check if there is pattern in image
            for (k=0; k < ry; ++k)
            {
                mask = 1 << (rx - 1);
                for (l=0; l < rx; ++l, mask >>= 1)
                    if (GetPixel(pImg, j+l, i+k) != ((ptrn[k] & mask) != 0))
                        break;
                if (l < rx) // pattern not found
                    break;
            }
            if (k >= ry) //pattern found
            {
                pDst[*fCnt].x = j;
                pDst[*fCnt].y = i;
                ++(*fCnt);
            }
        }

例如,我有这样的二进制字符串:1111 1111 1010 0000 0111 1111 1111 1111

我正在寻找模式:0100 0000

那么检测字符串中这种模式的最有效方法是什么?通过移位模式和字符串的位,然后对它们执行XOR?

1 个答案:

答案 0 :(得分:0)

查找给定模式可以用与该模式中的位数相同的步骤数完成。我不知道这是检测模式的最有效方法,但对于不太大的模式,它可能具有竞争力。

例如,考虑您的模式01000000。
必须在bs的位串中找到它,我们将bs [i]称为bs的第i位。
该图案在给定位置iiff

上匹配

bs [i]为假(0)
并且bs [i + 1]为假(0)
并且bs [i + 2]为假(0)
并且bs [i + 3]为假(0)
并且bs [i + 4]为假(0)
并且bs [i + 5]为假(0)
并且bs [i + 6]为true(1)
并且bs [i + 7]为false(0)

这可以变成逻辑表达式
~ bs[i] & ~ bs[i+1] & ~ bs[i+2] & ~ bs[i+3] & ~ bs[i+4] & ~ bs[i+5] & bs[i+6] & ~ bs[i+7]
模式中有0的地方有逻辑补码。

可以通过右移将其重写以得到表达式:
~ bs[i] & ~ ((bs>>)[i]) & ~ ((bs>>2)[i]) & ~ ((bs>>3)[i]) & ~ ((bs>>4)[i]) & ~ ((bs>>5)[i]) & ((bs>>6)[i]) & ~ ((bs>>7)[i])
其中(bs>>k)[i]是bs的第i位向右移动k步。

由此我们可以得出以下C代码

#include <stdio.h>

unsigned int findpattern(unsigned int bitstring, unsigned int pattern, 
                         unsigned int patternsize) {
  unsigned int match=~0;
  for(int i=0; i<patternsize; i++){
    match &= ((pattern&0x1)-1) ^ (bitstring);
    pattern >>=1;
    bitstring >>=1;
  }

  return match;
}

int main() {
  unsigned int bitstring=0xffa07fff;
  unsigned int pattern=0x40;

  unsigned int match=findpattern(bitstring,pattern,8);

  if (! match) 
    printf("No match for %x in %x\n",pattern, bitstring);
  else 
    printf("Matches found for %x in %x : %.8x\n", pattern, bitstring, match);
}

函数findpattern返回一个int,如果在位置i上找到了模式,则第i位将被设置。在没有找到模式的情况下,匹配为零。

这个想法只是通过右移图案来扫描图案的连续位。在任何时候,如果设置了lsb,我们都将结果与位串的正确移位版本进行AND,如果未设置pattern的lsb,则将其与移位后的位串的补数进行AND。 补充是通过与(pattern&1)-1进行异或来完成的。如果设置了lsb,则它是1-1 = 0(同一性)的异或,否则是-1(等于〜)的异或。

请注意,较高位可能存在错误匹配,因为以某种方式人为地将位串左移了(patternsize-1)零,这可能会导致位串/模式的某种组合出现问题。这就是最终掩码清除(patternsize-1)匹配的最左位的原因,因为不可能找到超出32位模式大小的匹配。因此,match与(2 ^(32-(patternsize-1))相乘,该数字是1组成的数字,patternsize-1的零在最左边。