Циклы в программном обеспечении генеалогического дерева

1515

Я разработчик программного обеспечения для семейного древа (написан на С++ и Qt). У меня не было проблем, пока один из моих клиентов не отправил мне сообщение об ошибке. Проблема в том, что у клиента есть двое детей со своей дочерью, и в результате он не может использовать мое программное обеспечение из-за ошибок.

Эти ошибки являются результатом моих различных утверждений и инвариантов относительно обрабатываемого графа семейства (например, после прохождения цикла программа заявляет, что X не может быть и отцом, и дедушкой Y).

Как я могу разрешить эти ошибки, не удаляя все утверждения данных?

  • 202
    Похоже, вы должны ограничивать продажу программного обеспечения теми, кто избегает попадания в сложные семейные ситуации! Как у тебя дети с собственной дочерью - надеюсь, ты говоришь о его невестке!
Показать ещё 17 комментариев
Теги:
graph
cycle
assertions
family-tree

18 ответов

727
Лучший ответ

Кажется, что у вас (и/или вашей компании) есть фундаментальное непонимание того, что такое семейное древо.

Позвольте мне уточнить, я также работаю в компании, которая (как один из ее продуктов) является генеалогическим деревом в своем портфолио, и мы боролись с аналогичными проблемами.

Проблема, в нашем случае, и я предполагаю, что ваш случай также исходит из формата GEDCOM, который крайне усомнился в том, что семья должна быть. Однако этот формат содержит некоторые серьезные неправильные представления о том, как выглядит семейное древо.

У GEDCOM много проблем, таких как несовместимость с такими же сексуальными отношениями, кровосмешение и т.д.... Что в реальной жизни происходит чаще, чем вы могли себе представить (особенно, когда вы возвращаетесь во времени до 1700-1800 годов).

Мы создали свое семейное древо в том, что происходит в реальном мире: события (например, рождение, свадьба, участие, союзы, смерть, усыновление и т.д.). Мы не накладываем на них никаких ограничений, кроме логически невозможных (например, нельзя быть одним родителем, отношениям нужны два человека и т.д.)

Отсутствие валидаций дает нам более "реальный мир", более простое и гибкое решение.

Что касается этого конкретного случая, я бы предложил удалить утверждения, поскольку они не имеют универсального значения.

Для отображения проблем (что будет возникать) я бы предложил рисовать те же самые node столько раз, сколько необходимо, намекая на дублирование, запустив все копии при выборе одного из них.

  • 32
    Это выглядит как правильный подход, и его достаточно легко расширить, чтобы обнаружить более сложные проблемы. Вы можете разработать набор отношений «А случилось до В» между событиями. Например, что человек родился до любых других событий, связанных с ним. Это ориентированный граф. Затем вы можете проверить, что график не содержит циклов. Смотрите этот вопрос на StackOverflow. Это должно быть хорошо, пока путешествие во времени не изобретено.
  • 41
    @ Пол-Харрисон Если это так просто. В старых записях (даже новых) имеются несоответствия даты. Крещение перед рождением, множественные записи о рождении и т. Д. Итак, в определенной степени, в официальных записях, есть путешествия во времени. Мы допускаем эти противоречивые данные. Мы разрешаем пользователям указывать, что приложение должно рассматривать как «запись о рождении» в случае дубликатов. И мы укажем нарушенные сроки, если найдены.
Показать ещё 6 комментариев
536

Расслабьте свои утверждения.

Не изменяя правила, которые, скорее всего, очень полезны для 99,9% ваших клиентов в ловушке ошибок при вводе их данных.

Вместо этого измените его с помощью ошибки "невозможно добавить отношение" к предупреждению с добавлением "все равно".

  • 143
    Когда вы сталкиваетесь с очень маловероятной ситуацией, то есть ситуацией, когда пользователь обычно делает это только по ошибке, хорошей идеей будет показать ему предупреждение. Это хорошая обратная связь. Но затем позвольте пользователю идти вперед, если он действительно уверен, что хочет. Поэтому я думаю, что это хороший ответ, даже если он не разбирается в том, как это сделать.
  • 15
    Хороший ответ! Мне просто интересно, как этот вид программного обеспечения справится с ситуацией «Я - мой дедушка» ( youtube.com/watch?v=eYlJH81dSiw )?
Показать ещё 5 комментариев
235

Здесь проблема с семейными деревьями: они не деревья. Они ориентированы на ациклические графы или DAG. Если я правильно понимаю принципы биологии человеческого воспроизводства, циклов не будет.

