【java規則引擎】之Drools之Rete演算法

Love Lenka發表於2017-01-03

一:規則引擎
--->規則引擎的核心是Pattern Matcher(模式匹配器)。不管是正向推理還是反向推理,首先要解決一個模式匹配的問題。

--->對於規則的模式匹配,可以定義為: 一個規則是一組模式的集合。如果事實/假設的狀態符合該規則的所有模式,則稱為該規則是可滿足的。 模式匹配的任務就是將事實/假設的狀態與規則庫中的規則一一匹配,找到所有可滿足的規則。



二:什麼是模式匹配

對於模式匹配我們都應該不陌生,我們經常使用的正規表示式就是一種模式匹配。

正規表示式是一種“模式(pattern)”,
程式語言提供的“正規表示式引擎”就是Pattern Matcher。比如python中的re模組。
首先輸入“知識”:re.compile(r'string'),
然後就可以讓其匹配(match)事實(字串)。
最後通過正規表示式引擎可以得到匹配後的結果。
對於規則匹配,通常定義如下:

條件部分,也稱為LHS(left-hand side)
事實部分,也稱為RHS(right-hand side)
假設系統中有N條規則,平均每個規則的條件部分有P個模式,在某個時點有M個事實需要處理。則規則匹配要做的事情就是: 對每一個規則r,判斷當前的事實o是否滿足LHS(r)=True,如果滿足,則將規則r的例項r(o),即規則+滿足該規則的事實,加到衝突集中等待處 理。 通常採取如下過程:

從N條規則中取出一條r;
從M個事實中取出P個事實的一個組合c;
用c測試LHS(r),如果LHS(r(c))=True,將RHS(r(c))加入佇列中;
如果M個事實還存在其他的組合c,goto 3;
取出下一條規則r,goto 2;
實際的問題可能更復雜,在規則的執行過程中可能會改變RHS的資料,從而使得已經匹配的規則例項失效或者產生新的滿足規則的匹配,形成一種“動態”的匹配鏈。



三:模式匹配演算法

上面的處理由於涉及到組合,過程很複雜。有必要通過特定的演算法優化匹配的效率。目前常見的模式匹配演算法包括Rete、Treat、Leaps,HAL,Matchbox等。





四:Rete演算法

Rete演算法是目前使用最廣泛的規則匹配演算法,由Charles L. Forgy博士在1979年發明。Rete演算法是一種快速的Forward-Chaining推理演算法,其匹配速度與規則的數量無關。 Rete的高效率主要來自兩個重要的假設:

時間冗餘性。 facts在推理過程中的變化是緩慢的, 即在每個執行週期中,只有少數的facts發生變化,因此影響到的規則也只佔很小的比例。所以可以只考慮每個執行週期中已經匹配的facts.
結構相似性。許多規則常常包含類似的模式和模式組。
Rete演算法的基本思想是儲存過去匹配過程中留下的全部資訊,以空間代價來換取執行效率 。對每一個模式 ,附加一個匹配元素表來記錄WorkingMemory中所有能與之匹配的元素。當一個新元素加入到WorkingMemory時, 找出所有能與之匹配的模式, 並將該元素加入到匹配元素表中; 當一個無素從WorkingMemory中刪除時,同樣找出所有與該元素匹配的模式,並將元素從匹配元素表中刪除。 Rete演算法接受對工作儲存器的修改操作描述 ,產生一個修改衝突集的動作 。

Rete演算法的步驟如下:

將初始資料(fact)輸入Working Memory。
使用Pattern Matcher比較規則(rule)和資料(fact)。
如果執行規則存在衝突(conflict),即同時啟用了多個規則,將衝突的規則放入衝突集合。
解決衝突,將啟用的規則按順序放入Agenda。
使用規則引擎執行Agenda中的規則。重複步驟2至5,直到執行完畢所有Agenda中的規則。
五:Tread演算法

