Это одна из нескольких проблем, оставленных сообществу Хобби Кальвина .
Возьмите файл описания семейного древа со строками вида:
[ID] [mother ID] [father ID] [gender] [full name]
например, это описывает первое генеалогическое древо по адресу http://en.wikipedia.org/wiki/Cousin :
1 ? ? M Adam
2 ? ? F Agatha
3 ? ? M Bill
4 2 1 F Betty
5 2 1 M Charles
6 ? ? F Corinda
7 3 4 M David
8 6 5 F Emma
Напишите программу или функцию, которая берет имя файла и два идентификатора и выводит, как эти люди связаны с кровными простыми словами, используя общие английские имена для отношений. Входные данные могут быть через STDIN, ARGV или аргументы функции, но выходные данные должны быть в STDOUT.
Заметки
- Идентификаторы являются положительными целыми числами.
?
используется, когда происхождение неизвестно.- Предположим, что граф будет связан и не имеет циклов.
- Вы не можете предполагать, что родители каждого человека указаны в списке перед этим человеком (поэтому идентификатор этого человека может быть больше, чем его собственный идентификатор).
- Предположим, что все являются мужчинами или женщинами, и у каждого есть ровно одна мать и ровно один отец (правильного пола), хотя они могут быть неизвестны.
- Предположим, имена уникальны.
- В именах могут быть пробелы.
Кровные отношения
Следующие определения отношений R определить , если человек является R или лицо Б . Если два варианта R перечислены, во - первых, женщина А , а второй для мужской A . Все это необходимо реализовать. Если несколько определений совпадают, следует использовать более раннее. Термины в скобках являются нейтральными в гендерном отношении терминами, которые не нуждаются в применении, но будут повторно использованы в дальнейших определениях. В определениях, включающих N и M , предположим, что N> 1 и M> 0 .
- дочь / сын: A перечисляет B как любого из родителей.
- мать / отец (родитель): B указывает A в качестве любого из родителей.
- сестра / брат (сестра): А и Б перечисляют одну и ту же мать и отца.
- сводная сестра / сводный брат (родной брат): A и B перечисляют одну и ту же мать или одного и того же отца.
- племянница / племянник: А списки родителя , который является родственником B .
- тетя / дядя: В это «племянница или племянник.
- внучка / внук (внук): A перечисляет родителя, который указывает B в качестве родителя.
- бабушка / дедушка (дедушка): B - внук A.
- внучатая племянница / внучатый племянник: является внуком C , который является сестринским B .
- двоюродная тетя / двоюродный: B является «s внучатой племянницей или внучатым племянником.
- правнучка / сын (1-й правнук): A - внук C , в котором B указан как родитель.
- прабабушка / отец (первый большой-прародитель): В это первый правнук «s.
- N-й правнучка / сын (N-й правнук): A - (N-1)-й внук C, который перечисляет B как своего родителя.
- Nth прабабушка / отец (Nth прадедах): B является Nth правнук «s.
- N - й внучатая племянница / племянник: является (N-1) -го правнук C , который является родственником B .
- Nth двоюродная тетя / дядя: B является «s Nth внучатая племянница N - го двоюродного племянника.
- кузен: является внуком C , который является прародителем B .
- N - ый кузен: является (N-1) -го внуком C , который является (N-1) -го прародителем B .
- кузен, М раз удален: является внуком C , который является Mth прародителем B или является Mth внуком C , который является прародителем B .
- N-й двоюродный брат, M раз удалены: A - это Pth правнук C, который является Q-й прадедушкой B , где
N = min(P,Q) + 1
иM = |P-Q|
.
Для получения Nth
, записи 2nd
, 3rd
, 4th
, и 5th
т.д.
Для получения M times
, записи once
, twice
, thrice
, 4 times
, и 5 times
т.д.
Примеры
Предположим, что используется следующий файл (вам не нужно иметь дело с несколькими пробелами, но я добавил их для удобочитаемости):
1 ? ? F Agatha
2 ? ? M Adam
3 ? ? F Betty
4 1 2 M Bertrand
5 1 2 F Charlotte
6 ? ? M Carl
7 ? ? F Daisy
8 3 4 M David
9 5 6 F Emma
10 ? ? M Edward
11 ? ? F Freya
12 7 8 M Fred
13 9 10 F Grace
14 ? ? M Gerald
15 ? ? F Hillary
16 11 12 M Herbert
17 13 14 F Jane
18 ? ? M James
19 15 16 F Kate
20 17 18 M Larry
21 ? 18 F Mary
Затем входные идентификаторы должны отображаться на выходы следующим образом:
1 2 --> Agatha is not a blood relative to Adam.
8 3 --> David is the son of Betty.
9 13 --> Emma is the mother of Grace.
4 5 --> Bertrand is the brother of Charlotte.
9 4 --> Emma is the niece of Bertrand.
5 8 --> Charlotte is the aunt of David.
16 7 --> Herbert is the grandson of Daisy.
1 9 --> Agatha is the grandmother Emma.
12 5 --> Fred is the great-nephew of Charlotte.
4 13 --> Bertrand is the great-uncle of Grace.
16 3 --> Herbert is the great-grandson of Betty.
6 17 --> Carl is the great-grandfather of Jane.
19 2 --> Kate is the 3rd great-granddaughter of Adam.
1 17 --> Agatha is the 2nd great-grandmother of Jane.
20 4 --> Larry is the 3rd great-nephew of Bertrand.
5 16 --> Charlotte is the 2nd great-aunt of Herbert.
8 9 --> David is the cousin of Emma.
19 20 --> Kate is the 4th cousin of Larry.
16 9 --> Herbert is the cousin, twice removed, of Emma.
12 17 --> Fred is the 2nd cousin, once removed, of Jane.
21 20 --> Mary is the half-sister of Larry.
Я написал их от руки, поэтому дайте мне знать, если вы заметите какие-либо ошибки.
Другой набор тестовых данных (предоставленный Скоттом Лидли, любые ошибки - мои, а не Мартина).
Семейное древо Птолемея
. Изображение иллюстративное; приведенные ниже данные взяты из статьи в Википедии " Династия Птолемеев ".
1 ? ? F Berenice I of Egypt
2 ? ? M Ptolemy I Soter
41 1 2 F Arsinoe II of Egypt
3 1 2 M Ptolemy II Philadelphus
4 ? ? F Arsinoe I of Egypt
5 ? ? M Philip
6 4 3 M Ptolemy III Euergetes
7 1 5 F Magas of Cyrene
8 7 ? F Berenice II
9 8 6 M Ptolemy IV Philopator
10 8 6 F Arsinoe III of Egypt
11 10 9 M Ptolemy V Epiphanes
12 ? ? F Cleopatra I of Egypt
13 12 11 M Ptolemy VI Philometor
14 12 11 F Cleopatra II
15 12 11 M Ptolemy VIII Physcon
19 ? ? F Eirene
16 14 13 M Ptolemy VII Neos Philopator
17 14 13 F Cleopatra III
18 14 15 M Ptolemy Memphites
20 19 15 M Ptolemy Apion
21 17 15 F Cleopatra IV
22 17 15 M Ptolemy IX Lathyros
23 17 15 F Cleopatra Selene I
24 17 15 M Ptolemy X Alexander I
25 23 22 F Berenice III of Egypt
26 23 24 M Ptolemy XI Alexander II
27 21 22 M Ptolemy XII Auletes
28 25 24 F Cleopatra V of Egypt
29 28 27 F Cleopatra VI of Egypt
30 28 27 F Berenice IV of Egypt
31 28 27 M Ptolemy XIII Theos Philopator
32 28 27 F Cleopatra VII Thea Philopator
33 28 27 M Ptolemy XIV
34 28 27 F Arsinoe IV of Egypt
35 ? ? M Julius Caesar
37 32 35 M Ptolemy XV Caesarion
36 ? ? M Mark Anthony
38 32 36 M Alexander Helios
39 32 36 M Ptolemy XVI Philadelphus
40 32 36 F Cleopatra Selene II
источник
Рубин -
189212901247Запустить как
ruby relation.rb ID1 ID2 relationship_file
.Ungolfed версия -
52513416 (то же дерево вызовов, только что сделал много свёртывания кода)Проходит следующий набор тестов:
источник
Javascript, 2292
Я уверен, что это может быть сыграно намного дальше, все, что я сделал, это вставил версию без гольфа через минификатор.
Вы можете запустить версию ungolfed здесь на jsFiddle . Вот вывод для данных примера:
источник
Питон 3: 1183
Имя файла и идентификаторы людей, которые будут описаны, читаются из стандартного ввода в одну строку.
Верхняя часть кода - определения функций. Сценарий запускается на полпути вниз и сначала обрабатывает входные данные (анализирует файл, затем присваивает дочерним элементам своих родителей за второй проход).
После того, как данные настроены, мы вызываем
A
функцию один раз, чтобы начать рекурсивный поиск. Результат определяет отношения.Остальная часть кода посвящена описанию этих отношений на английском языке. Братья и сестры сложны (и используют длинные очереди), остальные довольно просты.
Пример выполнения (вторая строка - мой ввод):
Ключ функции и имени переменной:
f
: Имя файла, из которого считываются данные семейства.a
Идентификатор человека, чьи отношения мы называем.b
Идентификатор человека, с которым отношения определены.t
Само генеалогическое древо, как словарь, отображающий из идентификатора в 5 кортежей идентификатор матери, идентификатор отца, пол, имя и список детей.g
: Булево значение, отражающее пол человекаa
. ЭтоTrue
если они мужчины.u
: Число поколений отb
общего предкаa
иb
(или 0, еслиb
этоa
предок ').d
: Число поколений отa
общего предкаa
иb
(или 0, еслиa
этоb
предок ').D(i)
: Рекурсивный поиск потомков человекаi
для человекаa
. Вернуть глубину, котораяa
была найдена, или Нет, если она не была найдена.A(i)
: Рекурсивный поискi
иi
потомков, но если он не найден, рекурсивный поиск такжеi
и предков (и их потомков). Возвращает 2-кортеж, значения которогоu
иd
описаны выше. Если связь обнаружена у обоих родителей,u+d
предпочтительна связь с наименьшим числом шагов поколений ( ). Если человекa
не имеет кровных отношений с нимi
,A(i)
возвращаетсяNone
.P(r)
: Вывести строку результата вr
скобках с именами людейa
иb
.O(n)
: Вернуть порядковую строку для заданного числаn
. Только поддерживает1 < n < 21
.G(n)
Возвращает строку префикса, эквивалентнуюn-1
"greats" (например,"2nd great-"
для n = 2`). Вернет пустую строку для n <= 1.GG(n)
: Возвращает строку префикса с "Nth great-" и "grand", в зависимости отn
поколения. Вернет пустую строку для n <= 1.Я выбрал несколько ярлыков во имя более короткого кода, который можно было бы пересмотреть для лучшей (или чуть более правильной) производительности на больших генеалогиях.
A
Функция не делает никаких попыток избежать рекурсии вниз дочерних деревьев , которые уже искали, что делает его медленнее , чем это необходимо (хотя , вероятно , все еще достаточно быстро для разумных размеров семей).O
Функция не правильно обрабатывать ординалы больше 20 (это немного сложнее , чтобы получить все11th
,21st
и101st
правильно, но в одном из моих проектов версий я сделал это около 25 дополнительных байтов). Если вы не имеете дело с очень старыми и известными семьями (например, с некоторыми из королевских семей Европы), я не уверен, что доверял бы точности генеалогии, которая все равно ушла так далеко.С другой стороны, я также пропустил несколько мест, где я мог сбрить несколько байтов. Например, я мог бы сэкономить 3 байта, переименовав
GG
его в одноименное имя, ноgreat-grand
мне казалось, что использование имени на основе более целесообразно.Я считаю, что все пробелы в коде требуются, но возможно, что некоторые могут быть пропущены, и я просто пропустил их (я продолжал находить случайные пробелы в списках аргументов, пока набирал этот ответ, но я думаю, что ' мы получили их все сейчас).
Поскольку для моего рекурсивного сопоставления требуется относительно простое правило, для которого предпочтительны отношения, если их несколько, я не даю точно запрошенные результаты в некоторых неясных случаях, связанных с инцестом между поколениями. Например, если человек
a
иb
дядя и дедушка человека, мой кодекс предпочтет отношения дедушки, несмотря на вопрос о том, что отношения дяди должны иметь более высокий приоритет.Вот пример набора данных, который раскрывает проблему:
Я подозреваю, что для большинства программ отношения между Бобом и Клэр или между Бобом и Даниэль вызовут проблемы. Они либо назовут половину братьев и сестер первой пары, а не отца / дочь, либо они будут описывать последнюю пару как дедушка / внучка, а не дядя / племянница. Мой код выполняет последнее, и я не вижу разумного способа изменить его, чтобы получить запрошенные результаты, не ошибаясь при этом в первой паре.
источник
Тестовый пакет. Заполните его в t / Relations.t и запустите «Доказать» или «Perl T / Relations.t». В настоящее время предполагается, что файл программы - "отношение.rb".
Это вики сообщества, так что не стесняйтесь добавлять тесты. Если вы измените его, я думаю, что временная метка (или другой очевидный флаг) будет в порядке. Список желаний:
источник