Насколько я знаю, даже христиане принимают браки (и, следовательно, детей) между двоюродными братьями, которые превратят семейное древо в семейную ДАГ.

Мораль истории: выберите правильные структуры данных.

  • 7
    Требуется дальнейшее ограничение каждого узла, имеющего 1 или 2 максимальных узла, указывающих на него, для in vitro и полового размножения. Хотя, чтобы быть более правдоподобным в реальной жизни, вы можете разрешить несколько пунктирных линий для неопределенного потомства на стороне отца (всегда ясно, кто является матерью, но только тестирование ДНК может гарантировать, кто такой отец, а это редко делается даже сегодня), или даже для обоих это усыновление принимается во внимание.
  • 7
    @manixrock - поскольку этот вопрос касается редких случаев, я хотел бы заявить, что не всегда ясно, кто является матерью. усыновление, брошенные дети, суррогатные мамы и т. д. могут осложнить ситуацию.
Показать ещё 5 комментариев
115

Я предполагаю, что у вас есть некоторая ценность, которая однозначно идентифицирует человека, на котором вы можете основывать свои чеки.

Это сложно. Предполагая, что вы хотите сохранить структуру дерева, я предлагаю следующее:

Предположим: A имеет детей со своей дочерью.

A добавляет себя к программе как A и как B. Однажды в роли отца, позвольте назвать его парнем.

Добавьте функцию is_same_for_out(), которая сообщает генерирующей части вашей программы, что все ссылки, идущие в B внутри, должны отправляться A при представлении данных.

Это сделает некоторую дополнительную работу для пользователя, но я думаю, что ИТ было бы относительно легко реализовать и поддерживать.

Основываясь на этом, вы можете работать с синхронизацией кода A и B во избежание несоответствий.

Это решение, безусловно, не идеально, но является первым подходом.

  • 9
    Вероятно, такие «прокси» узлы действительно являются подходящим решением. Однако я понятия не имею, как их можно вставить в пользовательский интерфейс, не оскорбляя пользователя. Я могу сказать вам, что написание программного обеспечения, которое имеет дело с реальными людьми (особенно вашими клиентами), не так просто.
  • 6
    Это никогда не заканчивается - новый сын Б будет его собственным дядей. Я хотел бы рассмотреть полный возврат средств за программу!
Показать ещё 2 комментария
84

Вам следует сосредоточиться на , что действительно делает ценность для вашего программного обеспечения. Является ли время, затрачиваемое на то, чтобы он работал для ОДНОГО потребителя по цене лицензии? Вероятно, нет.

Я советую вам извиниться перед этим клиентом, сказать ему, что его ситуация не подходит для вашего программного обеспечения и выдает ему возмещение.

  • 3
    Очень верно. Но также взвесьте другие потенциальные проблемы с подобными проблемами, которые другие подняли.
  • 2
    Конечно. Причина заключается в следующем: если это редкий крайний случай для некритического приложения, вам не нужно что-либо исправлять или реализовывать. Если это действительно вредит вашим пользователям, то стоит поработать над этим.
Показать ещё 3 комментария
82

Вам следует настроить семейство Atreides (либо современное, Dune, или древний, Eedipus Rex) в качестве теста. Вы не можете найти ошибки, используя дезинфицированные данные в качестве тестового примера.

  • 2
    К сожалению, слишком многие люди сначала думают о «хороших» данных, а не о крайних случаях, которые ломают их системы.
57

