家谱软件中的循环

Par*_*öse 1594 c++ graph cycle assertions family-tree

我是一些家庭树软件的开发者(用C ​​++和Qt编写).在我的一位客户向我邮寄错误报告之前,我没有遇到任何问题.问题是客户有两个孩子和自己的女儿,因此,他因错误而无法使用我的软件.

这些错误是我处理家族图的各种断言和不变量的结果(例如,在走一个循环之后,程序声明X不能同时是Y的父亲和祖父).

如何在不删除所有数据断言的情况下解决这些错误?

Ber*_*als 727

看来你(和/或你的公司)对家谱应该是什么有一个根本的误解.

让我澄清一点,我也为一家公司(其产品之一)的产品组合中的家族树工作,我们一直在努力解决类似的问题.

在我们的案例中,问题,我也假设你的情况,来自GEDCOM格式,该格式对于一个家庭应该是什么非常自以为是.然而,这种格式包含了一些关于家谱真正看起来的严重错误观念.

GEDCOM有许多问题,例如与同性关系,乱伦等不相容......在现实生活中发生的事情比你想象的更频繁(特别是在1700-1800之后回归).

我们已经将我们的家谱模型化为现实世界中发生的事件:事件(例如,出生,婚礼,订婚,工会,死亡,收养等).我们对这些没有任何限制,除了逻辑上不可能的(例如,一个不能是一个人自己的父母,关系需要两个人等等)

缺乏验证为我们提供了一个更"现实世界",更简单,更灵活的解决方案.

至于这个具体的情况,我建议删除断言,因为它们并不普遍存在.

