prolog - 使用“库(聚合)”计算两个序列中匹配元素的复杂性
问题描述
我们想计算恰好代表 DNA 序列的两个(可能很长)字符串之间的对应关系。这些序列是从 char 中获取的字符列表,其中a,c,t,g,'_'
'_' 是一个“不知道”的占位符,它从不对应于任何东西,甚至它本身。在这种情况下,我们使用library(aggregate)(感谢 CapelliC 的想法):
match(Seq1,Seq2,Count) :-
aggregate_all(count,
(
nth1(Pos,Seq1,X),
nth1(Pos,Seq2,X),
memberchk(X,[a,c,g,t])
),
N).
这种方法可以与“直截了当”的方法进行比较,在这种方法中,人们将建立一个(尾递归)递归,该递归只是串联地遍历两个序列并成对地比较元素,同时计数。
由于序列可能非常大,因此算法复杂性变得有趣。
可以预期,n = length(sequence)并且两个序列的长度相同:
- 直截了当的方法:复杂度为O(n)
- 聚合方法:复杂度为O(n²)
上述算法的(时间和空间)复杂度是多少,为什么?
测试代码
为了补充上述内容,基于 SWI-Prolog 的plunit测试代码块:
:- begin_tests(atcg).
wrap_match(String1,String2,Count) :-
atom_chars(String1,Seq1),
atom_chars(String2,Seq2),
fit(Seq1,Seq1,0,Count).
test("string 1 empty",nondet) :-
wrap_match("atcg","",Count),
assertion(Count == 0).
test("string 2 empty") :-
wrap_match("","atcg",Count),
assertion(Count == 0).
test("both strings empty") :-
wrap_match("","",Count),
assertion(Count == 0).
test("both strings match, 1 char only") :-
wrap_match("a","a",Count),
assertion(Count == 1).
test("both strings match") :-
wrap_match("atcgatcgatcg","atcgatcgatcg",Count),
assertion(MatchCount == 12).
test("both strings match with underscores") :-
wrap_match("_TC_ATCG_TCG","_TC_ATCG_TCG",Count),
assertion(MatchCount == 9).
test("various mismatches 1") :-
wrap_match("atcgatcgatcg","atcgatcgatcg",Count),
assertion(MatchCount == 8).
test("various mismatches with underscores") :-
wrap_match("at_ga_cg__cg","atcgatcgatcg",Count),
assertion(Count == 8).
:- end_tests(atcg).
所以:
?- run_tests.
% PL-Unit: atcg ........ done
% All 8 tests passed
true.
解决方案
经验信息
在使用下面的代码进行一些手动数据收集(需要自动化的东西)之后,它会输出经过的时间和对控制台进行的推理次数:
gimme_random_sequence(Length,Seq) :-
length(Seq,Length),
maplist(
[E]>>(random_between(0,3,Ix),nth0(Ix,[a,t,c,g],E)),
Seq).
how_fast(Length) :-
gimme_random_sequence(Length,Seq1),
gimme_random_sequence(Length,Seq2),
time(match(Seq1,Seq2,_)).
...以及 LibreOffice Calc 中的一些图形摸索(我的ggplot技能生疏),我们有经验数据表明该算法的成本是
O((长度(序列))²)。
Count,Inferences,Seconds,milliseconds,megainferences
1000,171179,0.039,39,0.171179
2000,675661,0.097,97,0.675661
3000,1513436,0.186,186,1.513436
4000,2684639,0.327,327,2.684639
5000,4189172,0.502,502,4.189172
6000,6027056,0.722,722,6.027056
7000,8198103,1.002,1002,8.198103
8000,10702603,1.304,1304,10.702603
9000,13540531,1.677,1677,13.540531
10000,16711607,2.062,2062,16.711607
11000,20216119,2.449,2449,20.216119
20000,66756619,8.091,8091,66.756619
30000,150134731,17.907,17907,150.134731
40000,266846773,32.012,32012,266.846773
50000,416891749,52.942,52942,416.891749
60000,600269907,74.103,74103,600.269907
推荐阅读
- javascript - 点击按钮后如何发送邮件?
- c# - 当 SSL 页面使用代理时,WebRequest 删除授权标头
- git - 我提交时 Github 无法识别我的帐户
- python - Sqlalchemy 连接表查询以将列名均衡为多级列
- javascript - Js scrollIntoView 无法将元素滚动到滚动视图的确切顶部
- java - 关于对象不是在 Feign 和 Sentinel 上声明类的实例
- python - 如何强制两个数组相等以在 pyplot 中使用?
- mysql - 如何让我的 SQL 数据库附加到“All in One WP Migration”插件?另外,如何阻止我的域增加?
- ios - 使用 OpenGL ES for iOS 绘制三角形
- ios - 无法将集合视图添加到两个视图控制器