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

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

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

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

Ответ 1

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

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

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

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

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

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

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

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

Ответ 2

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

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

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

Ответ 3

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

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

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

Ответ 4

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

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

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

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

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

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

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

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

Ответ 5

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

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

Ответ 6

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

Ответ 7

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

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

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

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

Ответ 8

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

Ответ 9

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

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

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

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

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

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

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

Ответ 10

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

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

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

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

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

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

Ответ 11

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

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

Ответ 12

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

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

Ответ 13

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

Ответ 14

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

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

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

Ответ 15

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

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

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

Ответ 16

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Ответ 17

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

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

Ответ 18

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

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

$ #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