系図データからの家族の関係を計算します
-
21-08-2019 - |
質問
私は、次のデータスキーマ(のみ、直接この問題に適用する列を示す、私の実際のデータスキーマから簡体字)与えられた、家系図に二人の個人間の家族関係を計算できるようにしたいと思います:
individual
----------
id
gender
child
----------
child_id
father_id
mother_id
このような構成により、どのようにして二つの個別のIDの関係(すなわちいとこ、大きなグランド叔父、など。)を算出することができます。
また、2人の関係は(B-Aは、叔父であるのに対し、すなわちA-Bは甥であり得る)実際にあるように、どのように一方が他方に対して相補生成することができる(与えられた叔父を、そしてどのように我々は甥を生成する可能性があり、我々は、性別を知っていると仮定?)。これは些細な問題の詳細です、前者は私が本当に興味ものです。
おかげですべての!
解決 2
以下の関係を計算するために私のアルゴリズムの私のPHPの実装があります。これは、私は元の質問に概説されたデータスキーマに基づいています。これは、そのような半兄弟やいとこの二重などの化合物の関係を解決しない、二人の個人間の「最も近い」、すなわち最短パスの関係を見つけます。
などget_father
やget_gender
としてそのデータ・アクセス機能は、私は常に使用するデータベース抽象化レイヤのスタイルで書かれています。基本的なmysql_query
など、すべてのDBMS固有の機能は、このような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を決定するアルゴリズムが最適よりもはるかに少ないです。私はそれを最適化するために、別の質問を投稿する予定、と別のそのような二重のいとこなどの化合物との関係を計算する問題に対処する。
正しい方向に私を助けをprodすべての人に感謝します!あなたのヒントを使用すると、これは私が当初考えていたよりもはるかに簡単であることが判明します。
他のヒント
あなたが最初href="http://en.wikipedia.org/wiki/Lowest_common_ancestor" rel="noreferrer" title="Lowest共通Ancestor"> 両方の A と B を。この最低共通の祖先 C の
を呼び出し B (CBに次に C から A までの工程で(CA)の距離を計算し、 C )。これらの値は、これら2つの値に基づいて、関係を決定する別のテーブルにインデックス付けされるべきです。たとえばます:
CA CB Relation
1 2 uncle
2 1 nephew
2 2 cousin
0 1 father
0 2 grandfather
あなたは、この表の基本的な関係を維持し、祖父のような特定の関係上、追加の距離を「great-」を追加、例:(0、3)曽祖父を=。
かもしれませんうまくいけば、これは正しい方向にあなたを指します。運のベスト!
の のUPDATE:のの(私はまだ評判を持っていないので、私は、あなたのコードの下にコメントすることはできません。)
あなたの関数aggrandize_relationshipsは少しオフになっていると思います。回 - あなたはオフセット(オフセット1)接頭辞「great-」そして、1以上であれば「壮大」付けることによって、それを簡素化することができます。お使いのバージョンでは、非常に遠い親戚の接頭辞「偉大な壮大な偉大な壮大」を含んでいないかもしれません。(私はこの説明で正しいパラメータを持っていますが、うまくいけば、あなたはそれの要点を得ればわからない。また、あなたの家族の木があることが起こっている場合は考えて遠い背中が、ポイントは有効なままになります。)
の のTOO UPDATE:のの 申し訳ありませんが、上記が正しくありません。私は、デフォルトのケースを読み違え、それが再帰的に再び関数を呼び出したと思いました。私の守備では、私は、「第二の曽祖父」の表記に慣れていなかった、と常に「偉大な偉大な祖父」自分自身を使用していました。コード以降!!
このツリーの関係電卓は、ツリーのXML表現を受け入れ、その中の任意の二つのメンバーの関係を計算するオブジェクトで役立つかもしれません。この記事は関係が計算され、そしてどのような二いとこ、または一旦除去いとこのような用語、意味されている方法を説明します。このコードでは、JavaScriptで記述された関係を計算するためのオブジェクト、だけでなく、レンダリング、ツリーと対話するためのWeb UIを含んでいます。サンプルプロジェクトでは、従来のASPページとして設定されます。
http://www.codeproject.com/Articles/30315/Tree-リレーションシップ・電卓する
私は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.");
}
}
このはあなたを助けるかもしれない、それが生成するSQLクエリの理論と実装の多くだとクエリツリー構造
http://www.artfulsoftware.com/mysqlbook/sampler/mysqled1ch20.html の
具体的には、使用しています隣接リストモデルを見て一例として、家系ます。
のように奇妙なPrologは、あなたが探しているものだそうです。 <以下、アドホックプログラム( http://www.pastey.net/117134 のより良い着色)を考えます/ P>
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.
あなたはいつ、どのようにそれを使用する知っていれば、これは、強力なツールです。これはまさにPrologのための場所のように思えます。私はそれがひどく人気の、または埋め込むことは容易ではありません知っているが、コメントの1に示すwolphramアルファの印象的な特徴は、上記使用した構築物以上のものを使用していないコード化することができ、これはプロローグ101
であります