代码之家  ›  专栏  ›  技术社区  ›  defines

根据系谱数据计算家庭关系

  •  17
  • defines  · 技术社区  · 15 年前

    我希望能够计算家庭树中两个人之间的家庭关系,给定以下数据模式(从我的实际数据模式简化,仅显示直接适用于此问题的列):

    individual
    ----------
    id
    gender
    
    child
    ----------
    child_id
    father_id
    mother_id
    

    有了这个结构,如何才能计算出两个身份证之间的关系(即堂兄、大叔等)。

    此外,由于实际上有两种关系(即A-B可能是侄子,而B-A是叔叔),一种关系如何产生对另一种关系的补充(假定我们知道性别,我们如何产生侄子?)这是一个更为琐碎的问题,前者是我真正感兴趣的问题。

    谢谢大家!

    6 回复  |  直到 15 年前
        1
  •  7
  •   taserian    15 年前

    您首先需要计算 Lowest Common Ancestor 两者兼而有之 . 称之为最低共同祖先 C .

    然后以步为单位计算距离 C (CA)和 C (断路器)。这些值应该索引到另一个表中,该表根据这两个值确定关系。例如:

    CA      CB      Relation
    1       2       uncle
    2       1       nephew
    2       2       cousin
    0       1       father
    0       2       grandfather
    

    您可以将基本关系保留在这个表中,并为某些关系添加“great-”,如祖父,例如:(0,3)=great祖父。

    希望这能为你指明正确的方向。祝你好运!

    更新: (我不能在你的代码下面发表评论,因为我还没有声誉。)

    我想,你扩大人际关系的功能有点不正常。如果偏移量为1或更大,可以通过在“grand”前加前缀来简化它,然后在“great-”(偏移量-1)前加前缀。你的版本可能包括前缀“伟大的伟大伟大伟大”为非常遥远的亲戚。(不确定我是否有正确的参数在这个解释,但希望你得到它的要点。另外,不知道你的家谱是否会走那么远,但这一点仍然有效。)

    更新: 抱歉,上述内容不正确。我误解了默认情况,认为它再次递归地调用了函数。在我看来,我不熟悉“第二位曾祖父”的标记法,我自己也一直使用“曾祖父”。代码前进!!

        2
  •  6
  •   defines    15 年前

    下面是我的算法的PHP实现来计算关系。这是基于我在原始问题中概述的数据模式。这只会发现两个人之间的“最近”关系,即最短路径关系,它不会解决半个兄弟姐妹或双表兄弟姐妹等复合关系。

    请注意,数据访问功能如 get_father get_gender 是以我经常使用的数据库抽象层的样式编写的。应该很容易理解正在发生的事情,基本上所有DBMS特定的函数,例如 mysql_query 被诸如 db_query ;它一点也不复杂,特别是在本代码的示例中,但是如果不清楚,可以在注释中发布问题。

    <?php
    /* Calculate relationship "a is the ___ of b" */
    
    define("GENDER_MALE", 1);
    define("GENDER_FEMALE", 2);
    
    function calculate_relationship($a_id, $b_id)
    {
      if ($a_id == $b_id) {
        return 'self';
      }
    
      $lca = lowest_common_ancestor($a_id, $b_id);
      if (!$lca) {
        return false;
      }
      $a_dist = $lca[1];
      $b_dist = $lca[2];
    
      $a_gen = get_gender($a_id);
    
      // DIRECT DESCENDANT - PARENT
      if ($a_dist == 0) {
        $rel = $a_gen == GENDER_MALE ? 'father' : 'mother';
        return aggrandize_relationship($rel, $b_dist);
      }
      // DIRECT DESCENDANT - CHILD
      if ($b_dist == 0) {
        $rel = $a_gen == GENDER_MALE ? 'son' : 'daughter';
        return aggrandize_relationship($rel, $a_dist);
      }
    
      // EQUAL DISTANCE - SIBLINGS / PERFECT COUSINS
      if ($a_dist == $b_dist) {
        switch ($a_dist) {
          case 1:
            return $a_gen == GENDER_MALE ? 'brother' : 'sister';
            break;
          case 2:
            return 'cousin';
            break;
          default:
            return ordinal_suffix($a_dist - 2).' cousin';
        }
      }
    
      // AUNT / UNCLE
      if ($a_dist == 1) {
        $rel = $a_gen == GENDER_MALE ? 'uncle' : 'aunt';
        return aggrandize_relationship($rel, $b_dist, 1);
      }
      // NEPHEW / NIECE
      if ($b_dist == 1) {
        $rel = $a_gen == GENDER_MALE ? 'nephew' : 'niece';
        return aggrandize_relationship($rel, $a_dist, 1);
      }
    
      // COUSINS, GENERATIONALLY REMOVED
      $cous_ord = min($a_dist, $b_dist) - 1;
      $cous_gen = abs($a_dist - $b_dist);
      return ordinal_suffix($cous_ord).' cousin '.format_plural($cous_gen, 'time', 'times').' removed';
    } //END function calculate_relationship
    
    function aggrandize_relationship($rel, $dist, $offset = 0) {
      $dist -= $offset;
      switch ($dist) {
        case 1:
          return $rel;
          break;
        case 2:
          return 'grand'.$rel;
          break;
        case 3:
          return 'great grand'.$rel;
          break;
        default:
          return ordinal_suffix($dist - 2).' great grand'.$rel;
      }
    } //END function aggrandize_relationship
    
    function lowest_common_ancestor($a_id, $b_id)
    {
      $common_ancestors = common_ancestors($a_id, $b_id);
    
      $least_distance = -1;
      $ld_index = -1;
    
      foreach ($common_ancestors as $i => $c_anc) {
        $distance = $c_anc[1] + $c_anc[2];
        if ($least_distance < 0 || $least_distance > $distance) {
          $least_distance = $distance;
          $ld_index = $i;
        }
      }
    
      return $ld_index >= 0 ? $common_ancestors[$ld_index] : false;
    } //END function lowest_common_ancestor
    
    function common_ancestors($a_id, $b_id) {
      $common_ancestors = array();
    
      $a_ancestors = get_ancestors($a_id);
      $b_ancestors = get_ancestors($b_id);
    
      foreach ($a_ancestors as $a_anc) {
        foreach ($b_ancestors as $b_anc) {
          if ($a_anc[0] == $b_anc[0]) {
            $common_ancestors[] = array($a_anc[0], $a_anc[1], $b_anc[1]);
            break 1;
          }
        }
      }
    
      return $common_ancestors;
    } //END function common_ancestors
    
    function get_ancestors($id, $dist = 0)
    {
      $ancestors = array();
    
      // SELF
      $ancestors[] = array($id, $dist);
    
      // PARENTS
      $parents = get_parents($id);
      foreach ($parents as $par) {
        if ($par != 0) {
          $par_ancestors = get_ancestors($par, $dist + 1);
          foreach ($par_ancestors as $par_anc) {
            $ancestors[] = $par_anc;
          }
        }
      }
    
      return $ancestors;
    } //END function get_ancestors
    
    function get_parents($id)
    {
      return array(get_father($id), get_mother($id));
    } //END function get_parents
    
    function get_father($id)
    {
      $res = db_result(db_query("SELECT father_id FROM child WHERE child_id = %s", $id));
      return $res ? $res : 0;
    } //END function get_father
    
    function get_mother($id)
    {
      $res = db_result(db_query("SELECT mother_id FROM child WHERE child_id = %s", $id));
      return $res ? $res : 0;
    } //END function get_mother
    
    function get_gender($id)
    {
      return intval(db_result(db_query("SELECT gender FROM individual WHERE id = %s", $id)));
    }
    
    function ordinal_suffix($number, $super = false)
    {
      if ($number % 100 > 10 && $number %100 < 14) {
        $os = 'th';
      } else if ($number == 0) {
        $os = '';
      } else {
        $last = substr($number, -1, 1);
    
        switch($last) {
          case "1":
            $os = 'st';
            break;
          case "2":
            $os = 'nd';
            break;
          case "3":
            $os = 'rd';
            break;
          default:
            $os = 'th';
        }
      }
    
      $os = $super ? '<sup>'.$os.'</sup>' : $os;
    
      return $number.$os;
    } //END function ordinal_suffix
    
    function format_plural($count, $singular, $plural)
    {
      return $count.' '.($count == 1 || $count == -1 ? $singular : $plural);
    } //END function plural_format
    
    ?>
    

    正如我之前提到的,确定LCA的算法远不如最优算法。我计划发布一个单独的问题来优化这个问题,另一个问题是解决计算复合关系的问题,比如双表兄妹。

    非常感谢所有帮助我朝正确方向前进的人!有了你的提示,这比我原来想象的要容易得多。

        3
  •  2
  •   Ramy Shosha    9 年前

    这可能有助于树关系计算器是一个对象,它接受树的XML表示,并将计算其中任意两个成员的关系。本文描述了如何计算关系,以及像“二表亲”或“一表亲”这样的术语一旦被删除意味着什么。此代码包括一个用JavaScript编写的用于计算关系的对象,以及一个用于呈现和与树交互的Web UI。示例项目设置为经典的ASP页。

    http://www.codeproject.com/Articles/30315/Tree-Relationship-Calculator

        4
  •  2
  •   AdZzZ    7 年前

    我用Java中的邻接表概念解决了这个问题。 一个人可以为每个人都有一个节点,并且在它的节点上关联它的子关系。 下面是仅查找兄弟姐妹和堂兄弟姐妹的代码。但是,您可以根据您的需求进行增强。我编写这个代码只是为了演示。

    public class Person {
        String name;
        String gender;
        int age;
        int salary;
        String fatherName;
        String motherName;
    
        public Person(String name, String gender, int age, int salary, String fatherName,
                String motherName) {
            super();
            this.name = name;
            this.gender = gender;
            this.age = age;
            this.salary = salary;
            this.fatherName = fatherName;
            this.motherName = motherName;
        }
    
    }
    

    下面是添加家庭成员和查找他们之间关系的主要代码。

    import java.util.LinkedList;
    
    public class PeopleAndRelationAdjacencyList {
        private static String MALE = "male";
        private static String FEMALE = "female";
    
    public static void main(String[] args) {
        int size = 25;
        LinkedList<Person> adjListArray[] = new LinkedList[size];
        for (int i = 0; i < size; i++) {
            adjListArray[i] = new LinkedList<>();
        }
    
        addPerson( adjListArray, "GGM1", MALE, null, null );
        addPerson( adjListArray, "GGF1", FEMALE, null, null );
    
        addPerson( adjListArray, "GM1", MALE, "GGM1", "GGF1" );
        addPerson( adjListArray, "GM2", MALE, "GGM1", "GGF1" );
    
        addPerson( adjListArray, "GM1W", FEMALE, null, null );
        addPerson( adjListArray, "GM2W", FEMALE, null, null );
    
        addPerson( adjListArray, "PM1", MALE, "GM1", "GM1W" );
        addPerson( adjListArray, "PM2", MALE, "GM1", "GM1W" );
        addPerson( adjListArray, "PM3", MALE, "GM2", "GM2W" );
    
        addPerson( adjListArray, "PM1W", FEMALE, null, null );
        addPerson( adjListArray, "PM2W", FEMALE, null, null );
        addPerson( adjListArray, "PM3W", FEMALE, null, null );
    
        addPerson( adjListArray, "S1", MALE, "PM1", "PM1W" );
        addPerson( adjListArray, "S2", MALE, "PM2", "PM2W" );
        addPerson( adjListArray, "S3", MALE, "PM3", "PM3W" );
        addPerson( adjListArray, "S4", MALE, "PM3", "PM3W" );
    
        printGraph(adjListArray);
        System.out.println("Done !");
    
    
        getRelationBetweenPeopleForGivenNames(adjListArray, "S3", "S4");
        getRelationBetweenPeopleForGivenNames(adjListArray, "S1", "S2");
    
    }
    
    
    private static void getRelationBetweenPeopleForGivenNames(LinkedList<Person>[] adjListArray, String name1, String name2) {
    
        if ( adjListArray[getIndexOfGivenNameInHeadPositionOfList(adjListArray, name1)].peekFirst().fatherName
                .equalsIgnoreCase(
                        adjListArray[getIndexOfGivenNameInHeadPositionOfList(adjListArray, name2)].peekFirst().fatherName) ) {
            System.out.println("SIBLIGS");
            return;
        }
    
        String name1FatherName = adjListArray[getIndexOfGivenNameInHeadPositionOfList(adjListArray, name1)].peekFirst().fatherName;
        String name2FatherName = adjListArray[getIndexOfGivenNameInHeadPositionOfList(adjListArray, name2)].peekFirst().fatherName;
    
        if ( adjListArray[getIndexOfGivenNameInHeadPositionOfList(adjListArray, name1FatherName)].peekFirst().fatherName
                .equalsIgnoreCase(
                        adjListArray[getIndexOfGivenNameInHeadPositionOfList(adjListArray, name2FatherName)].peekFirst().fatherName) ) {
            System.out.println("COUSINS");
        }
    }
    
    
    
    private static void addPerson(LinkedList<Person>[] adjListArray, String name, String gender, String fatherName, String motherName) {
        Person person = new Person(name, gender, 0, 0, fatherName, motherName);
        int indexToPutperson = getEmptyIndexInAdjListToInserterson(adjListArray);
        adjListArray[indexToPutperson].addLast(person);
        if( fatherName!=null ){
            int indexOffatherName = getIndexOfGivenNameInHeadPositionOfList( adjListArray, fatherName);
            adjListArray[indexOffatherName].addLast(person);
        }
        if( motherName!=null ){
            int indexOfMotherName = getIndexOfGivenNameInHeadPositionOfList( adjListArray, motherName);
            adjListArray[indexOfMotherName].addLast(person);
        }
    }
    
    private static int getIndexOfGivenNameInHeadPositionOfList( LinkedList<Person>[] adjListArray, String nameToBeSearched ) {
        for (int i = 0; i < adjListArray.length; i++) {
            if( adjListArray[i] != null ){
                if(adjListArray[i].peekFirst() != null){
                    if(adjListArray[i].peekFirst().name.equalsIgnoreCase(nameToBeSearched)){
                        return i;
                    }
                }
            }
        }
        // handle if father name is not found
        return 0;
    }
    
    
    private static void printGraph(LinkedList<Person>[] adjListArray) {
        for (int v = 0; v < 15; v++) {
            System.out.print("head");
    
            LinkedList<Person> innerLinkedList = adjListArray[v];
            for (int i = 0; i < innerLinkedList.size(); i++) {
                Person person = innerLinkedList.get(i);
                System.out.print(" -> " + person.name);
            }
    
            System.out.println("\n");
        }
    }
    
    private static int getEmptyIndexInAdjListToInserterson( LinkedList<Person>[] adjListArray) {
        for (int i = 0; i < adjListArray.length; i++) {
            if(adjListArray[i].isEmpty()){
                return i;
            }
        }
        throw new IndexOutOfBoundsException("List of relation is full.");
    }
    

    }

        5
  •  0
  •   Charles Ma    15 年前

    这可能有助于您,生成和查询树结构需要大量的SQL查询理论和实现。

    http://www.artfulsoftware.com/mysqlbook/sampler/mysqled1ch20.html

    特别是,看看 adjacency list model 以家族树为例。

        6
  •  0
  •   Wojciech Bederski    15 年前

    听起来很奇怪,Prolog似乎就是你要找的东西。 提供以下特别计划( http://www.pastey.net/117134 更好的着色)

    female(alice).
    female(eve).
    female(kate).
    
    male(bob).
    male(carlos).
    male(dave).
    
    % mother(_mother, _child).
    mother(alice, bob).
    mother(kate, alice).
    
    % father(_father, _child)
    father(carlos, bob).
    
    child(C, P) :- father(P, C).
    child(C, P) :- mother(P, C).
    
    parent(X, Y) :- mother(X, Y).
    parent(X, Y) :- father(X, Y).
    
    sister(alice, eve).
    sister(eve, alice).
    sister(alice, dave).
    
    brother(dave, alice).
    
    % brother(sibling, sibling)
    sibling(X, Y) :- brother(X, Y).
    sibling(X, Y) :- sister(X, Y).
    
    
    uncle(U, C) :- sibling(U, PARENT),
        child(C, PARENT),
        male(U).
    
    
    relationship(U, C, uncle) :- uncle(U, C).
    relationship(P, C, parent) :- parent(P, C).
    relationship(B, S, brother) :- brother(B, S).
    relationship(G, C, grandparent) :- parent(P, C), parent(G, P).
    
    

    您可以向Prolog解释器询问类似的问题:

    relationship(P1, P2, R).

    答案如下:

    
    P1 = dave, P2 = bob, R = uncle ;
    P1 = alice, P2 = bob, R = parent ;
    P1 = kate, P2 = alice, R = parent ;
    P1 = carlos, P2 = bob, R = parent ;
    P1 = dave, P2 = alice, R = brother ;
    P1 = kate, P2 = bob, R = grandparent ;
    false.
    

    如果你知道如何以及何时使用它,它是一个强大的工具。这似乎正是一个普罗罗格的地方。我知道它不太流行,也不容易嵌入,但是其中一条注释中显示的Wolphram Alpha令人印象深刻的特性只能用上面使用的构造进行编码,这是Prolog 101。