• Posts tagged "家谱"

Blog Archives

用prolog推理家族关系

Prolog语言知识推理系列文章,不同编程语言有着不同编程视角,JAVA是面向对象,Nodejs是异步回调,R语言是统计算法,Prolog就是知识推理。每种语言都是独特的,如果想把一门语言学好用好,关键是利用语言的特点,做对的事情。

本系列文章主要介绍Prolog语言,从入门安装到知识推理。Prolog是完全不一样的,他没有复杂的程序结构,也不是为了解决算法问题,而是专注于逻辑推理,擅长解决抽象的问题。

关于作者:

  • 张丹,分析师/程序员/Quant: R,Java,Nodejs
  • blog: http://fens.me
  • email: bsspirit@gmail.com

转载请注明出处:
http://blog.fens.me/prolog-family/

前言

最近在研究逻辑推理的一些问题,无意中发现了一门编程语言prolog就是解决推理问题的。家族关系是社会生活中一个重要的组成元素,每个人都有自己的家族关系网,这种关系结构并不是很容易地表达,而且人物关系一旦多了,会非常地复杂。

试试prolog的在推理上的强大之处,能否帮我们解决复杂的社会问题的推理计算过程。

目录

  1. 家族关系定义
  2. 用prolog表达家族关系
  3. 推理计算

1. 家族关系定义

我们虚拟一个家族关系,共16个人,8男8女,包括几个不同的维度关系,性别,家长,孩子,结婚,兄弟,姐妹等,如下图所示。

上图注释:

  • 性别特征:男为蓝色方形,女为橙色圆形。
  • 家长关系:用蓝色箭头表示,箭头的开始为家长,箭头的指向为孩子。
  • 婚姻关系:用红色连线表示,两端为结婚的男性和女性。

2. 用prolog表达家族关系

我们首先建立一个prolog的文件叫family.pl,在family.pl中定义关系的定义,如果直接在prolog的运行环境中运行代码代码,则会出现DWIM could not correct goal的错误,错误解决请参考文章prolog语言安装

新建文件family.pl。在window中可以直接在任何文本编辑器中,新建一个文件。


~ notepad c:/work/prolog/02 family/family.pl

根据prolog语言的语法结构,我们需要在编程时,分别定义事实(Fact)、规则(Rule)和问题(Question)。

  • 事实,就是把具体的对象进行语言表达和描述。
  • 规则,就是建立对象之间的关系。
  • 问题,就是用prolog来找答案。

事实:建立对象的性别特征,女性和男性。


% female: a,c,i,k,m,e,g,o
% male:   b,d,j,l,n,f,h,p
female(a).
female(c).
female(i).
female(k).
female(m).
female(e).
female(g).
female(o).
male(b).
male(d).
male(j).
male(l).
male(n).
male(f).
male(h).
male(p).

规则:建立家长关系


% parent
parent(a,d).
parent(a,j).
parent(b,d).
parent(c,m).
parent(c,n).
parent(d,m).
parent(d,n).
parent(d,g).
parent(e,g).
parent(e,h).
parent(f,h).
parent(g,o).
parent(i,k).
parent(i,l).
parent(j,k).
parent(j,l).

规则:建立婚姻关系


% married
married(a,b).
married(c,d).
married(i,j).
married(e,f).
married(o,p).

通过上面的代码,我们就让这个家族关系的事实和基本规则建立完成了。

3. 推理计算

接下来,我们就可以进行关系的计算了。我们只需要提出问题,让程序自动实现推理计算的过程。

首先需要打开prolog运行环境,然后加载刚写的family.pl脚本文件。


# 启动 prolog运行环境
~ swipl

# 设置工作路径
?- cd("c:/work/prolog/02 family/").
true.

# 加载脚本
?- [family].
true.

问题1:a是谁的家长


?- parent(a,Who).
Who = d ;
Who = j.

a是我们之前定义好的常量,Who是变量,用Who表达结果。得出a是d和j的家长。

