C'est l'un des nombreux défis laissés à la communauté par Calvin's Hobbies .
Prenez un fichier "arbre généalogique décrivant" avec des lignes du formulaire:
[ID] [mother ID] [father ID] [gender] [full name]
comme celui-ci qui décrit le premier arbre généalogique sur 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
Écrivez un programme ou une fonction qui prend le nom de fichier et deux ID et affiche comment ces personnes sont liées au sang en termes plus simples, en utilisant les noms anglais courants pour les relations. L'entrée peut être via STDIN, ARGV ou des arguments de fonction, mais la sortie doit être vers STDOUT.
Remarques
- Les ID sont des entiers positifs.
?
est utilisé lorsque la filiation n'est pas connue.- Supposons que le graphique soit connecté et ne comporte aucun cycle.
- Vous ne pouvez pas supposer que les parents de chaque personne sont répertoriés avant cette personne (donc l'ID parent d'une personne peut être supérieur à leur propre ID).
- Supposons que tout le monde est un homme ou une femme et que chacun a exactement une mère et exactement un père (de sexe correct), bien qu'ils puissent être inconnus.
- Supposons que les noms sont uniques.
- Les noms peuvent contenir des espaces.
Relations avec le sang
Les définitions suivantes des rapports R déterminer si la personne A est R ou de la personne B . Si deux variantes de R sont répertoriés, le premier est pour femme A et le second pour les hommes A . Tous ces éléments doivent être mis en œuvre. Si plusieurs définitions correspondent, la précédente doit être utilisée. Les termes entre parenthèses sont des termes non sexistes, qui n'ont pas besoin d'être mis en œuvre mais seront réutilisés dans d'autres définitions. Dans les définitions impliquant N et M , supposons N> 1 et M> 0 .
- fille / fils: A indique B comme parent.
- mère / père (parent): B indique A comme parent.
- soeur / frère (frère ou sœur): A et B énumèrent les mêmes mère et père.
- demi-sœur / demi-frère (frère ou sœur): A et B énumèrent la même mère ou le même père.
- nièce / neveu: Une liste un parent qui est le frère de B .
- tante / oncle: B est la nièce ou le neveu de A.
- petite-fille / petit-fils (petit-enfant): A répertorie un parent qui répertorie B comme parent.
- grand - mère / grand-père (grand - parent): B est un petit enfant s ».
- petite-nièce / petit-neveu: A est le petit - fils de C qui est le frère de B .
- grand-tante / grand-oncle: B est la petite-nièce ou le petit-neveu de A.
- arrière-petite-fille / fils (1er arrière-petit-enfant): A est un petit-enfant de C qui inscrit B comme parent.
- arrière-grand-mère / père (1er arrière-grand-parent): B est le premier arrière-petit-enfant de A.
- Nième arrière-petite-fille / fils (Nième arrière-petit-enfant): A est un (N-1) ème petit-enfant de C qui a inscrit B comme parent.
- Nième grand-mère / père (Nième grand-parents): B est un Nième-petit-enfant de.
- Nième grand-nièce / neveu: A est le (N-1) ième bisnieto de C qui est le frère de B .
- Nième grand-tante / oncle: B est un grand-nièce Nième grand-neveu Nième.
- cousine: A est le petit - fils de C qui est le grand - parent de B .
- Cousine Nth: A est le (N-1) ème des petits - C qui est le (N-1) ième grand - parent de B .
- cousin, M fois enlevé: A est le petit - fils de C qui est le grand - parent Mth de B ou A est le petit - fils Mth de C qui est le grand - parent de B .
- Nième cousin, M fois retiré: A est le Pth arrière-petit-enfant de C qui est le Qth arrière-grand-parent de B , où
N = min(P,Q) + 1
etM = |P-Q|
.
Pour Nth
, écriture 2nd
, 3rd
, 4th
, 5th
etc.
Pour M times
, écriture once
, twice
, thrice
, 4 times
, 5 times
etc.
Exemples
Supposons que le fichier suivant soit utilisé (vous n'avez pas besoin de pouvoir gérer plusieurs espaces, mais je les ai ajoutés pour plus de lisibilité):
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
Les ID d'entrée doivent ensuite correspondre aux sorties comme suit:
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.
Je les ai rédigés à la main, alors faites-moi savoir si vous repérez des erreurs.
Un autre ensemble de données de test (fourni par Scott Leadley, toutes les erreurs sont les miennes et non celles de Martin)
Arbre généalogique de Ptolemy
L'image est illustrative; les données ci-dessous proviennent de l'article de Wikipédia " Dynastie ptolémaïque ".
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
la source
Rubis -
189212901247Exécuter en tant que
ruby relation.rb ID1 ID2 relationship_file
.Version non golfée -
52513416 (même arborescence d'appels, vient de faire beaucoup de pliage de code)Réussit la suite de tests suivante:
la source
Javascript, 2292
Je suis sûr qu'il peut être joué beaucoup plus loin, tout ce que j'ai fait a été de mettre une version non golfée à travers une minifieuse.
Vous pouvez exécuter la version non golfée ici sur jsFiddle . Voici la sortie pour les données d'exemple:
la source
Python 3: 1183
Le nom de fichier et les identifiants des personnes à décrire sont lus à partir de l'entrée standard sur une seule ligne.
La partie supérieure du code est la définition des fonctions. Le script démarre à mi-chemin et travaille d'abord sur le traitement de l'entrée (analyse du fichier, puis affectation des enfants à leurs parents dans un deuxième passage).
Une fois les données configurées, nous appelons la
A
fonction une fois pour lancer une recherche récursive. Le résultat définit la relation.Le reste du code est dédié à la description de cette relation en anglais. Les frères et sœurs et les cousins sont compliqués (et utilisent de longues lignes), le reste est assez simple.
Exemple d'exécution (la deuxième ligne est mon entrée):
Touche de fonction et de nom de variable:
f
: Le nom de fichier à partir duquel les données de la famille sont lues.a
: L'identifiant de la personne dont nous nommons la relation.b
: L'identifiant de la personne à laquelle la relation est définie par rapport.t
: L'arbre généalogique lui-même, en tant que dictionnaire mappant un identifiant à 5 tuple de l'identifiant de la mère, l'identifiant du père, le sexe, le nom et une liste d'enfants.g
: Une valeur booléenne reflétant le sexe de la personnea
. C'estTrue
si c'est un homme.u
: Le nombre de générations deb
à l'ancêtre commun dea
etb
(ou 0 sib
esta
l'ancêtre de).d
: Le nombre de générations dea
à l'ancêtre commun dea
etb
(ou 0 sia
estb
l'ancêtre de).D(i)
: Recherche récursivement les descendants de personnei
pour personnea
. Retourne que la profondeur aa
été trouvée à, ou Aucune si elle n'a pas été trouvée.A(i)
: Recherche récursivei
eti
« s descendants, mais si la recherche ne trouve récursivei
» ancêtres (et leurs descendants) aussi. Renvoie un tuple 2, dont les valeurs sontu
etd
décrites ci-dessus. Si une relation est trouvée par les deux parents, celle avec le plus petit nombre d'étapes générationnelles (u+d
) est préférée. Si la personnea
n'a aucun lien de sang avec la personnei
,A(i)
revientNone
.P(r)
: Affiche la chaîne de résultatr
entre crochets par les noms des personnesa
etb
.O(n)
: Renvoie une chaîne ordinale pour le nombre donnén
. Prise en charge uniquement1 < n < 21
.G(n)
: Renvoie une chaîne de préfixe équivalente àn-1
"grands" (par exemple"2nd great-"
pour n = 2`). Renvoie une chaîne vide pour n <= 1.GG(n)
: Renvoie une chaîne de préfixe avec "Nième grand-" et "grand", selon lesn
générations. Renvoie une chaîne vide pour n <= 1.J'ai pris quelques raccourcis au nom d'un code plus court qui pourrait être révisé pour de meilleures performances (ou légèrement plus correctes) sur de grandes généalogies. La
A
fonction ne fait aucune tentative pour éviter de reproduire les arbres enfants qui ont déjà été recherchés, ce qui la rend plus lente que nécessaire (mais probablement encore assez rapide pour les familles de taille raisonnable). LaO
fonction ne gère pas correctement ordinaux supérieure à 20 (il est un peu difficile à obtenir tous11th
,21st
et à101st
droite, mais dans un de mes brouillons , je l' ai fait en 25 octets supplémentaires). À moins que vous n'ayez affaire à des familles très anciennes et célèbres (par exemple certaines des familles royales d'Europe), je ne suis pas sûr que je ferais confiance à l'exactitude d'une généalogie qui remonte si loin de toute façon.D'un autre côté, j'ai également sauté quelques endroits où je pouvais raser quelques octets. Par exemple, je pouvais économiser 3 octets en renommant
GG
un nom de caractère unique, mais baser le nomgreat-grand
me semblait plus intéressant.Je crois que tous les espaces blancs dans le code sont requis, mais il est possible que certains puissent être ignorés et je les ai juste manqués (j'ai continué à trouver des espaces parasites dans les listes d'arguments pendant que je tapais cette réponse, mais je pense que je '' les ai tous obtenus maintenant).
Parce que ma correspondance récursive nécessite une règle relativement simple pour laquelle les relations préfèrent s'il y en a plusieurs, je ne donne pas exactement les résultats demandés dans certains cas obscurs impliquant un inceste intergénérationnel. Par exemple, si la personne
a
est à la foisb
l'oncle et le grand-père de la personne, mon code préférera la relation de grand-père malgré la question disant que la relation de l'oncle devrait avoir une priorité plus élevée.Voici un exemple de jeu de données qui expose le problème:
Je soupçonne que pour la plupart des programmes, les relations entre Bob et Claire ou entre Bob et Danielle causeront des problèmes. Ils appellent la première paire demi-frères et sœurs plutôt que père / fille ou ils décrivent cette dernière paire comme grand-père / petite-fille plutôt que comme oncle / nièce. Mon code fait ce dernier, et je ne vois aucun moyen raisonnable de le changer pour obtenir les résultats demandés sans se tromper également sur la première paire.
la source
Une suite de tests. Remplissez-le dans t / relation.t et exécutez "prouver" ou "perl t / relation.t". Il suppose actuellement que le fichier programme est "relation.rb".
C'est un wiki communautaire, alors n'hésitez pas à ajouter des tests. Si vous le changez, je pense qu'un horodatage (ou un autre drapeau évident) serait en règle. Liste de souhaits:
la source