我们想计算恰好代表 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.
