Ich bin der Entwickler einer Stammbaum-Software (geschrieben in C ++ und Qt). Ich hatte keine Probleme, bis mir einer meiner Kunden einen Fehlerbericht schickte. Das Problem ist, dass der Kunde zwei Kinder mit seiner eigenen Tochter hat und daher meine Software aufgrund von Fehlern nicht verwenden kann.
Diese Fehler sind das Ergebnis meiner verschiedenen Behauptungen und Invarianten bezüglich des Familiengraphen, der verarbeitet wird (zum Beispiel besagt das Programm nach einem Zyklus, dass X nicht sowohl Vater als auch Großvater von Y sein kann).
Wie kann ich diese Fehler beheben, ohne alle Datenzusicherungen zu entfernen?
c++
graph
cycle
assertions
family-tree
Partick Höse
quelle
quelle
Antworten:
Es scheint, dass Sie (und / oder Ihr Unternehmen) ein grundlegendes Missverständnis darüber haben, was ein Stammbaum sein soll.
Lassen Sie mich klarstellen, ich arbeite auch für ein Unternehmen, das (als eines seiner Produkte) einen Stammbaum in seinem Portfolio hat, und wir haben mit ähnlichen Problemen zu kämpfen.
Das Problem in unserem Fall, und ich nehme auch Ihren Fall an, kommt vom GEDCOM- Format, das äußerst einschätzend darüber ist, was eine Familie sein sollte. Dieses Format enthält jedoch einige schwerwiegende Missverständnisse darüber, wie ein Stammbaum wirklich aussieht.
GEDCOM hat viele Probleme, wie Inkompatibilität mit gleichgeschlechtlichen Beziehungen, Inzest usw. Was im wirklichen Leben häufiger vorkommt, als Sie sich vorstellen können (insbesondere, wenn Sie in die Zeit zwischen 1700 und 1800 zurückkehren).
Wir haben unseren Stammbaum nach dem Vorbild der realen Welt modelliert: Ereignisse (z. B. Geburten, Hochzeiten, Verlobungen, Gewerkschaften, Todesfälle, Adoptionen usw.). Wir schränken diese nicht ein, außer für logisch unmögliche (zum Beispiel kann man nicht der eigene Elternteil sein, Beziehungen brauchen zwei Personen usw.)
Das Fehlen von Validierungen gibt uns eine "realere", einfachere und flexiblere Lösung.
In diesem speziellen Fall würde ich vorschlagen, die Behauptungen zu entfernen, da sie nicht allgemein gültig sind.
Für die Anzeige von Problemen (die auftreten werden) würde ich empfehlen, denselben Knoten so oft wie nötig zu zeichnen und auf die Duplizierung hinzuweisen, indem alle Kopien bei Auswahl einer davon beleuchtet werden.
quelle
Entspannen Sie Ihre Behauptungen.
Nicht durch Ändern der Regeln, die für 99,9% Ihrer Kunden wahrscheinlich sehr hilfreich sind, um Fehler bei der Eingabe ihrer Daten zu erkennen.
Ändern Sie es stattdessen von einem Fehler "Beziehung kann nicht hinzugefügt werden" in eine Warnung mit "ohnehin hinzufügen".
quelle
Hier ist das Problem mit Stammbäumen: Sie sind keine Bäume. Sie sind gerichtete azyklische Graphen oder DAGs. Wenn ich die Prinzipien der Biologie der menschlichen Fortpflanzung richtig verstehe, wird es keine Zyklen geben.
Soweit ich weiß, akzeptieren sogar die Christen Ehen (und damit Kinder) zwischen Cousins, was den Stammbaum in eine Familien-DAG verwandeln wird.
Die Moral der Geschichte lautet: Wählen Sie die richtigen Datenstrukturen.
quelle
Ich vermute, dass Sie einen Wert haben, der eine Person eindeutig identifiziert, auf die Sie Ihre Schecks stützen können.
Dies ist eine schwierige Frage. Angenommen, Sie möchten die Struktur als Baum behalten, schlage ich Folgendes vor:
Nehmen wir an:
A
hat Kinder mit seiner eigenen Tochter.A
fügt sich dem Programm alsA
und als hinzuB
. Sobald wir in der Rolle des Vaters sind, nennen wir es Freund.Fügen Sie eine
is_same_for_out()
Funktion hinzu, die dem ausgabeerzeugenden Teil Ihres Programms mitteilt, dass alleB
intern zugehörigen LinksA
bei der Präsentation von Daten aufgerufen werden sollen .Dies bedeutet zusätzliche Arbeit für den Benutzer, aber ich denke, die IT wäre relativ einfach zu implementieren und zu warten.
Darauf aufbauend könnten Sie an der Codesynchronisierung arbeiten
A
undB
Inkonsistenzen vermeiden.Diese Lösung ist sicherlich nicht perfekt, aber ein erster Ansatz.
quelle
Sie sollten sich darauf konzentrieren, was für Ihre Software wirklich wertvoll ist . Ist die Zeit, die dafür aufgewendet wird, dass es für EINEN Verbraucher funktioniert, den Preis der Lizenz wert? Wahrscheinlich nicht.
Ich rate Ihnen, sich bei diesem Kunden zu entschuldigen, ihm mitzuteilen, dass seine Situation für Ihre Software nicht in Frage kommt, und ihm eine Rückerstattung zu gewähren.
quelle
Sie sollten die Atreides- Familie (entweder modern, Dune oder alt, Ödipus Rex ) als Testfall eingerichtet haben. Sie finden keine Fehler, wenn Sie bereinigte Daten als Testfall verwenden.
quelle
Dies ist einer der Gründe, warum Sprachen wie "Go" keine Behauptungen haben. Sie werden verwendet, um Fälle zu behandeln, an die Sie wahrscheinlich nicht allzu oft gedacht haben. Sie sollten nur das Unmögliche behaupten, nicht einfach das Unwahrscheinliche . Letzteres zu tun, gibt Behauptungen einen schlechten Ruf.
assert(
Gehen Sie jedes Mal , wenn Sie tippen , zehn Minuten weg und denken Sie wirklich darüber nach.In Ihrem besonders beunruhigenden Fall ist es sowohl denkbar als auch entsetzlich, dass eine solche Behauptung unter seltenen, aber möglichen Umständen falsch wäre. Behandeln Sie es daher in Ihrer App, wenn Sie nur sagen "Diese Software wurde nicht für das von Ihnen vorgestellte Szenario entwickelt".
Es ist vernünftig zu behaupten, dass Ihr Ur-Ur-Ur-Großvater Ihr Vater als unmöglich ist.
Wenn ich für eine Testfirma gearbeitet hätte, die beauftragt wurde, Ihre Software zu testen, hätte ich dieses Szenario natürlich vorgestellt. Warum? Jeder jugendliche, aber intelligente "Benutzer" wird genau das Gleiche tun und sich über den daraus resultierenden "Fehlerbericht" freuen.
quelle
Ich hasse es, eine solche vermasselte Situation zu kommentieren, aber der einfachste Weg, nicht alle Ihre Invarianten neu auszulösen, besteht darin, einen Phantomscheitelpunkt in Ihrem Diagramm zu erstellen, der als Proxy für den inzestuösen Vater fungiert.
quelle
Also habe ich einige Arbeiten an Stammbaumsoftware durchgeführt. Ich denke, das Problem, das Sie lösen möchten, ist, dass Sie in der Lage sein müssen, auf dem Baum zu laufen, ohne in Endlosschleifen zu geraten - mit anderen Worten, der Baum muss azyklisch sein.
Es sieht jedoch so aus, als würden Sie behaupten, dass es nur einen Weg zwischen einer Person und einem ihrer Vorfahren gibt. Das garantiert, dass es keine Zyklen gibt, ist aber zu streng. Biologisch gesehen ist die Nachkommenschaft ein gerichteter azyklischer Graph (DAG). Der Fall, den Sie haben, ist sicherlich ein entarteter Fall, aber so etwas passiert immer bei größeren Bäumen.
Wenn Sie sich zum Beispiel die 2 ^ n Vorfahren ansehen, die Sie in Generation n haben, wenn es keine Überlappung gab, dann hätten Sie 1000 n. Chr. Mehr Vorfahren als Menschen am Leben waren. Es muss also Überschneidungen geben.
Sie neigen jedoch auch dazu, Zyklen zu erhalten, die ungültig sind, nur schlechte Daten. Wenn Sie den Baum durchqueren, müssen Zyklen behandelt werden. Sie können dies in jedem einzelnen Algorithmus oder unter Last tun. Ich habe es unter Last gemacht.
Das Finden echter Zyklen in einem Baum kann auf verschiedene Arten erfolgen. Der falsche Weg besteht darin, jeden Vorfahren einer bestimmten Person zu markieren. Wenn beim Überqueren die Person, zu der Sie als Nächstes wechseln möchten, bereits markiert ist, schneiden Sie den Link ab. Dadurch werden potenziell genaue Beziehungen getrennt. Der richtige Weg, dies zu tun, besteht darin, von jedem Individuum auszugehen und jeden Vorfahren mit dem Pfad zu diesem Individuum zu markieren. Wenn der neue Pfad den aktuellen Pfad als Unterpfad enthält, handelt es sich um einen Zyklus, der unterbrochen werden sollte. Sie können Pfade als Vektor <bool> (MFMF, MFFFMF usw.) speichern, was den Vergleich und die Speicherung sehr schnell macht.
Es gibt einige andere Möglichkeiten, Zyklen zu erkennen, z. B. das Senden von zwei Iteratoren und das Überprüfen, ob sie jemals mit dem Teilmengen-Test kollidieren. Am Ende habe ich jedoch die lokale Speichermethode verwendet.
Beachten Sie auch, dass Sie den Link nicht wirklich trennen müssen. Sie können ihn einfach von einem normalen Link in einen "schwachen" Link ändern, dem einige Ihrer Algorithmen nicht folgen. Sie sollten auch vorsichtig sein, wenn Sie auswählen, welcher Link als schwach markiert werden soll. Manchmal können Sie herausfinden, wo der Zyklus unterbrochen werden soll, indem Sie sich die Geburtsdaten ansehen. Oft können Sie jedoch nichts herausfinden, da so viele Daten fehlen.
quelle
Eine weitere falsche Antwort auf eine dumme Frage:
Die eigentliche Antwort lautet: Verwenden Sie eine geeignete Datenstruktur. Die menschliche Genealogie kann nicht vollständig mit einem reinen Baum ohne Zyklen ausgedrückt werden. Sie sollten eine Art Diagramm verwenden. Sprechen Sie auch mit einem Anthropologen, bevor Sie fortfahren, da es viele andere Orte gibt, an denen ähnliche Fehler beim Modellieren der Genealogie gemacht werden könnten, selbst im einfachsten Fall der "westlichen patriarchalischen monogamen Ehe".
Selbst wenn wir die hier diskutierten lokal tabuisierten Beziehungen ignorieren möchten, gibt es viele völlig legale und völlig unerwartete Möglichkeiten, Zyklen in einen Stammbaum einzuführen.
Zum Beispiel: http://en.wikipedia.org/wiki/Cousin_marriage
Grundsätzlich ist die Cousinehe nicht nur üblich und zu erwarten, sondern auch der Grund, warum Menschen von Tausenden kleiner Familiengruppen auf eine Weltbevölkerung von 6 Milliarden Menschen übergegangen sind. Es kann nicht anders funktionieren.
Es gibt wirklich sehr wenige Universalien, wenn es um Genealogie, Familie und Abstammung geht. Fast jede strenge Annahme über Normen, die darauf hindeuten, wer eine Tante sein kann oder wer wen heiraten kann oder wie Kinder zum Zwecke der Vererbung legitimiert werden, kann durch eine Ausnahme irgendwo auf der Welt oder in der Geschichte verärgert werden.
quelle
Abgesehen von möglichen rechtlichen Auswirkungen scheint es sicher, dass Sie einen 'Knoten' in einem Stammbaum als Vorgänger-Person behandeln müssen, anstatt davon auszugehen, dass der Knoten die einzige Person sein kann.
Lassen Sie den Baumknoten eine Person sowie die Nachfolger einschließen - und dann können Sie einen weiteren Knoten tiefer im Baum haben, der dieselbe Person mit unterschiedlichen Nachfolgern enthält.
quelle
Einige Antworten haben Wege aufgezeigt, um die Behauptungen / Invarianten beizubehalten, aber dies scheint ein Missbrauch von Behauptungen / Invarianten zu sein. Behauptungen sollen sicherstellen, dass etwas, das wahr sein sollte, wahr ist, und Invarianten sollen sicherstellen, dass sich etwas, das sich nicht ändern sollte, nicht ändert.
Was Sie hier behaupten, ist, dass es keine inzestuösen Beziehungen gibt. Offensichtlich sie tun exist, so dass Ihre Behauptung ist ungültig. Sie können diese Behauptung umgehen, aber der eigentliche Fehler liegt in der Behauptung selbst. Die Behauptung sollte entfernt werden.
quelle
Ihr Stammbaum sollte gerichtete Beziehungen verwenden. Auf diese Weise haben Sie keinen Zyklus.
quelle
Genealogische Daten sind zyklisch und passen nicht in ein azyklisches Diagramm. Wenn Sie also Aussagen gegen Zyklen haben, sollten Sie diese entfernen.
Die Möglichkeit, dies in einer Ansicht zu handhaben, ohne eine benutzerdefinierte Ansicht zu erstellen, besteht darin, das zyklische übergeordnete Element als "Geister" -Eltern zu behandeln. Mit anderen Worten, wenn eine Person sowohl Vater als auch Großvater derselben Person ist, wird der Großvaterknoten normal angezeigt, aber der Vaterknoten wird als "Geister" -Knoten gerendert, der eine einfache Bezeichnung wie ("siehe Großvater") hat. ) und zeigt auf den Großvater.
Um Berechnungen durchführen zu können, müssen Sie möglicherweise Ihre Logik verbessern, um zyklische Diagramme zu verarbeiten, sodass ein Knoten bei einem Zyklus nicht mehr als einmal besucht wird.
quelle
Das Wichtigste ist
avoid creating a problem
, also glaube ich, dass Sie eine direkte Beziehung verwenden sollten , um einen Zyklus zu vermeiden.Wie @markmywords sagte, #include "fritzl.h".
Zum Schluss muss ich noch sagen
recheck your data structure
. Vielleicht läuft dort etwas schief (vielleicht löst eine bidirektionale verknüpfte Liste Ihr Problem).quelle
Behauptungen überleben die Realität nicht
Normalerweise überleben Behauptungen den Kontakt mit Daten aus der realen Welt nicht. Es ist Teil des Software-Engineering-Prozesses, zu entscheiden, mit welchen Daten Sie sich befassen möchten und welche nicht.
Zyklische Familiendiagramme
In Bezug auf Stammbäume (tatsächlich handelt es sich um vollständige Diagramme, einschließlich Zyklen) gibt es eine schöne Anekdote:
Noch seltsamer wird es, wenn man Leihmütter oder "Fuzzy Fatherhood" berücksichtigt.
Wie gehe ich damit um?
Definieren Sie Zyklen als außerhalb des Gültigkeitsbereichs
Sie könnten entscheiden, dass Ihre Software solche seltenen Fälle nicht behandeln soll. In diesem Fall sollte der Benutzer ein anderes Produkt verwenden. Dies macht den Umgang mit den häufigeren Fällen viel robuster, da Sie mehr Aussagen und ein einfacheres Datenmodell beibehalten können.
Fügen Sie in diesem Fall Ihrer Software einige gute Import- und Exportfunktionen hinzu, damit der Benutzer bei Bedarf problemlos auf ein anderes Produkt migrieren kann.
Manuelle Beziehungen zulassen
Sie können dem Benutzer erlauben, manuelle Beziehungen hinzuzufügen. Diese Beziehungen sind keine "erstklassigen Bürger", dh die Software nimmt sie unverändert, überprüft sie nicht und behandelt sie nicht im Hauptdatenmodell.
Der Benutzer kann dann seltene Fälle von Hand bearbeiten. Ihr Datenmodell bleibt weiterhin recht einfach und Ihre Behauptungen bleiben erhalten.
Seien Sie vorsichtig mit manuellen Beziehungen. Es besteht die Versuchung, sie vollständig konfigurierbar zu machen und somit ein vollständig konfigurierbares Datenmodell zu erstellen. Dies wird nicht funktionieren: Ihre Software wird nicht skaliert, Sie werden seltsame Fehler bekommen und schließlich wird die Benutzeroberfläche unbrauchbar. Dieses Anti-Pattern wird "Soft Coding" genannt , und "The Daily WTF" ist voller Beispiele dafür.
Machen Sie Ihr Datenmodell flexibler, überspringen Sie Zusicherungen und testen Sie Invarianten
Der letzte Ausweg wäre, Ihr Datenmodell flexibler zu gestalten. Sie müssten fast alle Aussagen überspringen und Ihr Datenmodell auf einem vollständigen Diagramm basieren. Wie das obige Beispiel zeigt, ist es leicht möglich, Ihr eigener Großvater zu sein, sodass Sie sogar Zyklen haben können.
In diesem Fall sollten Sie Ihre Software ausführlich testen. Sie mussten fast alle Behauptungen überspringen, daher besteht eine gute Chance für zusätzliche Fehler.
Verwenden Sie einen Testdatengenerator, um ungewöhnliche Testfälle zu überprüfen. Es gibt schnelle Überprüfung Bibliotheken für Haskell , Erlang oder C . Für Java / Scala gibt es ScalaCheck und Nyaya . Eine Testidee wäre, eine zufällige Population zu simulieren, sie zufällig kreuzen zu lassen und dann Ihre Software zuerst das Ergebnis importieren und dann exportieren zu lassen. Die Erwartung wäre, dass alle Verbindungen im Ausgang auch im Eingang sind und umgekehrt.
Ein Fall, in dem eine Eigenschaft gleich bleibt, wird als Invariante bezeichnet. In diesem Fall ist die Invariante die Menge der "romantischen Beziehungen" zwischen den Individuen in der simulierten Population. Versuchen Sie, so viele Invarianten wie möglich zu finden und testen Sie sie mit zufällig generierten Daten. Invarianten können funktional sein, z.
Oder sie können technisch sein:
Wenn Sie die simulierten Tests ausführen, werden Sie viele seltsame Eckfälle finden. Das Reparieren wird viel Zeit in Anspruch nehmen. Außerdem verlieren Sie viele Optimierungen, Ihre Software läuft viel langsamer. Sie müssen entscheiden, ob es sich lohnt und ob dies im Rahmen Ihrer Software liegt.
quelle
Anstatt alle Behauptungen zu entfernen, sollten Sie dennoch nach Dingen suchen, bei denen eine Person ihr eigener Elternteil ist, oder nach anderen unmöglichen Situationen und einen Fehler darstellen. Möglicherweise wird eine Warnung ausgegeben, wenn dies unwahrscheinlich ist, sodass der Benutzer weiterhin häufige Eingabefehler erkennen kann. Diese Funktion funktioniert jedoch, wenn alles korrekt ist.
Ich würde die Daten in einem Vektor mit einer permanenten Ganzzahl für jede Person speichern und die Eltern und Kinder in persönlichen Objekten speichern, wobei das besagte int der Index des Vektors ist. Dies wäre ziemlich schnell zwischen den Generationen (aber langsam für Dinge wie die Suche nach Namen). Die Objekte befinden sich in der Reihenfolge, in der sie erstellt wurden.
quelle
Dupliziere den Vater (oder benutze Symlink / Referenz).
Wenn Sie beispielsweise eine hierarchische Datenbank verwenden:
quelle
ln -s
Befehl funktioniert nicht so. Die Auflösung des LinksFamily/Son/Father
wirdFamily/Son/Daughter/Father
vonFamily/Son
unten gesucht, wo sich der Link befindet, nicht von.
wo Sie denln -s
Befehl ausgegeben haben .