新冠病毒的检测需要采样咽拭子,然后将其中含有的碱基对解螺旋、经过聚合酶链式反应(PCR)扩增后与相应的荧光探针结合,然后进行检测。
而制造能与病毒碱基序列结合的荧光探针,需要知道病毒的碱基序列。
在这个过程中,有一个算法发挥了巨大的作用。
如今的这个算法已经变得十分基础,也有更多的算法超越了它。
但是,它诞生的那一刻,必定闪耀着人类智慧的光芒。
我第一次见到这个算法时,经历了拒绝、疑惑、不解。
直到后来,一切全都化为了惊叹。
这个算法叫KMP算法。由于三个字母的原因,有人戏称为“看毛片”算法。但它和“毛片”真的没有一点关系。KMP实际是合力研究出该算法的三位大神的姓名首字母。是的,这个算法是当时三个大神共同努力的结果。
它实际是一个字符串匹配算法。
那它如何为基因测序做出贡献,又为什么让我拒绝、疑惑、不解,最后惊叹。我们一起来重新感受下。
首先,我们先给出算法解决的问题,这是一个极为常见的字符串匹配问题。
存在一个字符串T,请找出其是否含有子串P,如果有,请给出P所在的位置。
假设字符串T的长度为m,字符串P的长度为n。
则见到这个问题后,有经验的程序员不出5秒就能知道其时间复杂度为O(m×n)。是的,这是很多人的第一反应。其推导如下。
字符串T我们常称为主串,而P则被称为模式串。在主串的任意一个位置,我们都要以此为起点和模式串依次对比。模式串长度为n,而主串中有m个位置,则复杂度为O(m×n)。
T: ABCFEDABCFTYGFHTABCDEFHMI P: ABCDEFG
我听说有个算法能做到复杂度O(m+n)的时候,还没学这门课。听说那个算法叫KMP。
不可能,我内心是拒绝的。
怎么可能呢,O(m+n)意味着比较指针不能回撤,直觉告诉我这是不可能的。
我以O(m+n)为目标尝试了几下,没找到什么思路。
然后我搜了一下各种资料。
竟然真的是……
KMP算法怎么做的?我全是疑惑!
那时的我把算法找来,读了一遍。
给你,你先读一遍吧。
/** * 求出一个字符数组的next数组 * @param t 字符数组 * @return next数组 */ public static int[] getNextArray(char[] t) { int[] next = new int[t.length]; next[0] = -1; next[1] = 0; int k; for (int j = 2; j < t.length; j++) { k=next[j-1]; while (k!=-1) { if (t[j - 1] == t[k]) { next[j] = k + 1; break; } else { k = next[k]; } next[j] = 0; //当k==-1而跳出循环时,next[j] = 0,否则next[j]会在break之前被赋值 } } return next; } /** * 对主串s和模式串t进行KMP模式匹配 * @param s 主串 * @param t 模式串 * @return 若匹配成功,返回t在s中的位置(第一个相同字符对应的位置),若匹配失败,返回-1 */ public static int kmpMatch(String s, String t){ char[] s_arr = s.toCharArray(); char[] t_arr = t.toCharArray(); int[] next = getNextArray(t_arr); int i = 0, j = 0; while (i<s_arr.length && j<t_arr.length){ if(j == -1 || s_arr[i]==t_arr[j]){ i++; j++; } else j = next[j]; } if(j == t_arr.length) return i-j; else return -1; }
卧槽,没读懂。
这算法牛逼啊!我不解。
这个不解就一直闷在我的心头。
直到后来,我坐在图书馆,啃透了KMP算法。
(现在网上很多视频教程,讲得很好,十分钟看完就能懂,比自己啃快多了)
牛!真牛!
主要是思路牛。
下面我给出一个KMP算法的实现动画,大家关注一下最上面的比较指针,真的没有任何回撤操作。
KMP算法动画演示 https://www.zhihu.com/video/1237840447395414016
等你真正理解了它,发现它并没有太复杂,主要是利用了模式串中的相关性。甚至,会有更优的算法被提出。但是KMP算法由D.E.Knuth、J.H.Morris、V.R.Pratt发表于1977。在那个年代,相信KMP算法诞生时,一定闪耀着光芒。
那KMP算法为什么会和基因扯上关系呢?
平时,我们的文本,也就几百上万的字符。即使是做大数据分析(我以前在某度厂做过大数据分析),接触到的字符串也不过几十万、上百万个。当然,在这个尺度下,O(m×n)算法和O(m+n)的性能差别已经是极为巨大的。
然而,人类的基因序列,就是腺嘌呤(A)、鸟嘌呤(G)、胞嘧啶(C)、胸腺嘧啶(T)组成的那些ATCGCTACG……等朴实无华的序列串,有30亿个(即30亿对碱基对)。
基因片段的拼接需要不断在上亿的序列(碱基对)中找一个目标序列,O(m×n)算法和O(m+n)的性能差别有万倍、百万倍,甚至更多。
只用暴力匹配的方式,难以完成。
而KMP算法将O(m×n)降到了O(m+n)。只需要读取一遍目标串和模式串,就能给出结果!
在KMP算法诞生的1977年,那时人类才发现DNA的双螺旋结构不过20年,再过大约15年后人类基因组计划才会开始。
当然,在KMP算法诞生后人类基因组计划开始前的15年中,字符串匹配算法可能又会有进步,并且字符串匹配算法也并不最适合基因测序(具体原因参见下方 @奶油煎蛋红烧肉 的评论)。是新的算法(例如OLC、de Bruijn Graph等算法)用在了基因组计划中。但KMP算法在匹配方面的研究已经打好了基础,它已经提前为人类基因组计划扫除了部分算法上的障碍。
之后,也正因为基因测序技术的发展,人类能迅速锁定出新冠病毒的序列,然后研发测试试剂。帮我们开展测试,推动人类抵御这场突如其来的病毒。
所以,在这场全世界抗击疫情的斗争中,有算法的身影。低调、朴实,但也无可替代。
如今的它,低调,朴实,甚至让人觉着习以为常。
但我知道,它曾经闪耀着人类智慧的光芒;而今,也依旧令人惊艳和伟大。
欢迎关注我,我会偶尔解答软件架构和编程方面的问题。
既然是哪些,那我就多说几个。
首先是KMP和与之相关的AC自动机(Aho-Corasick automaton,刚接触以为是自动AC机),其思想和效率真的很惊艳。
然后是快速傅里叶变换(FFT),可以以的复杂度计算n次多项式乘法。
其次是扩展欧几里得算法,数论题最常用算法了,求乘法逆元、解一次不定方程超级好用,而且代码很短很好记。
再次是快速幂取模算法,理解之后代码很好写,而且效率很高。k阶矩阵的n次方复杂度仅为(不用strassen法加速的情况)。
最后再推荐一个好用但不完全严谨的算法:Miller-Rabin素数测试算法。非常快而且错误率很低啊!
自适应辛普森公式,对三点辛普森公式递归调用的积分算法,代码不到20行,解决所有一维定积分问题。
旋转卡壳算法可以在O(N)时间内求多边形直径。
-----------------------------------------------------------------------------上边是正经回答,下边是抖机灵
快速排序算法也是非常好用的,#include<stdlib.h>然后调库是比赛中所有要排序的地方的处理方法,快速排序算法让我惊艳的地方是我去省图书馆看见两个志愿者需要把还回来的一堆书按顺序入架,管理员大妈给他们教的时候说:“你先在这堆书里拉出一本来,把比它号小的扔到一边,比它大的扔到另一边,然后剩下两堆继续这样整,这样排的快!”这是我见识过最惊艳的算法使用,没有之一!
本站所有内容均为互联网搜索引擎提供的公开搜索信息,本站不存储任何数据与内容,任何内容与数据均与本站无关,如有需要请联系相关搜索引擎包括但不限于百度,google,bing,sogou 等
© 2025 tinynews.org All Rights Reserved. 百科问答小站 版权所有