从系谱到算法
系谱本质上就是图。一旦你这样看待它,检查某种遗传模式只需对节点进行一次遍历。
问题
系谱是一组人的集合,每个人都有性别和患病状态,通过亲子边相连。我们需要一个函数,它接收一个系谱,如果它与给定的遗传模式一致则返回 true,如果某段亲子关系与该模式矛盾则返回 false。
我们将演练一种模式:常染色体隐性遗传。其余五种模式遵循相同的结构,只是循环内的规则有所不同。
规则
常染色体隐性遗传需要两个隐性等位基因的拷贝才能表现出患病表型。基因型 aa 为患病,AA 和 Aa 则不是。有一种配对必然违反规则:如果双亲都是 aa,那么每个孩子也必定是 aa,因为双亲都没有 A 等位基因可以提供。双亲患病配对生出未患病的孩子,这就违反了规则。而双亲未患病却生出患病的孩子并不违反规则,这只是因为双亲都是携带者。
代码
将每个人表示为包含四个字段的记录:id、affected、father 和 mother,其中 father 和 mother 指向其他记录,如果未知则为 nil。只需访问每个人一次,并对照其双亲检查规则即可。遍历顺序无关紧要,因为规则只关注一个人及其直系双亲。
- for each person in people
- father = person.father
- mother = person.mother
- if father == nil or mother == nil
- continue // 没有双亲信息则无需检查
- if father.affected and mother.affected and not person.affected
- return false
- return true
八行代码,一次实质性检查。它的时间复杂度为 O(n),仅需一次遍历,每个人的处理工作量恒定。
其余五种模式
相同的函数,只需更改第 6 行。X连锁隐性遗传会将患病儿子与患病父亲和未患病母亲进行对照检查,因为儿子的X染色体绝不来自父亲。常染色体显性遗传则将规则反转:一个人患病但双亲未患病才是违规,而不是双亲患病但孩子未患病。Y连锁遗传和线粒体遗传只需检查双亲中的一方,因为这些模式只属于单亲遗传。
该工具对这六种模式各运行一次遍历,并报告哪些模式返回了 true。当有多个模式满足时,这就是针对小型系谱的真实答案,不应靠猜测来解决。
如果你想从头了解遗传模式本身的解释,请阅读我们的遗传学基础文章。关于真实疾病中X连锁隐性遗传的具体案例,请参见杜氏肌营养不良。