【java规则引擎】之规则引擎解释

转载:http://www.open-open.com/lib/view/open1417528754230.htmlhtml

现实生活中,规则无处不在。法律、法规和各类制度均是;对于企业级应用来讲,在IT技术领域,不少地方也应用了规则,好比路由表,防火墙策略,乃至角色权限控制(RBAC),或者Web框架中的URL匹配。不论是那种规则,都规定了一组肯定的条件和此条件所产生的结果。

举一个例子:

IF

汽车是红色
车是运动型的
驾驶员是男性
驾驶员在16-25岁之间
THEN

保险费用增长20%

从这个例子能够看出:

每条规则都是一组条件决定的一系列结果
一条规则可能与其余规则共同决定最终结果。好比例子中的规则只产生了增量,还须要与肯定基数的规则共同做用才能决定最终的费率
可能存在条件互相交叉的规则,此时有必要规定规则的优先级
规则做为一种知识,其典型运用就是经过实际状况,根据给定的一组规则,得出结论。这个结论多是某种静态的结果,也多是须要进行的一组操做。这种 规则的运用过程叫作推理。若是由程序来处理推理过程,那么这个程序就叫作推理机/推理引擎。推理引擎根据知识表示的不一样采起的控制策略也是不一样的,常见的 类型包括基于神经网络、基于案例和基于规则的推理机。其中,基于规则的推理机易于理解、易于获取、易于管理,被普遍采用。这种推理引擎被称为“规则引 擎”。


规则引擎起源于基于规则的专家系统(专家系统CLIPS: 源于1984年NASA的人工智能项目,现已开源,由C编写。),而基于规则的专家系统又是专家系统的其中一个分支。专家系统属于人工智能的范畴,它模仿 人类的推理方式,使用试探性的方法进行推理,并使用人类能理解的术语解释和证实它的推理结论。基于规则的专家系统(RBES)包括三部分:Rule Base(knowledge base)、Working Memory(fact base)和Inference Engine。它们的结构以下系统所示:node

推理引擎(Inference Engine)包括三部分:模式匹配器(Pattern Matcher)、议程(Agenda)和执行引擎(Execution Engine)。推理引擎经过决定哪些规则知足事实或目标,并授予规则优先级,知足事实或目标的规则被加入议程。

模式匹配器决定选择执行哪一个规则,什么时候执行规则;
议程管理模式匹配器挑选出来的规则的执行次序;
执行引擎负责执行规则和其余动做。
和人类的思惟相对应,规则引擎中也存在两种推理方式:正向推理(Forward-Chaining)和反向推理(Backward-Chaining)。

正向推理也叫演绎法,由事实驱动,从 一个初始的事实出发,不断地应用规则得出结论。首先在候选队列中选择一条规则做为启用规则进行推理,记录其结论做为下一步推理时的证据。如此重复这个过程,直到再无可用规则可被选用或者求得了所要求的解为止。
反向推理也叫概括法,由目标驱动,首先提出某个假设,而后寻找支持该假设的证据,若所需的证据都能找到,说明原假设是正确的;若不管如何都找不到所须要的证据,则说明原假设不成立,此时须要另作新的假设。
将事实与规则进行匹配的算法。常见的模式匹配算法有RETE,LFA,TREAI,LEAPS。Rete算法是目前效率最高的一个演绎法推理算法,许多规则引擎都是基于Rete算法来进行推理计算的。

推理引擎的推理步骤以下:模式匹配、冲突消解、执行引擎。

将初始数据(fact)输入 Working Memory 。
使用 Pattern Matcher 比较规则库(rule base)中的规则(rule)和数据(fact)。
若是执行规则存在冲突(conflict),即同时激活了多个规则,将冲突的规则放入冲突集合。
解决冲突,将激活的规则按顺序放入Agenda。
使用执行引擎执行Agenda中的规则。重复步骤2至5,直到执行完毕全部Agenda中的规则。
规则引擎的做用:

规则外部化,即有利于规则知识的复用,也可避免改变规则时带来的代码变动问题
由规则引擎使用某种算法进行推理过程,不须要编写复杂晦涩的逻辑判断代码
开发人员的不须要过多关注逻辑判断,能够专一于逻辑处理

RETE算法

Rete在拉丁语中是“net”,有网络的意思。Rete算法由Carnegie Mellon University的Dr Charles L. Forgy设计发明,是一个用来实现产生式规则系统(production/inference)的高效模式匹配算法。

RETE算法能够分为两部分:规则编译(rule compilation)和运行时执行(runtime execution)。

规则编译===>是指根据规则集生成推理网络的过程。

运行时执行===>指将数据送入推理网络进行筛选的过程。

相关概念:

