一,不涉及合情推理的基因推测
高考生物,都是不涉及合情推理的。
也就是说,都是把父辈的基因推出来,然后再推子辈的基因。
例如这一题:



现实生活中,情况更复杂,就需要合情推理了。
二,本文讨论范围
1,本文只讨论单基因的情况,不讨论多组的情况
2,本文只讨论AA和Aa一定是正常的,aa一定是遗传病的情况。
3,本文只讨论遗传病一定是一出生就知道了,不考虑没观测到的情况。
4,本文只讨论非伴性遗传的情况。
5,本文只讨论每个人都有已知的唯一的亲生父母的情况,不考虑基因编辑、克隆等各种技术。
三,合情推理基本假设
如果医生已经推出了父亲是Aa,母亲是AA或者Aa,那即将出生的孩子的概率怎么算?
显然,我们需要基于基因比例(A和a)的比例去做合理推算。
但是更复杂的情况来了,如果还有个已出生的孩子是AA或Aa,那么母亲和这个孩子都要基于基因比例去推算,然后又互相影响,则会变得非常复杂。
最复杂的点不在于计算复杂度,而在于现实复杂度,比如,父辈和子辈的基因统计比例可能本来就不一样?考虑到致死率、致弃婴导致的存活率、影响婚配等各种现实因素,很可能每一代的比例就是不一样的。
言归正传,这些偏离了本文的主题,所以我们做一个假设,我们只能拿到一个常数x,表示A的比例是x,a的比例是1-x,忽略各种现实因素。x的使用方法就是基于增广族谱的合情推理。
四,基于增广族谱的合情推理
(1)拓扑排序
一个有限的族谱S一定是有向无环图,先做拓扑排序得到S的子集S1={r属于S | r的亲生父亲或亲生母亲都不在S中}
(2)族谱增广
基于S1进行增广:对于S1中的每个可能是正常的个体(可能是AA或Aa),其父母若不在S中则设定成:x/(2-x)的概率是AA,(2-2x)/(2-x)的概率是Aa,对于S1中的每个遗传病个体(aa),无需做增广。
PS:这里出现的2个常数,已经是基于条件概率推导的结果。
(3)全族谱合情推理
只根据客观的已知信息,和族谱增广的结果,后续不再使用x,推导出所有人的基因概率。
这一步不能基于拓扑排序的结果进行按序处理,因为涉及根据子辈的信息往父辈反推的场景。
代码残卷:
cpp
enum FinalType : int{
A, //AA Aa
a, //aa
U // unknown
};
struct Person {
int id; //每个人的编号,0,1,2,3...
bool flag; //是否在集合S中
int faId; //-1表示不在集合S中
int maId; //-1表示不在集合S中
FinalType type;
};
//每个人是AA的概率
map<int, double> solve(vector<Person>& vp, float ratioA)
{
int id = vp.size();
//增广族谱
for (int i = id - 1; i >= 0; i--) {
if (vp[i].type == A) {
if (vp[i].faId == -1) {
vp[i].faId = id;
vp.push_back(Person{ id++,false,-1,-1,U });
}
if (vp[i].maId == -1) {
vp[i].maId = id;
vp.push_back(Person{ id++,false,-1,-1,U });
}
}
if (vp[i].type == U) {
if (vp[i].faId == -1 && vp[i].maId != -1){
vp[i].faId = id;
vp.push_back(Person{ id++,false,-1,-1,U });
}
if (vp[i].faId != -1 && vp[i].maId == -1) {
vp[i].maId = id;
vp.push_back(Person{ id++,false,-1,-1,U });
}
}
}
map<int, double> pAA;
map<int, double> pAa;
}
int main2()
{
vector<Person>vp{
Person{0,true,-1, - 1,A},
Person{1,true,-1,-1,A},
Person{2,true,0,1,a},
Person{3,true,0,1,A},
Person{4,true,-1,-1,a},
Person{5,true,3,4,A}
};
float ratioA = 0.99;
return 0;
}