Soy el desarrollador de algún software de árbol genealógico (escrito en C ++ y Qt). No tuve problemas hasta que uno de mis clientes me envió un informe de error. El problema es que el cliente tiene dos hijos con su propia hija y, como resultado, no puede usar mi software debido a errores.
Esos errores son el resultado de mis diversas afirmaciones e invariantes sobre el gráfico familiar que se está procesando (por ejemplo, después de caminar un ciclo, el programa establece que X no puede ser padre y abuelo de Y).
¿Cómo puedo resolver esos errores sin eliminar todas las aserciones de datos?
c++
graph
cycle
assertions
family-tree
Partick Höse
fuente
fuente
Respuestas:
Parece que usted (y / o su empresa) tienen un malentendido fundamental de lo que se supone que es un árbol genealógico.
Permítanme aclarar, también trabajo para una empresa que tiene (como uno de sus productos) un árbol genealógico en su cartera, y hemos estado luchando con problemas similares.
El problema, en nuestro caso, y supongo que su caso también, proviene del formato GEDCOM que es extremadamente obstinado sobre lo que debería ser una familia. Sin embargo, este formato contiene algunas ideas falsas sobre cómo se ve realmente un árbol genealógico.
GEDCOM tiene muchos problemas, como la incompatibilidad con las relaciones del mismo sexo, el incesto, etc., que en la vida real ocurre con más frecuencia de lo que imagina (especialmente cuando se remonta en el tiempo al 1700-1800).
Hemos modelado nuestro árbol genealógico a lo que sucede en el mundo real: eventos (por ejemplo, nacimientos, bodas, compromiso, uniones, defunciones, adopciones, etc.). No establecemos ninguna restricción sobre estos, excepto los lógicamente imposibles (por ejemplo, uno no puede ser el propio padre, las relaciones necesitan dos individuos, etc.)
La falta de validaciones nos da una solución más "real", más simple y más flexible.
En cuanto a este caso específico, sugeriría eliminar las afirmaciones, ya que no son válidas universalmente.
Para mostrar problemas (que surgirán), sugeriría dibujar el mismo nodo tantas veces como sea necesario, insinuando la duplicación encendiendo todas las copias al seleccionar una de ellas.
fuente
Relaja tus afirmaciones.
No cambiando las reglas, que probablemente sean muy útiles para el 99.9% de sus clientes al detectar errores al ingresar sus datos.
En cambio, cámbielo de un error "no se puede agregar la relación" a una advertencia con un "agregar de todos modos".
fuente
Aquí está el problema con los árboles genealógicos: no son árboles. Son gráficos acíclicos dirigidos o DAG. Si entiendo los principios de la biología de la reproducción humana correctamente, no habrá ningún ciclo.
Hasta donde yo sé, incluso los cristianos aceptan matrimonios (y, por lo tanto, hijos) entre primos, lo que convertirá el árbol genealógico en un DAG familiar.
La moraleja de la historia es: elegir las estructuras de datos correctas.
fuente
Supongo que tiene algún valor que identifica de manera única a una persona en la que puede basar sus cheques.
Este es complicado. Suponiendo que desea mantener la estructura en un árbol, sugiero esto:
Supongamos esto:
A
tiene hijos con su propia hija.A
se agrega al programa comoA
y comoB
. Una vez en el papel de padre, llamémoslo novio.Agregue una
is_same_for_out()
función que le diga a la salida que genera parte de su programa que todos los enlaces que vanB
internamente deberían ir a laA
presentación de datos.Esto hará un poco de trabajo extra para el usuario, pero supongo que sería relativamente fácil de implementar y mantener.
A partir de eso, podría trabajar en la sincronización de código
A
yB
evitar inconsistencias.Esta solución seguramente no es perfecta, pero es un primer enfoque.
fuente
Debería centrarse en lo que realmente hace valor para su software . ¿Vale la pena el precio de la licencia el tiempo dedicado a hacer que funcione para UN consumidor? Probablemente no.
Le aconsejo que se disculpe con este cliente, que le diga que su situación está fuera del alcance de su software y que le emita un reembolso.
fuente
Debería haber configurado la familia Atreides (ya sea moderna, Dune o antigua, Edipo Rey ) como un caso de prueba. No encuentra errores al usar datos desinfectados como un caso de prueba.
fuente
Esta es una de las razones por las cuales idiomas como "Go" no tienen afirmaciones. Se utilizan para manejar casos en los que probablemente no pensó, con demasiada frecuencia. Solo debes afirmar lo imposible, no simplemente lo improbable . Hacer esto último es lo que le da a las afirmaciones una mala reputación. Cada vez que escribe
assert(
, aléjese durante diez minutos y piense realmente en ello.En su caso particularmente inquietante, es concebible y atroz que tal afirmación sea falsa en circunstancias raras pero posibles. Por lo tanto, trátelo en su aplicación, aunque solo sea para decir "Este software no fue diseñado para manejar el escenario que presentó".
Afirmar que su tatara tatarabuelo es su padre como imposible es algo razonable.
Si estuviera trabajando para una compañía de pruebas que fue contratada para probar su software, por supuesto, habría presentado ese escenario. ¿Por qué? Cada 'usuario' juvenil pero inteligente hará exactamente lo mismo y saboreará en el 'informe de error' resultante.
fuente
Odio comentar sobre una situación tan jodida, pero la forma más fácil de no reajustar a todos sus invariantes es crear un vértice fantasma en su gráfico que actúe como un proxy para el padre incestuoso.
fuente
Entonces, he trabajado en el software del árbol genealógico. Creo que el problema que estás tratando de resolver es que debes poder caminar por el árbol sin entrar en bucles infinitos; en otras palabras, el árbol debe ser acíclico.
Sin embargo, parece que estás afirmando que solo hay un camino entre una persona y uno de sus antepasados. Eso garantizará que no haya ciclos, pero es demasiado estricto. Biológicamente hablando, la descendencia es un gráfico acíclico dirigido (DAG). El caso que tiene es ciertamente un caso degenerado, pero ese tipo de cosas suceden todo el tiempo en árboles más grandes.
Por ejemplo, si observa los 2 ^ n antepasados que tiene en la generación n, si no hubiera superposición, entonces tendría más antepasados en el año 1000 DC que personas vivas. Entonces, debe haber una superposición.
Sin embargo, también tiende a obtener ciclos que no son válidos, solo datos incorrectos. Si atraviesas el árbol, entonces los ciclos deben ser tratados. Puede hacer esto en cada algoritmo individual o en carga. Lo hice en carga.
Encontrar ciclos verdaderos en un árbol se puede hacer de varias maneras. La forma incorrecta es marcar a cada antepasado de un individuo determinado, y al atravesar, si la persona a la que va a pasar ya está marcada, corte el enlace. Esto cortará relaciones potencialmente precisas. La forma correcta de hacerlo es comenzar desde cada individuo y marcar a cada antepasado con el camino hacia ese individuo. Si la nueva ruta contiene la ruta actual como una ruta secundaria, entonces es un ciclo y debe romperse. Puede almacenar rutas como vector <bool> (MFMF, MFFFMF, etc.), lo que hace que la comparación y el almacenamiento sean muy rápidos.
Hay algunas otras formas de detectar ciclos, como enviar dos iteradores y ver si alguna vez chocan con la prueba de subconjunto, pero terminé usando el método de almacenamiento local.
También tenga en cuenta que no necesita cortar realmente el enlace, simplemente puede cambiarlo de un enlace normal a un enlace 'débil', al que no siguen algunos de sus algoritmos. También tendrá que tener cuidado al elegir qué enlace marcar como débil; a veces puedes averiguar dónde se debe romper el ciclo mirando la información de la fecha de nacimiento, pero a menudo no puedes descubrir nada porque faltan tantos datos.
fuente
Otra falsa respuesta seria para una pregunta tonta:
La respuesta real es utilizar una estructura de datos adecuada. La genealogía humana no se puede expresar completamente usando un árbol puro sin ciclos. Deberías usar algún tipo de gráfico. Además, hable con un antropólogo antes de continuar con esto, porque hay muchos otros lugares en los que se podrían cometer errores similares al tratar de modelar la genealogía, incluso en el caso más simple del "matrimonio monógamo patriarcal occidental".
Incluso si queremos ignorar las relaciones tabúes locales como se discutió aquí, hay muchas formas perfectamente legales y completamente inesperadas de introducir ciclos en un árbol genealógico.
Por ejemplo: http://en.wikipedia.org/wiki/Cousin_marriage
Básicamente, el matrimonio de primos no solo es común y esperado, es la razón por la cual los humanos han pasado de miles de pequeños grupos familiares a una población mundial de 6 mil millones. No puede funcionar de otra manera.
Realmente hay muy pocos universales cuando se trata de genealogía, familia y linaje. Casi cualquier suposición estricta sobre las normas que sugieren quién puede ser una tía, o quién puede casarse con quién, o cómo se legitiman los hijos con el propósito de heredar, puede verse alterada por alguna excepción en algún lugar del mundo o de la historia.
fuente
Dejando a un lado las posibles implicaciones legales, ciertamente parece que debe tratar a un 'nodo' en un árbol genealógico como una persona predecesora en lugar de asumir que el nodo puede ser la única persona.
Haga que el nodo del árbol incluya tanto a una persona como a los sucesores, y luego puede tener otro nodo más profundo en el árbol que incluya a la misma persona con diferentes sucesores.
fuente
Algunas respuestas han mostrado formas de mantener las aserciones / invariantes, pero esto parece un mal uso de aserciones / invariantes. Las afirmaciones son para asegurarse de que algo que debe ser verdadero sea verdadero, y los invariantes deben asegurarse de que algo que no debe cambiar no cambie.
Lo que estás afirmando aquí es que las relaciones incestuosas no existen. Es evidente que hacen existir, por lo que su afirmación no es válida. Puede evitar esta afirmación, pero el error real está en la afirmación misma. La afirmación debe ser eliminada.
fuente
Su árbol genealógico debe usar relaciones dirigidas. De esta manera no tendrás un ciclo.
fuente
Los datos genealógicos son cíclicos y no caben en un gráfico acíclico, por lo que si tiene afirmaciones contra los ciclos, debe eliminarlos.
La forma de manejar esto en una vista sin crear una vista personalizada es tratar al padre cíclico como un padre "fantasma". En otras palabras, cuando una persona es padre y abuelo de la misma persona, entonces el nodo abuelo se muestra normalmente, pero el nodo padre se representa como un nodo "fantasma" que tiene una etiqueta simple como ("ver abuelo" ) y señala al abuelo.
Para hacer cálculos, es posible que necesite mejorar su lógica para manejar gráficos cíclicos de modo que no se visite un nodo más de una vez si hay un ciclo.
fuente
Lo más importante es hacerlo
avoid creating a problem
, así que creo que debes usar una relación directa para evitar tener un ciclo.Como dijo @markmywords, #include "fritzl.h".
Finalmente tengo que decir
recheck your data structure
. Tal vez algo va mal allí (tal vez una lista enlazada bidireccional resuelva su problema).fuente
Las afirmaciones no sobreviven a la realidad
Por lo general, las afirmaciones no sobreviven al contacto con datos del mundo real. Es una parte del proceso de ingeniería de software decidir, con qué datos desea tratar y cuáles están fuera del alcance.
Gráficos familiares cíclicos
Con respecto a los "árboles" familiares (de hecho, son gráficos completos, incluidos los ciclos), hay una bonita anécdota:
Las cosas se ponen aún más extrañas cuando tomas en cuenta a los sustitutos o la "paternidad difusa".
Cómo lidiar con eso
Definir ciclos como fuera de alcance
Podrías decidir que tu software no debería tratar casos tan raros. Si ocurre tal caso, el usuario debe usar un producto diferente. Esto hace que lidiar con los casos más comunes sea mucho más robusto, porque puede mantener más afirmaciones y un modelo de datos más simple.
En este caso, agregue algunas buenas características de importación y exportación a su software, para que el usuario pueda migrar fácilmente a un producto diferente cuando sea necesario.
Permitir relaciones manuales
Podría permitir al usuario agregar relaciones manuales. Estas relaciones no son "ciudadanos de primera clase", es decir, el software los toma tal cual, no los verifica y no los maneja en el modelo de datos principal.
El usuario puede manejar casos raros a mano. Su modelo de datos seguirá siendo bastante simple y sus afirmaciones sobrevivirán.
Tenga cuidado con las relaciones manuales. Existe la tentación de hacerlos completamente configurables y, por lo tanto, crear un modelo de datos totalmente configurable. Esto no funcionará: su software no escalará, obtendrá errores extraños y finalmente la interfaz de usuario quedará inutilizable. Este antipatrón se llama "codificación suave" , y "El WTF diario" está lleno de ejemplos para eso.
Haga que su modelo de datos sea más flexible, omita aserciones, pruebe invariantes
El último recurso sería hacer que su modelo de datos sea más flexible. Tendría que omitir casi todas las afirmaciones y basar su modelo de datos en un gráfico completo. Como muestra el ejemplo anterior, es fácilmente posible ser su propio abuelo, por lo que incluso puede tener ciclos.
En este caso, debe probar exhaustivamente su software. Debes omitir casi todas las afirmaciones, por lo que hay una buena posibilidad de errores adicionales.
Use un generador de datos de prueba para verificar casos de prueba inusuales. Hay bibliotecas rápidos del cheque para Haskell , Erlang o C . Para Java / Scala hay ScalaCheck y Nyaya . Una idea de prueba sería simular una población aleatoria, dejar que se entrecrucen al azar, luego dejar que su software primero importe y luego exporte el resultado. La expectativa sería que todas las conexiones en la salida también están en la entrada y viceversa.
Un caso en el que una propiedad permanece igual se llama invariante. En este caso, el invariante es el conjunto de "relaciones románticas" entre los individuos en la población simulada. Trate de encontrar tantos invariantes como sea posible y pruébelos con datos generados aleatoriamente. Las invariantes pueden ser funcionales, por ejemplo:
O pueden ser técnicos:
Al ejecutar las pruebas simuladas, encontrará muchos casos extraños de esquina. Arreglarlos llevará mucho tiempo. También perderá muchas optimizaciones, su software funcionará mucho más lento. Tienes que decidir si vale la pena y si esto está dentro del alcance de tu software.
fuente
En lugar de eliminar todas las afirmaciones, aún debe verificar si una persona es su propio padre u otras situaciones imposibles y presentar un error. Tal vez emita una advertencia si es poco probable para que el usuario pueda detectar errores de entrada comunes, pero funcionará si todo es correcto.
Almacenaría los datos en un vector con un número entero permanente para cada persona y almacenaría a los padres y a los niños en objetos personales donde dicho int es el índice del vector. Esto sería bastante rápido entre generaciones (pero lento para cosas como búsquedas de nombres). Los objetos estarían en orden de cuando fueron creados.
fuente
Duplicar al padre (o usar enlace simbólico / referencia).
Por ejemplo, si está utilizando una base de datos jerárquica:
fuente
ln -s
comando no funciona de esa manera; la resolución del enlaceFamily/Son/Father
se buscaráFamily/Son/Daughter/Father
desde abajoFamily/Son
, donde reside el enlace, no desde.
donde emitió elln -s
comando.