C 使用位运算符的快速字符串搜索

C 使用位运算符的快速字符串搜索,c,string,algorithm,bit-manipulation,bioinformatics,C,String,Algorithm,Bit Manipulation,Bioinformatics,使用位运算符在很长的字符串中查找子字符串的最快(并行)方法是什么 e、 g.找到人类基因组(770MB)中“GCAGCTGAAAACA”序列的所有位置 *字母表由4个符号(“G”、“C”、“T”、“A”)组成,用2位表示: “G”:00,“A”:01,“T”:10,“C”:11 *您可以假设查询字符串(较短的一个)的长度是固定的,例如127个字符 *所谓最快,我的意思是不包括任何预处理/索引时间 *在预处理之后,文件将被加载到内存中,基本上,在一个较大的字符串中,将有数十亿个短字符串需要搜索,全

使用位运算符在很长的字符串中查找子字符串的最快(并行)方法是什么

e、 g.找到人类基因组(770MB)中“GCAGCTGAAAACA”序列的所有位置

*字母表由4个符号(“G”、“C”、“T”、“A”)组成,用2位表示: “G”:00,“A”:01,“T”:10,“C”:11

*您可以假设查询字符串(较短的一个)的长度是固定的,例如127个字符

*所谓最快,我的意思是不包括任何预处理/索引时间

*在预处理之后,文件将被加载到内存中,基本上,在一个较大的字符串中,将有数十亿个短字符串需要搜索,全部在内存中

*按位搜索,因为我正在寻找一种最简单、最快速的方法来搜索大型位阵列中的位模式,并尽可能靠近硅

*由于字母表太小,KMP不会很好地工作

*C代码,x86机器代码都很有趣

输入格式说明(.2bit):

相关的:


如果你只是浏览一个文件,你几乎可以保证是io绑定的。使用大缓冲区(~16K)和
strstr()
应该是您所需要的。如果文件以ascii编码,只搜索
“gcagctgaaaaca”
。如果它实际上是以位编码的;只需排列可能接受的字符串(应该有~8;去掉第一个字节),并使用
memmem()
加上一个微小的重叠位检查


我在这里要注意的是,glibc
strstr
memmem
已经使用Knuth-Morris-Pratt在线性时间内进行搜索,所以测试该性能。这可能会让你感到惊讶。

Boyer More是一种用于在普通字符串中搜索子字符串的技术。其基本思想是,如果子字符串的长度为10个字符,则可以查看字符串中位置9处的字符进行搜索。如果该字符不是搜索字符串的一部分,只需在该字符之后开始搜索即可。(如果该字符确实在字符串中,则Boyer-More算法使用查找表向前跳过最佳字符数。)


也许可以将这个想法重新用于基因组字符串的压缩表示。毕竟,只有256个不同的字节,因此您可以安全地预先计算跳过表。

将字母表编码到位字段的好处是紧凑:一个字节相当于四个字符。这类似于谷歌搜索单词的一些优化


这意味着四次并行执行,每次执行(转换后的)搜索字符串偏移一个字符(两位)。一种快速而肮脏的方法可能是只查找搜索字符串的第一个或第二个字节,然后在匹配字符串其余部分之前和之后检查额外的字节,如有必要,屏蔽末端。第一次搜索由x86指令
scasb
轻松完成。随后的字节匹配可以基于寄存器值,使用
cmpb

如果您首先使用无损编码方法(例如Huffman、指数Golumb等)对DNA字符串进行编码/压缩,那么您将获得各种核苷酸组合的DNA标记的排序概率表(“编码树”)(例如,
A
AA
CA
等)

这意味着,一旦你压缩了你的DNA:

  • 与总是每基使用两位的“未编码”方法相比,您可能会使用更少的位来存储
    GCAGCTGAAAACA
    和其他子序列
  • 您可以遍历编码树或表来构建编码的搜索字符串,该字符串通常比未编码的搜索字符串短
  • 您可以应用同一系列精确搜索算法(例如Boyer Moore)来定位这个较短的编码搜索字符串
  • 对于并行化方法,将编码的目标字符串分成N个块,并使用缩短的编码搜索字符串对每个块运行搜索算法。通过跟踪每个块的位偏移量,您应该能够生成匹配位置


    总的来说,如果您计划对不会改变的序列数据进行数百万次搜索,这种压缩方法将非常有用。您将搜索更少的位,总的来说可能会更少。

    您可以创建一个状态机。在本主题中, ,我使用[f]lex为我创建了状态机。使用4个字母(:=两位)的字母表需要一些黑客,但可以使用[f]lex生成的相同表来完成。(您甚至可以创建自己的fgetc()类似于一次从输入流中提取两位,并保留其他六位用于连续调用的函数。回推会有点困难,但不可撤消)


    顺便说一句:我严重怀疑将数据压缩到每个核苷酸两位是否有任何好处,但那是另一回事。

    好的,考虑到您的参数,问题并没有那么难,只是不像传统的字符串搜索问题那样难解决。它更像是数据库表连接问题,表要大得多,而不是汉拉姆

    • 选择一个好的滚动散列函数aka buzhash。如果你有数十亿个字符串,你正在寻找一个64位值的散列

    • 基于每个127个元素的搜索字符串创建一个哈希表。内存中的表只需要存储(哈希,字符串id),而不需要存储整个字符串

    • 扫描非常大的目标字符串,计算滚动哈希并在表中查找哈希的每个值。只要有匹配项,就将(字符串id,目标偏移量)对写入流,可能是文件

    • 重新读取目标字符串和配对流,根据需要加载搜索字符串,以将它们与每个偏移处的目标进行比较

    我假设一次将所有模式字符串加载到内存中是禁止的