dfa編程實現
① 有多個初始狀態的 DFA
對於多正則表達式匹配(Multiple Regular Expression Matching)的 DFA
在創建多正則表達式匹配的 DFA 的過程中,就有一個 DFA 的 Union 操作,在這個過程中,如果狀態膨脹失去控制,需要使用某種方式對正則表達式集合進行分組,以便抑制這種狀態膨脹。分組後會生成多個 DFA ,但是為了應用程序介面方便統一,將這多個 DFA 揉進一個 DFA 對象,每個分組的 DFA 就需要一個不同的初始狀態(根)。
改善 DFA Union 的性能
有相同 Tail 的多個 DFA
如果用 adfa_build 分別創建了多個不同的 ADFA(Acyclic DFA, 無環自動機),在隨後的某個時間點,想將這多個 ADFA 進行合並,仍然是 DFA 的 Union 操作,這種情況下 NFA 轉 DFA 過程中的狀態膨脹總是線性的,雖然如此,NFA 轉 DFA 的過程中仍然需要大量內存。
雖然之前我也曾想過 DFA 包含多個初始狀態(根)的可能性,但一直沒有深入思考,這次經過仔細思考,發現:對於(所有的) DFA 最小化演算法,從理論上講,完全沒有必要限制為單根 DFA,不管有多少個根,演算法都能正常工作!唯一需要注意的是,最小化前後的根,在某些應用中需要一一對應起來。
最小化之前的多根 DFA 可以完全是獨立的,唯一的要求是所有 DFA 的狀態 ID 屬於同一個 ID 空間,這很簡單,工程上用同一個 DFA 對象來表達即可,對於 分離的多個 DFA 對象,只需要實現一個 DFA 包裝器,將多個分離的 DFA 的狀態 ID 重新映射即可。
如此,就完成了執行多根 DFA 最小化的所有準備。最小化完成之後,這些多個 DFA 會共享一些相同的尾部,一般情況下,頭部不會共享;不過極端情況下,例如 DFA1 是 DFA2 的子集,那麼,從 DFA2 的根就能到達 DFA1 的根,此時整個 DFA1 就被完全共享了。
執行 DFA Union
所有的尾部都被最小化了,然後再用 NFA 轉化 DFA 的方式執行最小化,這樣,大大減小了 NFA 轉 DFA 的時間和內存消耗。
動態 DFA 匹配多個正則表達式
細節可參考:多正則表達式匹配 (Multiple Regular Expression Matching) 中的動態 DFA 演算法
主要有兩點:
多個子 DFA 在 regex_build 時使用最小化演算法得到一個包含多個根,一個尾的 DFA
動態 DFA 是多個子 DFA 的並集,因為並集的完全 DFA 無法構造出來(狀態的指數爆炸),動態地從DFA並集的NFA構造並集的部分DFA
用正則表達式集合的並集的動態DFA搜索到具體匹配的正則表表達式ID之後,從該ID的根出發,去抽取括弧部分
實現
在實現這個概念的過程中,對我的 adfa_minimize 做了一些重構,同時也進行了一些優化。
② < DFA M狀態最少化的程序實現>編譯課程設計啊!!!還要求MFC的界面!!! 求大神啊!搞定給財富值~
DFA狀態數的最小化
package chapter3;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
* 演算法 3.39 最小化一個DFA的狀態數
* @author Administrator
*
*/
publicclass Arithmetic_3_39
{
/**
* 輸入一個DFA D
* @param d DFA狀態轉換表
* @param S 狀態集合
* @param E 輸入字元表
* @param s 開始狀態
* @param F 接受狀態集
* @return 一個DFA D', 它和D接受相同的語言, 且輸入狀態數最少
*/
publicint[][] convert(int[][] d, Set<Integer> S, char[] E, int s, Set<Integer> F)
{
// 首先用接受狀態組和非接受狀態組劃分 PI
Set<Set<Integer>> PI = new HashSet<Set<Integer>>();
// 計算 S - F
S.removeAll(F);
PI.add(F);
PI.add(S);
// 最初, 令PInew = PI
Set<Set<Integer>> PInew = new HashSet<Set<Integer>>();
// TODO 解決淺復制帶來的問題
PInew.addAll(PI);
while (true)
{
// 對PI中的每個組G
for (Iterator<Set<Integer>> it = PI.iterator(); it.hasNext(); )
{
List<Object> groupList = new ArrayList<Object>();
Set<Integer> G = it.next();
// 使用字元表測試G的元素
// 對於字元表的每個輸入a
for (int i = 0; i < E.length; i++)
{
Map<Integer, Set<Integer>> groupMap = new HashMap<Integer, Set<Integer>>();
char a = E[i];
for (Iterator<Integer> sIt = G.iterator(); sIt.hasNext(); )
{
int stat = sIt.next();
// 從狀態S出發 沿著a能夠到達的狀態
int tar = d[stat][a];
// 獲取目標狀態在PI中的位置
int idx = getElelementIdx(PI, tar);
Set<Integer> group = groupMap.get(idx);
if (group == null)
{
group = new HashSet<Integer>();
groupMap.put(idx, group);
}
group.add(stat);
}
groupList.add(groupMap);
}
// 在PInew中將G替換為對G進行分組得到的那些小組
PInew.remove(G);
PInew.addAll(setListPoly(groupList));
}
// 判斷2個集合組是否相等
if (!isTwoSetGrpEqual(PI, PInew))
{
PI.clear();
PI.addAll(PInew);
} else
{
break;
}
}
// TODO 步驟4
for (Iterator<Set<Integer>> it = PI.iterator(); it.hasNext(); )
{
Set<Integer> set = it.next();
// 令S1是PI組中某個G的代表
for (Iterator<Integer> sIt = set.iterator(); sIt.hasNext(); )
{
int s1 = sIt.next();
while (sIt.hasNext())
{
// 用S1替換SWP
int swp = sIt.next();
for (int i = 0; i < d.length; i++)
{
for (int j = 0; j < d[i].length; j++)
{
if (d[i][j] == swp)
{
d[i][j] = s1;
}
}
}
// 刪除SWP的轉換函數
d[swp] = newint[]{};
}
}
}
return d;
}
/**
* 獲取某個元素在集合組中的索引
* @param set
* @param element
* @return
*/
privateint getElelementIdx(Set<Set<Integer>> set, int element)
{
int idx = 0;
for (Iterator<Set<Integer>> it = set.iterator(); it.hasNext(); )
{
Set<Integer> g = it.next();
if (g.contains(element))
{
// TODO 檢查HASHCODE 是否代表了集合的位置
return idx;
}
idx++;
}
return -1;
}
// 計算集合組聚合的結果
@SuppressWarnings("unchecked")
private Set<Set<Integer>> setListPoly(List<Object> oriSetList)
{
Set<Set<Integer>> result = new HashSet<Set<Integer>>();
if (oriSetList.size() > 0)
{
// 讀取第一個集合組
Map<Integer, Set<Integer>> groupMap = (Map<Integer, Set<Integer>>)oriSetList.get(0);
for (Iterator<Integer> it = groupMap.keySet().iterator(); it.hasNext(); )
{
result.add(groupMap.get(it.next()));
}
for (int i = 1; i < oriSetList.size(); i++)
{
// 獲取中間集合
Map<Integer, Set<Integer>> midMap = (Map<Integer, Set<Integer>>)oriSetList.get(i);
List<Set<Integer>> midSetList = new ArrayList<Set<Integer>>();
for (Iterator<Integer> it = midMap.keySet().iterator(); it.hasNext(); )
{
midSetList.add(midMap.get(it.next()));
}
// 開始計算
// 運算結果
List<Set<Integer>> calcResult = new ArrayList<Set<Integer>>();
for (Iterator<Set<Integer>> it = result.iterator(); it.hasNext(); )
{
Set<Integer> srcSet = it.next();
for (int k = 0; k < midSetList.size(); k++)
{
// 計算2個集合的交集
Set<Integer> mixed = getSetMixed(srcSet, midSetList.get(k));
// 如果結果不為空
if (!mixed.isEmpty())
{
// 保存運算結果
calcResult.add(mixed);
}
}
}
// 將計算結果替換result
result.clear();
result.addAll(calcResult);
}
}
return result;
}
// 計算二個集合的交集
private Set<Integer> getSetMixed(Set<Integer> set1, Set<Integer> set2)
{
Set<Integer> mixed = new HashSet<Integer>();
for (Iterator<Integer> it = set1.iterator(); it.hasNext(); )
{
int emu = it.next();
if (set2.contains(emu))
{
mixed.add(emu);
}
}
return mixed;
}
/**
* 判斷2個集合組是否相等
* @param setGrp1
* @param setGrp2
* @return
*/
privateboolean isTwoSetGrpEqual(Set<Set<Integer>> setGrp1, Set<Set<Integer>> setGrp2)
{
boolean same = false;
int matchCounts = 0;
if (setGrp1.size() == setGrp2.size())
{
for (Iterator<Set<Integer>> it = setGrp1.iterator(); it.hasNext(); )
{
Set<Integer> set1 = it.next();
for (Iterator<Set<Integer>> it2 = setGrp2.iterator(); it2.hasNext(); )
{
Set<Integer> set2 = it2.next();
if (set2.equals(set1))
{
matchCounts++;
}
}
}
if (matchCounts == setGrp1.size())
{
same = true;
}
}
return same;
}
}
// 測試:
package test;
import java.util.HashSet;
import java.util.Set;
import chapter3.Arithmetic_3_39;
publicclass TestCase
{
publicstaticvoid main(String[] args)
{
new TestCase().test_339();
}
publicvoid test_339()
{
// DFA的轉換表
int[][] d = {{}, {2, 3}, {2, 4}, {2, 3}, {2, 5}, {2, 3}};
// 輸入狀態集合
Set<Integer> S = new HashSet<Integer>();
S.add(1);
S.add(2);
S.add(3);
S.add(4);
S.add(5);
// 輸入字元
char[] E = {0, 1};
int s = 1;
Set<Integer> F = new HashSet<Integer>();
F.add(5);
Arithmetic_3_39 a339 = new Arithmetic_3_39();
a339.convert(d, S, E, s, F);
}
}
對於一個NFA,當把它確定化之後,得到的DFA所具有的狀態數可能並不是最小的。其原因之一,就在於上面所給出的確定化演算法沒有考慮到DFA中具有某種「同一性」的一些狀態可加以合並的問題。所謂一個DFA M狀態數的最小化,是指構造一個等價的DFA M′,而後者有最小的狀態數。為了說明狀態數最小化演算法的思想,我們先引入可區分狀態的概念。設M=(K,Σ,f,S0,Z)為一DFA,並設s和t是M的兩個不同狀態,我們說狀態s,t為某一輸入串w所區分,是指從s,t中之一出發,當掃視完w之後到達M的終態,但從其中的另一個狀態出發,當掃視完同一個w後而進入非終態。如果兩個狀態s和t不可區分 (即對任何輸入串w,當且僅當f(s,w)∈Z,f(t,w)∈Z),則稱s和t等價。顯然,在一個DFA中,就識別符號串的作用而言,相互等價的狀態處於同等的地位,故可設法將它們合並,以減少DFA的狀態個數。下面給出一個將DFA M狀態數最小化的演算法。此演算法的基本思想,就是將M的狀態集K逐步進行劃分,以期最後按上述狀態的等價關系將K分裂為r個 (r≤|K|)互不相交的子集,使得屬於同一子集中的任何兩個狀態都是等價的,而屬於不同子集的任意兩個狀態都是可區分的。此演算法的執行步驟如下:(1) 首先,將M的狀態集K按終態與非終態劃分為兩個子集Z及K-Z,以構成初始分劃,記為π={Z,K-Z}。(2) 設當前的分劃π中已含有m個子集,即π={I1, I2, …, Im}其中,屬於不同子集的狀態是可區分的,而屬於同一子集中的諸狀態則是待區分的。即需對每一子集Ii={Si1,Si2,…,Sin}中各狀態Sir (Sir∈K,1≤r≤n)進行考察,看是否還能對它們再進行區分。例如,Sip和Siq是Ii中的兩個狀態,若有某個a∈Σ,使f(Sip,a)=Sju及f(Siq,a)=Skv,而狀態Sju及Skv分別屬於π中兩個不同的子集Ij和Ik,故Sju與Skv為某一w所區分,從而Sip和Siq必為w所區分,故應將子集Ii進一步細分,使Sip和Siq分別屬於Ii的不同的子集。也就是說,對於每一子集Ii及每一a∈Σ,我們需考察Iai=f(Ii,a)=∪n[]r=1f(Sir,a)若Iai中的狀態分別落於π中的p個不同的子集,則將Ii分為p個更小的狀態子集I(1)i,I(2)i,…,I(p)i,使得對於每一個I(j)i,f(I(j)i,a)中的全部狀態都落於π的同一子集之中。注意,若對某狀態Sir,f(Sir,a)無意義,則Sir與任何Sit(f(Sit,a)有定義)都是可區分的。這樣,每細分一次,就得一個新的分劃πnew,且分劃中的子集數也由原來的m個變為m+p-1個。(3) 若πnew≠π,則將πnew作為π再重復第2步中的過程,如此等等,直到最後得到一個分劃π,使πnew=π,即π中的各個子集不能再細分為止。(4) 對於所得的最後分劃π,我們從它的每一子集Ij={Sj1,Sj2,…,Sjr}中任選一個狀態,如Sj1,作為Ij中所含各狀態的代表,這些所選出的狀態便組成了M′的狀態集K′。而且,若Ij中含有M的初態,則Sj1為M′的初態;若Ij中含有M的終態,則Sj1為M′的一個終態。此外,為構成DFA M′,還應將各子集中落選的狀態從原DFA M中刪去,並將原來進入這些狀態的所有矢線都改為進入它們的代表狀態。例36設已給DFAM=({S0,S1,…,S4}, {a,b},f,S0,{S4})相應的狀態轉換圖和狀態轉移矩陣如圖314(a)及(b)所示。現用上述演算法將M最小化。(1) 初始分劃由兩個子集組成,即π:{S0,S1,S2,S3}, {S4}(2) 為得到下一分劃,考察子集{S0,S1,S2,S3}。為敘述方便起見,下面我們用記號{}a表示:當M分別處於該子集各狀態之下,對輸入符號a轉移到的下一狀態所組成的集合。因為{S0,S1,S2,S3}a={S1}{S0,S1,S2,S3}但{S0,S1,S2}b={S2,S3}{S3}b={S4}即{S0,S1,S2,S3}b不包含在π的同一子集之中,故應將{S0,S1,S2,S3}分為兩個子集{S0,S1,S2}及{S3},於是便得到下一分劃πnew:{S0,S1,S2}, {S3}, {S4}又因πnew≠π,現以πnew作為π,即π:{S0,S1,S2}, {S3}, {S4}再考慮{S0,S1,S2},因為{S0,S1,S2}a={S1}{S0,S1,S2}而{S0,S2}b={S2}{S1}b={S3}故應將{S0,S1,S2}再分為{S0,S2}及{S1},從而又得到πnew:{S0,S2}, {S1}, {S3}, {S4}由於此時仍有πnew≠π,故再以πnew作為π,即π:{S0,S2}, {S1}, {S3}, {S4}現考察{S0,S2},由於{S0,S2}a={S1}而{S0,S2}b={S2}{S0,S2}即{S0,S2}a與{S0,S2}b已分別包含在π的同一子集{S1}及{S0,S2}之中,故子集{S0,S2}已不能再分裂。此時πnew=π,子集分裂的過程宣告結束。(3) 現選擇狀態S0作為{S0,S2}的代表,將狀態S2從狀態轉換圖中刪去,並將原來引向S2的矢線都引至S0,這樣,我們就得到了化簡後的DFA M′,如圖315(a)及(b)所示。最後,再考察圖314(a)及圖315(b)所示的FA。容易看出,它們所接受的語言均是以abb為後綴的任意a,b符號串所組成的集合,也就說,這兩個FA是相互等價的。實際上,我們還可以進一步證明如下的定理。定理32對於有同一接受集的FA,與之等價且具有最小狀態數的DFA在同構意義下 (即不顧狀態的命名)是惟一的。
③ 實現將NFA轉化為DFA(代碼)
由於代碼過長,所以轉到記事本,希望能幫你解決問題,存QQ網盤共你下載:
http://202.102.64.11/cgi-bin/dl?1//NFAtoDFA.txt
④ C語言實現NFA轉DFA
ε只能出現在NFA中,當然不是為了方便直觀,而是連通NFA和DFA的橋梁。編譯原理講授的不是如何繪制NFA或者DFA,二是告訴讀者怎樣能夠自動實現NFA或DFA的構造。在實際應用中ε可以幫助計算機轉換NFA為DFA,而在屬性文法和語法制導階段,它也是溝通綜合屬性與繼承屬性、執行語義動作不可或缺的一部分。另外ε的使用可以大大簡化文法產生式的構造難度。我記得最初使用ε是為了使得文法體系(字母表)更加完善,但是在實際應用中卻變得應用廣泛(此觀點不一定正確)。
最後想說的是,在編譯中,ε也帶來了不小的麻煩,否則也就不會有諸如「去空產生式」這樣的演算法了:)
⑤ !!編譯原理DFA和NFA
DFA或NFA是對計算機程序的行為的抽象模型。你編寫的程序其實就對應了一個自動機。簡單舉例來說,如果a,b可以取值0或1; 程序: if(a==1) b=1; 這個程序對應了一個自動機。
對應的自動機就有狀態 (0,0), (0,1), (1,1), (1, 0)
比如你自動機的初始狀態是 (1,0)即a=1,b=0時,運行程序的下一個狀態就是(1,1)。
畫圖出來就是 這4個狀態作為頂點,並且有下面幾條邊
(0,0) --> (0,0)(自環), (1,0)-->(1,1), (1,1)-->(1,1)(自環), (0,1)-->(0,1)自環
存在的意義就是一種理論模型,也可以認為是一種編程思想。 詞法分析系也離不開 if else, 這一系列的if else和條件也就組成自動機。。。
最經典體現自動機思想的演算法就是KMP演算法,你肯定學過,字元串子串匹配的演算法。 回憶這個演算法的過程:演算法第一步構造的next表(數據結構教材的說法)其實就是根據子串的內容構造了一個自動機! 演算法第二步將原串作為自動機輸入,自動機的輸出就是匹配到的子串位置或者無匹配。
⑥ 編譯原理這個DFA怎麼畫
這個是能畫的最簡單的,左邊是開始狀態。原則是:1)先連接運算,2)再選擇3)再閉包
⑦ dfa演算法的關鍵點是什麼
起因: 從網頁中爬去的頁面,需要判斷是否跟預設的關鍵詞匹配(是否包含預設的關鍵詞),並返回所有匹配到的關鍵詞 。
目前pypi 上兩個實現
但是其實包都是基於DFA 實現的
這里提供源碼如下:
#!/usr/bin/python2.6
# -*- coding: utf-8 -*-
import time
class Node(object):
def __init__(self):
self.children = None
# 標記匹配到了關鍵詞
self.flag = False
# The encode of word is UTF-8
def add_word(root,word):
if len(word) <= 0:
return
node = root
for i in range(len(word)):
if node.children == None:
node.children = {}
node.children[word[i]] = Node()
elif word[i] not in node.children:
node.children[word[i]] = Node()
node = node.children[word[i]]
node.flag = True
def init(word_list):
root = Node()
for line in word_list:
add_word(root,line)
return root
# The encode of word is UTF-8
# The encode of message is UTF-8
def key_contain(message, root):
res = set()
for i in range(len(message)):
p = root
j = i
while (j<len(message) and p.children!=None and message[j] in p.children):
if p.flag == True:
res.add(message[i:j])
p = p.children[message[j]]
j = j + 1
if p.children==None:
res.add(message[i:j])
#print '---word---',message[i:j]
return res
def dfa():
print '----------------dfa-----------'
word_list = ['hello', '民警', '朋友','女兒','派出所', '派出所民警']
root = init(word_list)
message = '四處亂咬亂吠,嚇得家中11歲的女兒躲在屋裡不敢出來,直到轄區派出所民警趕到後,才將孩子從屋中救出。最後在徵得主人同意後,民警和村民合力將這只發瘋的狗打死'
x = key_contain(message, root)
for item in x:
print item
if __name__ == '__main__':
dfa()
⑧ KF,怎樣執行dfa文件 NX二次開發
UG的二次開發定義很廣泛, 你簡單記錄一些宏Macro,然後添加到圖標裡面也算二次開發。 或者使用UG的Grip語言來根據自己需要實現的功能來編寫一個小程序,加到自己的UG圖標裡面,也是二次開發。或者使用C++語言,使用UG的KF模塊來編寫一些程序。