事实(Fact):对象之间及对象属性之间的关系
规则(rule):是由条件和结论构成的推理语句,通常表示为if…Then。一个规则的if部分称为LHS(left-hand-side),then部分称为RHS(right hand side)。
模式(module):就是指IF语句的条件。这里IF条件多是有几个更小的条件组成的大条件。模式就是指的不能在继续分割下去的最小的原子条件。
RETE推理网络的生成过程:从规则集{规则1,规则2……..}中拿出一条来,根据必定算法,变成RETE推理网络的节点。不断循环将全部规则都处理完,RETE推理网络就生成了。RETE网络主要分为两个部分,alpha网络和beta网络。以下图所示。算法

 

alpha网络:过滤working memory,找出符合规则中每个模式的集合,生成alpha memory(知足该模式的集合)。有两种类型的节点,过滤type的节点和其余条件过滤的节点(我以为这两种是依照须要设定的,也并不必定须要两种节点)。
Beta网络:有两种类型的节点Beta Memory和Join Node。前者主要存储Join完成后的集合。后者包含两个输入口,分别输入须要匹配的两个集合,由Join节点作合并工做传输给下一个节点。
在一个产生式系统中,主要流程能够分为如下步骤:

Match:找出符合LHS部分的working memory集合
Confilict resolution:选出一个条件被知足的规则
Act:执行RHS的内容
返回1
RETE算法主要改进Match的处理过程,经过构建一个网络进行匹配。



具体过程以下:

建立root节点(根节点),推理网络的入口。
拿到规则1,从规则1中取出模式1(前面说了,模式就是最小的原子条件,因此规则模式的关系是1:n)。
a)   检查模式1中的参数类型,若是是新类型,添加一个类型节点。

b)   检查模式1对应的Alpha节点是否存在,若是存在记录下节点的位置;若是没有,将模式1做为一个Alpha节点加入到网络中。同时根据Alpha节点创建Alpah内存表。

c)   重复b,直处处理完全部模式。

d)   组合Beta节点:Beta(2)左输入节点为Alpha(1),右输入节点为Alpha(2);Beta(i)左输入节点是Beta(i-1),右输入节点为Alpha(i),并将两个父节点的内存表内联成为本身的内存表

e)   重复d,直到全部Beta节点处理完毕

f)   将动做Then部分封装成最后节点作为Beta(n)

重复2,直到全部规则处理完毕
下面是一个从网上找得例子:


规则P1:

LHS:

C1:(年纪:研2)
C2:(性别:男)
C3:(身材:较瘦)
C4:(身高:大于175cm)
RHS:

标点符数组

Rete算法优于传统的模式匹配算法的特色

状态保存。事实集合中的每次变化,其匹配后的状态都被保存再alpha和beta节点中。在下一次事实集合发生变化时,绝大多数的结果都不须要变 化,rete算法经过保存操做过程当中的状态,避免了大量的重复计算。Rete算法主要是为那些事实集合变化不大的系统设计的,当每次事实集合的变化很是剧 烈时,rete的状态保存算法效果并不理想。
节点共享


Drools中用到的RETE算法

编译算法描述了规则如何在Production Memory中产生一个有效的辨别网络。用一个非技术性的词来讲,一个辨别网络就是用来过滤数据。方法是经过数据在网络中的传播来过滤数据。在顶端节点将 会有不少匹配的数据。当咱们顺着网络向下走,匹配的数据将会愈来愈少。在网络的最底部是终端节点(terminal nodes)。在Dr Forgy的1982年的论文中,他描述了4种基本节点:root, 1-input, 2-input and terminal。

下图是Drools中的RETE节点类型:网络

 

根节点(RootNode)是全部的对象进入网络的入口。而后,从根节点当即进入到ObjectTypeNode。ObjectTypeNode的 做用是使引擎只作它须要作的事情。例如,咱们有两个对象集:Account和Order。若是规则引擎须要对每一个对象都进行一个周期的评估,那会浪费不少 的时间。为了提升效率,引擎将只让匹配object type的对象经过到达节点。经过这种方法,若是一个应用assert一个新的account,它不会将Order对象传递到节点中。不少现代RETE实 现都有专门的ObjectTypeNode。在一些状况下,ObjectTypeNode被用散列法进一步优化。数据结构

ObjectTypeNode可以传播到AlphaNodes,LeftInputAdapterNodes和BetaNodes。

1-input节点一般被称为AlphaNode。AlphaNodes被用来评估字面条件(literal conditions)。虽然,1982年的论文只提到了相等条件(指的字面上相等),不少RETE实现支持其余的操做。例如,Account.name == “Mr Trout”是一个字面条件。当一条规则对于一种object type有多条的字面条件,这些字面条件将被连接在一块儿。这是说,若是一个应用assert一个account对象,在它能到达下一个 AlphaNode 以前,它必须先知足第一个字面条件。在Dr. Forgy的论文中,他用IntraElement conditions来表述。下面的图说明了Cheese的AlphaNode组合(name == “cheddar”,strength == “strong”):框架