Это одна из причин, почему такие языки, как "Перейти", не имеют утверждений. Они используются для обработки случаев, о которых вы, вероятно, не думали, слишком часто. Вы должны только утверждать невозможное, а не просто маловероятное. Выполнение последнего - это то, что дает утверждениям плохую репутацию. Каждый раз, когда вы печатаете assert(, уходите на десять минут и действительно думайте об этом.

В вашем особенно тревожном случае можно мыслить и ужасать, что такое утверждение будет поддельным при редких, но возможных обстоятельствах. Следовательно, обрабатывайте его в своем приложении, если только сказать "Это программное обеспечение не предназначено для обработки предложенного вами сценария".

Утверждение, что ваш великий, великий, прадед, являющийся вашим отцом невозможным, является разумным делом.

Если бы я работал в тестирующей компании, которая была нанята для тестирования вашего программного обеспечения, я бы, конечно, представил этот сценарий. Зачем? Каждый несовершеннолетний, но интеллектуальный "пользователь" собирается делать то же самое и наслаждаться в результате "отчета об ошибке".

  • 48
    Не забудьте замороженную сперму ...
  • 5
    Согласитесь с аргументом «когда использовать утверждения»; не понимаю, как это связано с тем, что «в некоторых языках есть утверждения, а в Go нет».
Показать ещё 8 комментариев
39

Мне не нравится комментировать такую ​​придуманную ситуацию, но самый простой способ не перенацелить всех ваших инвариантов - создать вершину phantom в вашем графике, которая будет служить прокси-сервером обратно к инцестуму папе.

36

Итак, я проделал некоторую работу над программным обеспечением семейства деревьев. Я думаю, что проблема, которую вы пытаетесь решить, заключается в том, что вам нужно иметь возможность ходить по дереву, не входя в бесконечные петли, другими словами, дерево должно быть ацикличным.

Однако, похоже, вы утверждаете, что существует только один путь между человеком и одним из их предков. Это гарантирует отсутствие циклов, но слишком строгий. Биологически говоря, нисхождение - это направленный ациклический граф (DAG). Случай, который у вас есть, конечно, является дегенеративным случаем, но этот тип вещей происходит все время на больших деревьях.

Например, если вы посмотрите на предков 2 ^ n, которые у вас есть при генерации n, если бы не было перекрытия, тогда у вас было бы больше предков в 1000 н.э., чем были люди в живых. Таким образом, должно быть перекрытие.

Однако вы также склонны получать недопустимые циклы, просто плохие данные. Если вы пересекаете дерево, тогда должны быть обработаны циклы. Вы можете сделать это в каждом отдельном алгоритме или при загрузке. Я сделал это при загрузке.

Поиск истинных циклов в дереве можно сделать несколькими способами. Неправильный способ состоит в том, чтобы пометить каждого предка от данного человека, и когда вы пройдете, если человек, которого вы собираетесь сделать шаг дальше, уже отмечен, а затем отредактируйте ссылку. Это разорвет потенциально точные отношения. Правильный способ сделать это - начать с каждого человека и пометить каждого предка по пути к этому человеку. Если новый путь содержит текущий путь в качестве подпути, то это цикл и должен быть сломан. Вы можете хранить пути как vector <bool> (MFMF, MFFFMF и т.д.), Что значительно ускоряет сравнение и хранение.

Существует несколько других способов обнаружения циклов, таких как отправка двух итераторов и наблюдение, столкнулись ли они с тестом подмножества, но в итоге я использовал локальный метод хранения.

Также обратите внимание, что вам не нужно действительно разорвать ссылку, вы можете просто изменить ее из обычной ссылки на "слабую" ссылку, за которой не следуют некоторые из ваших алгоритмов. Вы также захотите позаботиться о том, чтобы выбрать, какую ссылку обозначить как слабую; иногда вы можете выяснить, где цикл должен быть разбит, глядя на информацию о дате рождения, но часто вы ничего не можете понять, потому что так много данных отсутствует.

  • 0
    Осторожнее с этими предположениями; один мужчина и одна женщина-родитель - это не данность, когда люди приспосабливаются, или лесбиянки, которые считают себя родителями, в ближайшем будущем они могут даже стать биологически родителями, по крайней мере, девочек. В этом отношении, если мы применяем тележку к людям, даже предположение «у человека есть два разных родителя» отсутствует.
  • 1
    @Agrajag, да, поэтому я определил "биологически говоря" для обнаружения цикла. Даже в биологическом отношении существует множество возможных проблем, таких как суррогатные матери и искусственное оплодотворение. Если вы также разрешаете усыновление и другие небиологические методы определения родителей, то возможно иметь действительный истинный цикл в дереве - например, может быть, кто-то усыновляет своего прародителя, когда он стареет, и больше не может заботиться о себе. , Делать предположения о семейной жизни людей всегда сложно. Но при написании программного обеспечения вам нужно сделать некоторые предположения ..
35

Еще один макет серьезного ответа на глупый вопрос:

Реальный ответ: используйте соответствующую структуру данных. Человеческая генеалогия не может быть полностью выражена с использованием чистого дерева без циклов. Вы должны использовать какой-то график. Кроме того, поговорите с антропологом, прежде чем идти дальше с этим, потому что есть много других мест, подобных ошибкам можно было бы попытаться смоделировать генеалогию, даже в самом простом случае "западного патриархального моногамного брака".

Даже если мы хотим игнорировать локально-табу-отношения, как обсуждалось здесь, существует множество совершенно законных и совершенно неожиданных способов введения циклов в семейное дерево.

Например: http://en.wikipedia.org/wiki/Cousin_marriage

В принципе, брак двоюродного брака не только обычен и ожидаем, это причина, по которой люди перешли от тысяч небольших семейных групп к мировому населению в 6 миллиардов. Он не может работать иначе.

На самом деле очень мало универсалей, когда речь идет о генеалогии, семье и родословной. Почти любое строгое предположение о нормах, предполагающих, кем может быть тетя, или кто может жениться, кто и как узаконивается ребенок с целью наследования, может быть расстроено каким-то исключением где-то в мире или истории.

  • 9
    Ваш комментарий заставил меня задуматься о многоженстве. Программное обеспечение для генеалогии, которое моделирует только половое размножение, может требовать имя, прикрепленное к сперме и яйцеклетке, но более широкое определение структуры семьи не требует.
  • 0
    Генеалогическое программное обеспечение часто позволяет использовать в модели более одного супруга. То, как вы отображаете модель в представлении, широко варьируется, даже в пределах одной программы, в зависимости от предоставленного «режима».
20

Потенциальные юридические последствия в стороне, конечно, кажется, что вам нужно рассматривать "node" на генеалогическом древе как предшественник, а не предполагать, что node может быть единственным человеком.

В дереве node включается человек, а также его преемники, а затем вы можете добавить еще один node вниз по дереву, включающему одного и того же человека с разными преемниками.

13

Несколько ответов показали способы сохранения утверждений/инвариантов, но это кажется неправильным использованием утверждений/инвариантов. Утверждения должны удостовериться, что то, что должно быть истинным, истинно, и инварианты должны убедиться, что что-то, что не должно меняться, не изменяется.

То, что вы утверждаете здесь, заключается в том, что кровосмесительные отношения не существуют. Очевидно, что они существуют, поэтому ваше утверждение недействительно. Вы можете обойти это утверждение, но реальная ошибка заключается в самом утверждении. Утверждение должно быть удалено.

8

Ваше семейное древо должно использовать направленные отношения. Таким образом, у вас не будет цикла.

5

Генеалогические данные являются циклическими и не вписываются в ациклический граф, поэтому, если у вас есть утверждения против циклов, вы должны их удалить.

Способ обработки этого в представлении без создания настраиваемого представления заключается в том, чтобы рассматривать циклического родителя как родителя-призрака. Другими словами, когда человек является и отцом, и дедушкой одного и того же человека, тогда дед node показан нормально, но отец node представляется как "призрак" node, который имеет простую метку (см. дед) и указывает на дедушку.

Для выполнения расчетов вам может понадобиться улучшить вашу логику для обработки циклических графов, чтобы node не посещался более одного раза, если есть цикл.

4

Утверждения не выдерживают реальности

Обычно утверждения не выдерживают контакта с данными реального мира. Это часть процесса разработки программного обеспечения, чтобы решить, с какими данными вы хотите справиться и которые не входят в сферу действия.

Графы циклического семейства

Что касается семейных "деревьев" (на самом деле это полномасштабные графики, включая циклы), есть хороший анекдот:

Я женился на вдове, у которой была взрослая дочь. Мой отец, который часто бывал у нас, влюбился в мою дочери и женился на ней. В результате мой отец стал моим сыном, а моя дочь стала моей матерью. Через некоторое время я отдал жене сына, который был братом моего отца и моего дяди. Моя жена-отец (которая тоже моя дочь и моя мать) получила сына. В результате у меня появился брат и внук в одном лице. Моя жена теперь моя бабушка, потому что она моя мать. Итак, я муж моей жены и в то же время правнук моей жены. Другими словами, я - мой собственный дедушка.

Все становится еще страннее, когда вы принимаете суррогаты или "нечеткое отцовство".

Как с этим бороться

Определить циклы как внеуровневые

Вы можете решить, что ваше программное обеспечение не должно заниматься такими редкими случаями. Если такой случай возникает, пользователь должен использовать другой продукт. Это делает работу с более распространенными случаями более надежной, поскольку вы можете сохранить больше утверждений и более простую модель данных.

В этом случае добавьте некоторые хорошие функции импорта и экспорта в ваше программное обеспечение, чтобы пользователь мог легко перейти к другому продукту, когда это необходимо.

Разрешить ручные отношения

Вы можете позволить пользователю добавлять ручные отношения. Эти отношения не являются "первоклассными гражданами", то есть программное обеспечение принимает их как есть, не проверяет их и не обрабатывает их в основной модели данных.

Затем пользователь может обрабатывать редкие случаи вручную. Ваша модель данных будет оставаться довольно простой, и ваши утверждения выживут.

Будьте осторожны с ручными отношениями. Существует соблазн сделать их полностью настраиваемыми и, следовательно, создать полностью настраиваемую модель данных. Это не сработает: ваше программное обеспечение не будет масштабироваться, вы получите странные ошибки, и, наконец, пользовательский интерфейс станет непригодным. Этот анти-шаблон называется "мягкое кодирование" и "Ежедневный WTF" содержит примеры для этого.

Сделайте вашу модель данных более гибкой, пропустите утверждения, проверьте инварианты

Последнее средство сделает вашу модель данных более гибкой. Вам нужно будет пропустить почти все утверждения и основывать свою модель данных на полномасштабном графике. Как видно из приведенного выше примера, легко быть вашим собственным дедушкой, поэтому вы можете даже иметь циклы.

В этом случае вы должны тщательно протестировать свое программное обеспечение. Вам пришлось пропустить почти все утверждения, поэтому есть хорошие шансы на дополнительные ошибки.

Используйте генератор тестовых данных для проверки необычных тестовых случаев. Есть библиотеки быстрой проверки для Haskell, Erlang или C. Для Java/Scala существуют ScalaCheck и Nyaya. Одна тестовая идея заключалась бы в моделировании случайной популяции, позволяющей ей скрещиваться наугад, а затем сначала запускать ваше программное обеспечение, а затем экспортировать результат. Ожидание будет состоять в том, что все соединения на выходе также находятся на входе и наоборот.

Случай, когда свойство остается тем же, называется инвариантом. В этом случае инвариантом является множество "романтических отношений" между индивидуумами в моделируемой популяции. Попытайтесь найти как можно больше инвариантов и протестировать их со случайно генерируемыми данными. Инварианты могут быть функциональными, например:

  • дядя остается дядей, даже когда вы добавляете больше "романтических отношений".
  • у каждого ребенка есть родительский
  • население с двумя поколениями имеет по крайней мере один большой родительский

Или они могут быть техническими:

  • Ваше программное обеспечение не будет разбиваться на график до 10 миллиардов членов (независимо от того, сколько межсоединений)
  • Ваше программное обеспечение масштабируется с помощью O (число узлов) и O (число ребер ^ 2)
  • Ваше программное обеспечение может сохранять и перезагружать каждый семейный график до 10 миллиардов членов.

Запустив симулированные тесты, вы найдете множество странных угловых случаев. Фиксация их займет много времени. Также вы потеряете много оптимизаций, ваше программное обеспечение будет работать намного медленнее. Вы должны решить, если это того стоит, и если это входит в объем вашего программного обеспечения.

4

Самое главное - avoid creating a problem, поэтому я считаю, что вам нужно использовать прямую связь, чтобы избежать цикла.

Как указано в @markmywords, #include "fritzl.h".

Наконец, я должен сказать recheck your data structure. Возможно, что-то происходит неправильно (возможно, двунаправленный связанный список решает вашу проблему).

3

Вместо того, чтобы удалять все утверждения, вы все равно должны следить за тем, как человек, являющийся его родителем или другими невозможными ситуациями, и представить ошибку. Может быть, выдать предупреждение, если маловероятно, чтобы пользователь все еще обнаруживал общие ошибки ввода, но он будет работать, если все будет правильно.

Я бы сохранил данные в векторе с постоянным целым числом для каждого человека и сохранил родителей и детей в личных объектах, где указанный int является индексом вектора. Это было бы довольно быстро, чтобы идти между поколениями (но медленно для таких вещей, как поиск по имени). Объекты будут в порядке их создания.

-4

Дублируйте отца (или используйте символическую ссылку/ссылку).

Например, если вы используете иерархическую базу данных:

$ #each person node has two nodes representing its parents.
$ mkdir Family
$ mkdir Family/Son
$ mkdir Family/Son/Daughter
$ mkdir Family/Son/Father
$ mkdir Family/Son/Daughter/Father
$ ln -s Family/Son/Daughter/Father Family/Son/Father
$ mkdir Family/Son/Daughter/Wife
$ tree Family
Family
└── Son
    ├── Daughter
    │   ├── Father
    │   └── Wife
    └── Father -> Family/Son/Daughter/Father

4 directories, 1 file
  • 3
    Команда ln -s не работает таким образом; разрешение ссылки Family/Son/Father будет искать Family/Son/Daughter/Father из-под Family/Son , где находится ссылка, а не из . где вы дали команду ln -s .
  • 48
    клонирование запрещено Женевскими конвенциями

Ещё вопросы

Сообщество Overcoder
Наверх
Меню