问题2:d是谁的孩子


?- parent(Who,d).
Who = a ;
Who = b.

通过家长的反向规则,可计算出孩子。

我们可以新建一个规则:建立孩子关系,家长关系的反向关系。


child(X,Y) :- parent(Y,X).

重新加载family.pl文件再运行。


?- child(d,Who).
Who = a ;
Who = b.

问题3:g的爸爸是谁?
需要满足2个条件,是g的家长,同时是男性。


?- parent(X,g),male(X).
X = d .

我们可以新建一个规则:建立父子关系和母子关系。


% mother, father
mother(X,Y) :- parent(X,Y),female(X).
father(X,Y) :- parent(X,Y),male(X).

g的爸爸和g的妈妈。


% g的爸爸
?- father(Who,g).
Who = d .

% g的妈妈
?- mother(Who,g).
Who = e.

问题4:e和f结婚了吗?
结婚是一个双方关系,需要计算e和f是否结婚,同时还需要判断f和e是否结婚。


?- married(e,f).
true.

?-  married(f,e).
false.

我们需要建立f和e的新规则:建立双向的婚姻关系。


% 建立双向的婚姻关系
married(X,Y) :-  married(Y,X).

重新加载后,计算f和e的婚姻规则。


?- married(f,e).
true .

问题5:谁是亲姐妹,谁是亲兄弟
谁是亲姐妹,设X和Y是亲姐妹,Z是X和Y的家长,X和Y都是女性,X不是Y。


?- parent(Z,X),parent(Z,Y),female(X),female(Y), \+ X=Y.
Z = d,
X = m,
Y = g ;
Z = d,
X = g,
Y = m ;
false.

计算得出X=m和Y=g是亲姐妹,她们的家长是Z=d。
那么,谁是亲兄弟?设X和Y是亲兄弟,Z是X和Y的家长,X和Y都是男性,X不是Y。


?- parent(Z,X),parent(Z,Y),male(X),male(Y), \+ X=Y.
Z = a,
X = d,
Y = j ;
Z = a,
X = j,
Y = d ;
false.

计算得出X=d和Y=j是亲兄弟,她们的家长是Z=a。

新加新规则:建立兄弟和姐妹的关心。


sister(X,Y) :- parent(Z,X),parent(Z,Y),female(X),female(Y), \+ X=Y.
brother(X,Y) :- parent(Z,X),parent(Z,Y),male(X),male(Y), \+ X=Y.

问题6:祖父母,姥姥姥爷,爷爷奶奶
在英语里祖父母似乎是不分的,都叫grandparent,而在汉语里妈妈的父母是姥姥和姥爷,爸爸的父母是爷爷和奶奶,规则有所不同。

直接新规则:


% grandparent
grandparent(X,Y) :- parent(X,Z),parent(Z,Y).

% yeye,nainai
yeye_nainai(Y,X) :- father(Z,X),parent(Y,Z).

% laolao,laoye
laolao_laoye(Y,X) :- mother(Z,X),parent(Y,Z).

重新加载程序:o的祖父母是谁?


?- grandparent(Y,o).
Y = d ;
Y = e ;
false.

o的姥姥和姥爷是谁?


?- laolao_laoye(Y,o). 
Y = d ;
Y = e.

n的爷爷和奶奶是谁?


?- yeye_nainai(Y,n).
Y = a ;
Y = b.

其实,可以找的关系还有很多,比如表兄妹,姑姑,婶婶,舅舅,姨夫等,同时我们也可以增加其他维度的内容,比如同在一所大学,谁与谁曾经结婚又离婚了,孩子谁在抚养等多种逻辑上复杂的问题。

本文的完整代码,可以在github上找到:https://github.com/bsspirit/prolog-learning/tree/main/02 family

同样,本文还是对于prolog的初探,进行逻辑推理,从浅入入深的过程,希望本文能让所有的prolog新手快速上手。

转载请注明出处:
http://blog.fens.me/prolog-family/

打赏作者