为了显示问题(将出现),我建议根据需要多次绘制相同的节点,通过在选择其中一个副本时点亮所有副本来暗示重复.

  • @ paul-harrison如果那只是那么简单.在较旧的记录(甚至是新记录)中,存在日期不一致.出生前的洗礼,多次出生记录等......所以在某种程度上,在官方记录中,有时间旅行.我们允许这种不一致的数据.我们允许用户在重复的情况下指出应用程序应该考虑"出生记录".如果找到,我们将指出损坏的时间表. (41认同)
  • @ ben-voigt GEDCOM是由耶稣基督后期圣徒教会创建的一种格式.该规范明确规定婚姻(MARR)应在男女之间.对于同性婚姻或乱伦,应使用ASSO标签(ASSOCIATES),也用于表示友谊或是邻居.很明显,同性婚姻是本规范中的二等关系.一个更中性的规范不会要求男性女性关系. (38认同)
  • 这看起来是正确的方法,并且很容易扩展以检测更复杂的问题.你可以在事件之间找出一组"A之前发生的B"关系.例如,一个人在涉及他们的任何其他事件之前出生.这是有向图.然后,您可以检查图表是否包含循环.[请参阅StackOverflow上的这个问题.](http://stackoverflow.com/questions/261573/best-algorithm-for-detecting-cycles-in-a-directed-graph)这应该是好的,直到时间旅行发明. (32认同)

Ben*_*igt 563

放松你的断言.

不是通过更改规则,这些规则很可能对99.9%的客户在输入数据时发现错误非常有帮助.

相反,将其从错误"无法添加关系"更改为带有"无论如何添加"的警告.

  • 当遇到*非常不可能*的情况时,即用户通常*只是错误地执行该情况时,最好向用户显示警告.这是很好的反馈.但是如果他们确实*确定他们想要,那么让用户继续前进.所以我认为这是一个很好的答案,即使它没有进入如何的细节. (143认同)
  • 好答案!我只是想知道,这种软件将如何处理"我是我自己的爷爷"(http://www.youtube.com/watch?v=eYlJH81dSiw)的情况? (15认同)
  • 这不是一个真正的答案,因为我认为问题来自实际遍历树?但是,这是一个很好的建议. (4认同)
  • @bdwakefield:问题是"如何在不删除所有数据断言的情况下解决这些错误?" 我相信我已经回答了这个问题. (3认同)
  • @Ben这取决于断言的用途.如果它们阻止无限循环或致命错误发生,那么您实际上建议删除断言.如果他们只是在那里警告用户可能存在错误,那么你的回答是好的. (2认同)

exD*_*M69 224

这是家庭树的问题:它们不是树木.它们是有向无环图或DAG.如果我正确理解人类生殖生物学的原理,就不会有任何循环.

据我所知,即使是基督徒也接受堂兄弟之间的婚姻(以及子女),这会将家谱变成家庭DAG.

故事的寓意是:选择正确的数据结构.

  • 男人嫁给他的祖母不会让自己成为自己的祖父并增加一个周期.如果他们有孩子,那将是一个非循环的常规图形边缘. (13认同)
  • 它实际上是两个ADG.有父母图和法律关系图.通常是相同的,但不止一个人可能会发生分歧. (11认同)
  • 它不一定是非循环的,是吗?曼结婚祖母. (9认同)
  • 对于体外和有性生殖,需要进一步限制每个节点具有指向其的1或2个最大节点.虽然对现实生活更加真实,但你可能会允许多条虚线表示父亲方面的不确定性(母亲总是很清楚,但只有DNA测试可以确保父亲是谁,即使在今天也很少这样做),或者甚至两者都是采用被考虑在内. (7认同)
  • @manixrock - 因为这个问题是关于罕见的情况,我想断言并不总是清楚母亲是谁.收养,遗弃的婴儿,代孕妈妈等都可能使问题复杂化. (7认同)
  • 假设完美的知识和生物学,在生物学中不会有任何循环,但在现实世界中,家谱充满了"或许"的联系.例如,上面的manxrock声称总是清楚母亲是谁.在现实世界中并非如此.是的,今天DNA测试可以回答这个问题,但是你如何让你的亲戚从5代回到DNA测试? (2认同)

小智 115

我猜你有一些价值,可以唯一地识别你可以作为支票基础的人.

这是一个棘手的问题.假设你想保持结构为树,我建议:

假设这个:A有孩子和他自己的女儿.

A将自己添加到程序AB.一旦担任父亲的角色,我们称之为男朋友.

添加一个is_same_for_out()函数,告诉程序的输出生成部分,所有B内部链接都应该在A数据表示时进行.

这将为用户做一些额外的工作,但我想IT的实施和维护相对容易.

从此开始,您可以处理代码同步AB避免不一致.

这个解决方案肯定不是完美的,但这是第一种方法.

  • 可能这样的"代理"节点确实是合适的解决方案.但是我不知道如何在不冒犯用户的情况下将它们放在用户界面中.我可以告诉你,编写处理真人(特别是你的客户)的软件并不容易. (9认同)
  • 它永远不会结束 - B的新儿子将是他自己的叔叔.我会考虑全额退款! (6认同)
  • @Will A:然后意识到他也是他自己的母亲,并将他年轻的自己招募到时间机构? (3认同)
  • 在一个系统内复制(和同步)数据是不好的做法.它表明该解决方案是次优的,应该重新考虑.如果需要创建额外(重复)节点,请将其指定为代理并将数据读写和写入委托给原始节点. (2认同)

chr*_*eml 84

您应该专注于真正为您的软件创造价值的东西.花在为一个消费者工作的时间是否值得许可证的价格?可能不是.

我建议你向这位客户道歉,告诉他他的情况超出了你的软件的范围并给他退款.

  • 可能每个人都有一些在他/她的祖先某处乱伦的案例.所以,如果有人挖掘家族历史(太深),你就会遇到这种情况. (10认同)
  • 非常的.但也要考虑其他潜在的问题以及其他人提出的类似问题. (3认同)
  • 当然.原因是:如果它是非关键应用程序的罕见边缘情况,则不需要修复或实现任何内容.如果它真的伤害了你的用户,那么就有价值. (2认同)

小智 79

您应该将Atreides系列(现代,Dune或古代,Oedipus Rex)设置为测试用例.通过使用已清理的数据作为测试用例,您找不到错误.

  • 可悲的是,太多人首先想到的是"好"的数据,而不是破坏他们系统的边缘情况. (2认同)

Tim*_*ost 59

这就是为什么像"Go"这样的语言没有断言的原因之一.它们习惯于处理你可能没有想过的案例.你应该只断言不可能的,而不仅仅是不可能的.做后者是断言声誉不好的原因.每次打字assert(,走开十分钟,真的想一想.

在你特别令人不安的情况下,这种说法在罕见但可能的情况下是虚假的,这是可以想象的,也是令人震惊的.因此,在您的应用程序中处理它,如果只是说"此软件不是为处理您提供的场景而设计的".

断言你的伟大,伟大,伟大的祖父是你的父亲是不可能的是一个合理的事情.

如果我在一家受雇于测试软件的测试公司工作,我当然会提出这种情况.为什么?每个少年而又聪明的"用户"都会做同样的事情,并在最终的"错误报告"中津津乐道.

  • 同意"何时使用断言"的论点; 看不出它与"某些语言有断言,Go没有"有什么关系. (5认同)
  • 断言(或类似断言的代码)是无关紧要的.像Go这样的语言中的代码可以并且将对数据结构做出假设; 它只是不能用断言记录和执行这些假设.底线:应用程序有一个bug. (5认同)
  • @Red Hue - 有时编译器会使不可能的......成为可能.某些版本的gcc在abs()实现中认为-10 == 10. (2认同)
  • @Red Hue:断言的全部内容是记录和测试应该始终为真(或错误)的条件.它有助于让你(和其他人)以某种方式"修复"这些不可能的案例,因为那时他们明确地(而不是巧妙地)打破了应用程序.如果有一个"不可能"的案例出现的正当理由,那么你已经断言了太多. (2认同)

Sea*_*ean 41

我讨厌对这种搞砸的情况发表评论,但不重新调整所有不变量的最简单方法是在图形中创建一个虚拟顶点,作为代理回到乱伦的父亲.


tfi*_*iga 37

所以,我在家庭树软件方面做了一些工作.我认为你要解决的问题是你需要能够在没有无限循环的情况下走树 - 换句话说,树需要是非周期性的.

然而,看起来你断言一个人和他们的祖先之间只有一条路.这将保证没有周期,但是太严格了.从生物学角度讲,后代是有向无环图(DAG).你所拥有的案例当然是一个堕落的案例,但这种事情一直在大树上发生.

例如,如果你看一下n代的2 ^ n个祖先,如果没有重叠,那么你在公元1000年就会有更多的祖先,而不是有人活着.所以,必须有重叠.

但是,您也倾向于获得无效的循环,只是错误的数据.如果您正在遍历树,则必须处理循环.您可以在每个单独的算法中或在加载时执行此操作.我是在加载时做的.

在树中查找真实循环可以通过几种方式完成.错误的方法是从给定的个体标记每个祖先,并且当遍历时,如果已经标记了要进入下一个的人,则切断链接.这将切断潜在的准确关系.正确的方法是从每个人开始,并用每个人的路径标记每个祖先.如果新路径包含当前路径作为子路径,那么它是一个循环,应该被打破.您可以将路径存储为vector <bool>(MFMF,MFFFMF等),这使得比较和存储非常快.

还有一些其他方法可以检测周期,例如发送两个迭代器并查看它们是否与子集测试发生冲突,但我最终使用了本地存储方法.

另请注意,您不需要实际切断链接,只需将其从普通链接更改为"弱"链接,而不是某些算法.在选择标记为弱的链接时,您还需要注意; 有时您可以通过查看出生日期信息来确定周期应该被打破的位置,但通常您无法弄清楚任何因为缺少这么多数据.


小智 36

愚蠢问题的另一个模拟严肃答案:

真正的答案是,使用适当的数据结构.使用没有循环的纯树无法完全表达人类谱系.你应该使用某种图形.此外,在进一步讨论之前,先与人类学家交谈,因为即使在最简单的"西方父权制一夫一妻婚姻"案例中,也有很多其他地方可以尝试类似的家谱模型.

即使我们想忽略这里讨论的本地禁忌关系,也有很多完全合法且完全出乎意料的方法将循环引入家谱.

例如:http://en.wikipedia.org/wiki/Cousin_marriage

基本上,表亲婚姻不仅是普遍的和预期的,它是人类从数千个小家庭群体变成全球60亿人口的原因.它不能以任何其他方式工作.

在家谱,家庭和血统方面,确实很少有普遍性.几乎任何关于规范的严格假设都暗示着姨妈可以成为谁,或者谁可以嫁给谁,或者如何将儿童合法化为继承目的,可以在世界或历史的某个地方被某种例外所困扰.

  • 你的评论让我想到了一夫多妻制.仅对有性生殖进行建模的家谱软件可能需要附加在精子和卵子上的名称,但家族结构的更广泛定义则不然. (9认同)

Wil*_*l A 20

除了潜在的法律含义之外,您肯定需要将家族树上的"节点"视为前任人,而不是假设该节点可以是唯一的人.

让树节点包含一个人以及后继者 - 然后您可以在树的下方有另一个节点,其中包含具有不同后继者的同一个人.


ker*_*ger 13

一些答案已经显示了保持断言/不变量的方法,但这似乎是对断言/不变量的误用.断言是为了确保应该为真的东西是真的,并且不变量是为了确保不应该改变的东西不会改变.

你在这里断言的是,不存在乱伦关系.显然它们确实存在,所以你的断言是无效的.你可以解决这个断言,但真正的错误在于断言本身.断言应该被删除.


Pat*_*sen 8

你的家谱应该使用直接关系.这样你就不会有一个循环.


Tyl*_*den 5

系谱数据是循环的,不适合非循环图,因此如果您对循环有断言,则应删除它们.

在不创建自定义视图的情况下在视图中处理此方法的方法是将循环父级视为"ghost"父级.换句话说,当一个人同时是同一个人的父亲和祖父时,则正常显示祖父节点,但父节点被渲染为具有简单标签的"幽灵"节点("看到祖父") )并指向祖父.

为了进行计算,您可能需要改进逻辑以处理循环图,以便在存在循环时不会多次访问节点.