在 Rete演算法中 ,同一規則連線結點上的暫存器保留了大量的冗餘結果。實際上, 暫存器中大部分資訊已經體現在衝突集的規則例項中。因此 ,如果在部分匹配過程中直接使用衝突集來限制模式之間的變數約束,不僅可以減少暫存器的數量 ,而且能夠加快匹配處理效率 。這一思想稱為 衝突集支撐策略 。

考慮增刪事實對匹配過程的影響,當向工作儲存器增加一個事實時 ,衝突集中已有的規則例項仍然保留,只是將與該事實匹配的規則例項加入到衝突集中; 當從工作儲存器刪去一個事實時,不可能有新的規則例項產生, 只是將 包含該事實的規則例項從衝突集中刪去。

基於衝突集支撐策略和上述觀察, Treat演算法放棄了Rete演算法中利用暫存器儲存模式之間變數約束中間結果的思想,對於每一個模式 ,除保留原有 a暫存器的外 ,增加兩個新鏈來記錄與該模式匹配的增刪事實,一個叫做增鏈 (addlist),另一個叫做刪鏈 (deletelist)。當修改描述的操作符為 “+”時,臨時執行部分連線任務;當修改描述的操作符為 “一”時,直接刪去衝突集中包含該事實的規則例項。

Treat演算法的步驟如下:

行動 :根據點火規則的 RHS,生成修改描述表 CHANGES;
模式匹配:置每一模式的刪鏈和增鏈為空,對 CHANGES的每一個修改描述 ,執行模式匹配。對於與修改描述中的事實匹配成功的模式,若修改描述的操作符為 “+”, 將該事實加入這一模式的增鏈;若修改描述的操作符為 “一”,將該事實加入這一模式的 刪鏈。
刪去事實的處理:對於任一模式鏈中的每一個事實,找到衝突集中所有包含該事實 的規則例項,並將這一規則例項從衝突集中刪去。相應地修改該模式的 a暫存器 。
新增事實的處理:對 於 每 一 模 式 ,若 其 增 鏈 非 空 ,則 將 增 鏈 中 的 所 有 事 實 加 入 該 模 式的a暫存器 ,並對與新增事實相關的每一條規則臨時執行部分匹配,尋找該規則新的實 例。具體做法為:首先將第一個模式增鏈中的事實集合與後一模式的 a暫存器進行連線 , 再將部分連線結果與第三個模式的a暫存器進行連線 ,一直到所有模式均連線完成為止。 其中 ,a暫存器 的內容包括新增 事實。若連線結果非空 ,則將找到 的規則 例項插入到衝突 集中。
六:Leaps 演算法

前向推理引擎,包括LEAPS,都包括了匹配-選擇-執行(match-select-action)迴圈。即,確定可以匹配的規則,選擇某個匹配 的元 組,此元組相應的規則動作被執行。重複這一過程,直到某一狀態(如沒有更多的規則動作)。RETE和TREAT匹配演算法速度慢的原因是,它們把滿足規則條 件的元組都例項化。Leaps演算法的最大的改進就是使用一種"lazy"的方法來評估條件(conditions),即僅當必要時才進行元組的例項化。這 一改進極大的減少了前向推理引擎的時空複雜度,極大提高了規則執行速度。

Leaps演算法將所有的 asserted 的 facts ,按照其被 asserted 在 Working Memory 中的順序( FIFO ),放在主堆疊中。它一個個的檢查 facts ,通過迭代匹配 data type 的 facts 集合來找出每一個相關規則的匹配。當一個匹配的資料被發現時,系統記住此時的迭代位置以備待會的繼續迭代,並且激發規則結果( consequence )。當結果( consequence )執行完成以後,系統就會繼續處理處於主堆疊頂部的 fact 。如此反覆。

Leaps演算法的效率可以比Rete演算法和Tread演算法高几個數量級。

七:其他演算法

對於HAL演算法和Matchbox演算法,使用的範圍不是很廣,這裡不做過多的介紹。

相關文章