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

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

举一个例子:

IF

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

保险费用增加20%

从这个例子可以看出:

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

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

推理引擎(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的使用:

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

Terminal nodes被用来表明一条规则已经匹配了它的所有条件(conditions)。 在这点,我们说这条规则有了一个完全匹配(full match)。在一些情况下,一条带有“或”条件的规则可以有超过一个的terminal node。

Drools通过节点的共享来提高规则引擎的性能。因为很多的规则可能存在部分相同的模式,节点的共享允许我们对内存中的节点数量进行压缩,以提供遍历节点的过程。下面的两个规则就共享了部分节点:

 rule
when
Cheese( $chedddar : name == " cheddar " )
$person : Person( favouriteCheese == $cheddar )
then
System.out.println( $person.getName() + " likes cheddar " );
end rule
when
Cheese( $chedddar : name == " cheddar " )
$person : Person( favouriteCheese != $cheddar )
then
System.out.println( $person.getName() + " does likes cheddar " );
end

从图上可以看到,编译后的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算法。

【java规则引擎】之规则引擎解释的更多相关文章

  1. 【java虚拟机系列】从java虚拟机字节码执行引擎的执行过程来彻底理解java的多态性

    我们知道面向对象语言的三大特点之一就是多态性,而java作为一种面向对象的语言,自然也满足多态性,我们也知道java中的多态包括重载与重写,我们也知道在C++中动态多态是通过虚函数来实现的,而虚函数是 ...

  2. java虚拟机字节码执行引擎

    定义 java虚拟机字节码执行引擎是jvm最核心的组成部分之一,它做的事情很简单:输入的是字节码文件,处理过程是字节码解析的等效过程,输出的是执行结果.在不同的虚拟机实现里,执行引擎在执行java代码 ...

  3. Java 8 的 Nashorn 脚本引擎教程

    本文为了解所有关于 Nashorn JavaScript 引擎易于理解的代码例子. Nashorn JavaScript 引擎是Java SE 8的一部分,它与其它像Google V8 (它是Goog ...

  4. Atitit.java expression fsm 表达式词法分析引擎 v2 qaa.docx

    Atitit.java expression fsm 表达式词法分析引擎 v2 qaa.docx C:\0workspace\AtiPlatf_cms\src\com\attilax\fsm\Java ...

  5. StyleCop中有一些官方自己写好的检测规则下面就是英文的解释

    在StyleCop中有一些官方自己写好的检测规则下面就是英文的解释 文档规则 1.SA1600:ElementsMustBeDocumented元素必须添加注释 2.SA1601: PartialEl ...

  6. Java标识符的命名规则

    Java语言中,为各种变量.方法和类等起的名字称为标识符Java标识符的命名规则:应以字母.下划线.美元符开头后跟字母.下划线.美元符或数字Java标识符大小写敏感,长度无限制 1.java中能用作标 ...

  7. Java包名命名规则

    1.  sun公司建议java包的命名规则为域名的倒写. 比如:sun公司www.sun.com 就用该是com.sun.www 2. indi : 个体项目,指个人发起,但非自己独自完成的项目,可公 ...

  8. 基于Java的开源3D游戏引擎jMonkeyEngine

    jMonkeyEngine简介 jMonkeyEngine是一款纯Java语言编写的游戏引擎,继承了Java应用跨平台的特性,而且是开放源代码的,遵循BSD开源协议,BSD开源协议用一句简单的话概括就 ...

  9. 【Java】标识符 & 命名规则

    Java的标识符和命名规则 什么是标识符[Identifier]? 指用来标识某个实体的一个符号.在不同的应用环境下有不同的含义. 在编程语言中,标识符是开发者编程时使用的名字,对于变量.常量.函数. ...

  10. java标识符与命名规则

    标识符就是给变量.类或方法起的名字.可以用字母.下划线或美元符号开头,区分大小写,没有最大长度限制.(关键字除外) 关键字   访问控制 private protected public       ...

随机推荐

  1. 每天一个linux命令(6/18):lsof命令

    lsof(list open files)是一个列出当前系统打开文件的工具.在linux环境下,任何事物都以文件的形式存在,通过文件不仅仅可以访问常规数据,还可以访问网络连接和硬件.所以,lsof的功 ...

  2. uCOS-II的学习笔记(共九期)和例子(共六个)

    源:uCOS-II的学习笔记(共九期)和例子(共六个) 第一篇 :学习UCOS前的准备工作http://blog.sina.com.cn/s/blog_98ee3a930100w0eu.html 第二 ...

  3. tomcat8配置tomcat-users.xml不生效

    一般想进入tomcat管理后台,只要在tomcat-users.xml配置文件中添加一下内容即可 <role rolename="manager-gui"/> < ...

  4. Go reflect反射

    Go语言中的反射非常强大,可以对string, int, struct, func...进行反射,使用起来也比较简单. 示例1:反射函数 package main import ( "fmt ...

  5. Spring MVC 接收多个实体参数

    在SpringMVC 的接收参数中,如果接收一个实体对象,只需要在方法参数中这样做:@RequestBody User user //单个的时候这样接收 @RequestMapping(value = ...

  6. gitLab 傻瓜式使用教程

    第一步,先去gitLab网上注册一下gitLab 进网站注册出来是这个界面: 2016082993103QQ20160829-1.png 然后进行人机验证(这个没啥困难的0.0) 2016082998 ...

  7. sqlserver ,镜像数据库,CDC,实时监控数据变化

    1.数据库镜像配置 1)主机环境:计算机名称修改SQL1.xiaoping.com 添加用户sqluser 密码永不变,率属于administrators sqlserver安装时,将所有sqlser ...

  8. c#.NET中日志信息写入Windows日志中解决方案

    1. 目的应用系统的开发和维护离不开日志系统,选择一个功能强大的日志系统解决方案是应用系统开发过程中很重要的一部分.在.net环境下的日志系统解决方案有许多种,log4net是其中的佼佼者.在Wind ...

  9. GreenDao 多表事务操作

    场景:Android APP多表操作事务管理 使用Android自带的sql操作类操作的时候需要手动处理事务,使用GreenDao的时候不用管了,啥都处理好了.但是,如果是多表操作的话,怎么统一管理事 ...

  10. Liberty glance 新功能 healthcheck

    oslo.middleware‘s healthcheck http://specs.openstack.org/openstack/oslo-specs/specs/kilo/oslo-middle ...