Drools经过散列法优化了从ObjectTypeNode到AlphaNode的传播。每次一个AlphaNode被加到一个 ObjectTypeNode的时候,就以字面值(literal value)做为key,以AlphaNode做为value加入HashMap。当一个新的实例进入ObjectTypeNode的时候,不用传递到每 一个AlphaNode,它能够直接从HashMap中得到正确的AlphaNode,避免了没必要要的字面检查。



2-input节点一般被称为BetaNode。Drools中有两种BetaNode:JoinNode和NotNode。BetaNodes被用来对2个对象进行对比。这两个对象能够是同种类型,也能够是不一样类型。



咱们约定BetaNodes的2个输入称为左边(left)和右边(right)。一个BetaNode的左边输入一般是a list of objects。在Drools中,这是一个数组。右边输入是a single object。两个NotNode能够完成‘exists’检查。Drools经过将索引应用在BetaNodes上扩展了RETE算法。下图展现了一个 JoinNode的使用:ide

 

注意到图中的左边输入用到了一个LeftInputAdapterNode,这个节点的做用是将一个single Object转化为一个单对象数组(single Object Tuple),传播到JoinNode节点。由于咱们上面提到过左边输入一般是a list of objects。

Terminal nodes被用来代表一条规则已经匹配了它的全部条件(conditions)。 在这点,咱们说这条规则有了一个彻底匹配(full match)。在一些状况下,一条带有“或”条件的规则能够有超过一个的terminal node。

Drools经过节点的共享来提升规则引擎的性能。由于不少的规则可能存在部分相同的模式,节点的共享容许咱们对内存中的节点数量进行压缩,以提供遍历节点的过程。下面的两个规则就共享了部分节点:性能

 1 rule
 2     when
 3         Cheese( $chedddar : name  ==  " cheddar "  )
 4         $person : Person( favouriteCheese  ==  $cheddar )
 5     then
 6         System.out.println( $person.getName()  +   "  likes cheddar "  );
 7 end
 8 
 9 rule
10     when
11         Cheese( $chedddar : name  ==   " cheddar "  )
12         $person : Person( favouriteCheese  !=  $cheddar )
13     then
14         System.out.println( $person.getName()  +   "  does likes cheddar "  );
15 end
View Code

从图上能够看到,编译后的RETE网络中,AlphaNode是共享的,而BetaNode不是共享的。上面说的相等和不相等就体如今BetaNode的不一样。而后这两条规则有各自的Terminal Node。


RETE算法的第二个部分是运行时(runtime)。当一个应用assert一个对象,引擎将数据传递到root
node。从那里,它进入ObjectTypeNode并沿着网络向下传播。当数据匹配一个节点的条件,节点就将它记录到相应的内存中。这样作的缘由有以
 
下几点:主要的缘由是能够带来更快的性能。虽然记住彻底或部分匹配的对象须要内存,它提供了速度和可伸缩性的特色。当一条规则的全部条件都知足,这就是完
 
全匹配。而只有部分条件知足,就是部分匹配。(我以为引擎在每一个节点都有其对应的内存来储存知足该节点条件的对象,这就形成了若是一个对象是彻底匹配,那
 这个对象就会在每一个节点的对应内存中都存有其映象。)测试

参考连接:

引用地址:http://www.biaodianfu.com/rules-engine.html

 

另外一篇带有案例的博客:http://hhw3.blog.163.com/blog/static/2690966201301065929233/

Java规则引擎对提交给引擎的Java数据对象进行检索,根据这些对象的当前属性值和它们之间的关系,从加载到引擎的规则集中发现符合条件的规则,建立 这些规则的执行实例。这些实例将在引擎接到执行指令时、依照某种优先序依次执行。通常来说,Java规则引擎内部由下面几个部分构成:工做内存 (Working Memory)即工做区,用于存放被引擎引用的数据对象集合;规则执行队列,用于存放被激活的规则执行实例;静态规则区,用于存放全部被加载的业务规则, 这些规则将按照某种数据结构组织,当工做区中的数据发生改变后,引擎须要迅速根据工做区中的对象现状,调整规则执行队列中的规则执行实例。Java规则引 擎的结构示意图如图4所示。

当引擎执行时,会根据规则执行队列中的优先顺序逐条执行规则执行实例,因为规则的执行部分可能会改变工做区的数据对象,从而会使队列中的某些规则执行实例 由于条件改变而失效,必须从队列中撤销,也可能会激活原来不知足条件的规则,生成新的规则执行实例进入队列。因而就产生了一种“动态”的规则执行链,造成 规则的推理机制。这种规则的“链式”反应彻底是由工做区中的数据驱动的。    任何一个规则引擎都须要很好地解决规则的推理机制和规则条件匹配的效率问题。规则条件匹配的效率决定了引擎的性能,引擎须要迅速测试工做区中的数据对 象,从加载的规则集中发现符合条件的规则,生成规则执行实例。1982年美国卡耐基·梅隆大学的Charles L. Forgy发明了一种叫Rete算法,很好地解决了这方面的问题。目前世界顶尖的商用业务规则引擎产品基本上都使用Rete算法。

相关文章
相关标签/搜索