BỘ GIÁO DỤC – ĐÀO TẠO
ĐẠI HỌC ĐÀ NẴNG
BÁO CÁO TIỂU LUẬN MÔN LÝ THUYẾT TÍNH TOÁN
Đề tài: Văn phạm phi ngữ cảnh tương ứng với một
máy nhận PDA và sự phân tích cú pháp
Giáo viên hướng dẫn : PGS.TS Phan Huy Khánh
Nhóm học viên thực hiện : Nguyễn Tiến Mẫu
Nguyễn Văn Phong
Nguyễn Thị Vũ Thảo
Lớp : Khoa học máy tính
Khóa : Tháng 8/2009
Đà Nẵng, tháng 5/2010
Trang 1
Phần 1. LÝ THUYẾT
PUSHDOWN AUTOMATA
1. Văn phạm phi ngữ cảnh tương ứng với máy nhận PDA
Phần này sẽ hữu ích để ghi nhớ rằng máy PDA đẩy xuống đã được cấu trúc
trong phần trước để giả lập các dẫn xuất bên trái nhất trong một văn phạm phi ngữ
cảnh được thừa nhận. Nếu tại một điểm nào đó trong một dẫn xuất chuỗi hiện tại
là xα. x là một chuỗi chứa kí tự kết thúc, khi đó tại một điểm nào đó trong dãy mô
phỏng, chuỗi vào đọc tới x và ngăn xếp chứa chuỗi α. Ngăn xếp alphabet của PDA
là ΣUV. Sự di chuyển được định nghĩa để những biến được lấy ra từ ngăn xếp và
được thay thế bởi những biến ở phía bên phải nó, và những kí tự kết thúc trên ngăn
xếp được dùng để so sánh với những kí tự vào. Trong cấu trúc này những trạng
thái hầu hết là ngẫu nhiên; sau khi kí tự đầu tiên di chuyển, máy PDA dừng lại tại
một trạng thái cho đến khi nó sẵn sàng để nhận.
Bây giờ hãy xem xét vấn đề ngược lại, cấu trúc một văn phạm phi ngữ cảnh, cái
sinh ra ngôn ngữ được chấp nhận bởi một máy nhận PDA. Những đối số được
phức tạp hóa một cách vừa phải, nhưng nó sẽ được đơn giản hóa đến mức độ mà
chúng ta có thể thừa nhận rằng máy PDA chấp nhận ngôn ngữ bởi một stack rỗng.
Bởi vậy, công việc đầu tiên của chúng ta là tự làm sáng tỏ rằng sự thừa nhận này
có thể thực hiện mà không làm mất đi tính tổng quát. Chúng ta phát biểu thành
định lí 7.3 sau đây và đưa ra phần chứng minh ngắn gọn, để lại phần cụ thể cho các
ví dụ.
Định lí 7.3
Cho M=(Q, Σ, Γ, q
0
, Z
0
, A,
δ
) là một máy đẩy xuống chấp nhận ngôn ngữ
*
L ⊆ ∑
. Và một PDA khác có M
1
=(Q
1
, Σ
1
=, Γ
1
, q
1
, Z
1
, A
1
,
δ
1
) chấp nhận ngôn ngữ
L bởi ngăn xếp rống. Nói cách khác, cho bất kì một chuỗi x, x
L nếu và chỉ nếu
(q1, x, Z1)
*M1 (q,
Λ
,
Λ
) cho một vài trạng thái q
Q1.
Chứng minh ngắn gọn:
Tại thời điểm máy PDA M chấp nhận một chuỗi thì ngăn xếp của nó có thể
không rỗng. Chúng ta muốn xây dựng M1
để khi 2 máy M1
và M xử lí trên cùng
một chuỗi kí tự vào thì ngăn xếp của M1 sẽ chính xác bị rỗng khi máy M chuyển
sang một trạng thái chấp nhận. Nếu chúng ta tạo ra máy M1 một cách đơn giản là
một bản sao của máy M nhưng với khả năng tự làm rỗng ngăn xếp mà không cần
đọc một kí tự vào nào. Khi máy M chuyển sang một trạng thái nhận thì chúng ta sẽ
có phần chúng ta muốn: bất kì khi nào máy M chuyển sang một trạng thái nhận, thì
câu vào cũng được M1
chấp nhận bởi ngăn xếp rỗng. Lí do mà điều này không
hoàn toàn đầy đủ là máy M có thể bị đổ vỡ (bị treo) (trong một trạng thái không
Trang 2
được chấp nhận) với một ngăn xếp rỗng; trong trường hợp này, vì M1 sao chép
chính xác M nên ngăn xếp của nó cũng bị rỗng và vì ngăn xếp của M1 rỗng nên nó
sẽ chấp nhận một một chuỗi kí tự mà mà máy M không nhận. Để tránh điều này,
chúng ta sẽ để M1 bắt đầu bằng cách đặt vào ngăn xếp của nó một kí tự đặc biệt
thấp hơn kí tự bắt đầu của M. Kí tự đặc biệt này cho phép M1 tránh sự làm rỗng
ngăn xếp của nó cho đến khi nó được làm rỗng một cách thích hợp.
Cách mà chúng ta để cho M
1
tự làm rỗng ngăn xếp của nó khi M chuyển sang
một trạng thái nhận là cung cấp cho nó một sự chuyển tiếp từ trạng thái này sang
một trạng thái đặc biệt gọi là trạng thái “đang làm rỗng ngăn xếp”, tại đó có nhiều
sự chuyển tiếp để lấy các kí tự ra từ ngăn xếp cho đến khi ngăn xếp rỗng.
Trở lại vấn đề: chúng ta có một PDA M chấp nhận ngôn ngữ L bởi một ngăn
xếp rỗng (viết tắt là L=Le(M)) và chúng ta muốn xây dựng một CFG tạo ra ngôn
ngữ L. Thật là hữu ích để cố gắng duy trì tối đa sự tương ứng trong định lí 7.2 giữa
thao tác của PDA và phần bên trái của luật sinh được giả lập. Chuỗi kí tự hiện tại
trong luật sinh sẽ bao gồm 2 phần, phần thứ nhất là chuỗi kí tự của những kí tự đọc
vào bởi PDA cho đến lúc này và phần thứ 2 còn lại tương ứng với nội dung của
stack hiện tại. Thực tế, chúng ta định nghĩa CFG để mà phần thứ 2 này sẽ bao gồm
toàn bộ các biến với mục đích làm nổi bật sự tương quan giữa nó và nội dung của
stack. Để mà tạo ra một chuỗi kí tự của những trạng thái kết thúc chúng ta phải loại
trừ tận cùng tất cả các biến từ chuỗi hiện tại, và để mà chuỗi vào được thừa nhận
bởi PDA (bởi một stack rỗng), tất cả các kí tự trên stack cuối cùng phải được lấy ra
hết.
Đầu tiên, chúng ta xem xét một cách tiếp cận khá đơn giản, nói chung nó quá
đơn giản để thực hiện. Lấy những biến trong grammar làm tất cả các kí tự có thể
của ngăn xếp của PDA, đổi tên nếu cần thiết để bao gồm luôn cả những kí tự
không nhập vào. Lấy kí tự bắt đầu là Z
0
, bỏ qua những trạng thái đầy đủ của PDA.
Mỗi khi PDA di chuyển là đọc a ( a là A hoặc một phần tử của Σ) và thay thế A
trên ngăn xếp bởi B
1
, B
2
,…B
m
, có kết quả sau.
A
→
a B
1
B
2
…B
m
Cách tiếp cận này phác thảo cho chúng ta sự tương quan ở trên giữa những nội
dung của ngăn xếp hiện tại và chuỗi các biến còn lại trong chuỗi kí tự hiện tại đang
được thành lập. tuy nhiên, nó sẽ cho phép grammar tạo ra tất cả các chuỗi kí tự
được thừa nhận bởi PDA. Nguyên nhân mà nó khá đơn giản là bằng cách bỏ qua
những trạng thái của PDA mà chúng ta có thể để cho những chuỗi kí tự khác được
nhận được. Chúng ta xem xét ví dụ 7.1 Có một PDA thừa nhận ngôn ngữ {xcxr
x
{a,b}*}. Sự thừa nhận bởi trạng thái cuối cùng, đúng ra là là bởi ngăn xếp rỗng
nhưng chúng ta có thể sữa chữa điều này và cũng có thể loại trừ trạng thái q
2
bằng
cách thay đổi bước 12 thành
δ
(q
1
,
Λ
, Z
0
) = {(q
1
,
Λ
)}
Trang 3
Thay vì {(q
2
, Z
0
)}. Chúng ta sử dụng A, B làm những kí tự stack thay vì dùng a,
b. Những bước chuyển của PDA bao gồm
δ
(q
0
, a, Z
0
) = {(q
0
, A, Z
0
)}
δ
(q
1
, c, A) = {(q
1
, A)}
δ
(q
1
, a, A) = {(q
1
,
Λ
)}
δ
(q
1
,
Λ
, Z
0
) = {(q
1
,
Λ
)}
Sử dụng qui luật mà chúng ta giả định trên, chúng ta thu được những bước
chuyển tương ứng
Z
0
→
aAZ
0
A
→
cA
A
→
a
Z
0
→Λ
Chuỗi aca có các dẫn xuất đầy đủ là:
Z
0
⇒
aAZ
0
⇒
acAZ
0
⇒
acaZ
0
⇒
aca
Tương ứng với chuỗi các dịch chuyển sau
(q
0,
aca, Z
0
)
(q
0
, ca, AZ
0
)
(q1, a, AZ
0
)
(q1,
Λ
, Z0)
(q1,
Λ
,
Λ
)
Nếu chúng ta chạy PDA và thay chuỗi vào là aa, thì di sự chuyển đầu tiên là
(q
0
, aa, Z
0
)
(q
0
, a, AZ
0
)
và tại điểm này máy sẽ bị treo, bởi vì chỉ có duy nhất một trạng thái q
1
cho phép
đọc a và thay thế A trên ngăn xếp thành
Λ
. Tuy nhiên grammar của chúng ta còn
cho phép sự dẫn dắt sau
Z
0
⇒
aAZ
0
⇒
aaZ
0
⇒
aa
Để loại bỏ vấn đề này, chúng ta phải định nghĩa grammar của chúng ta sao cho
thống nhất các trạng thái của PDA. Thay vì sử dụng chính các kí tự của stack như
là các biến, chúng ta thử sử dụng những đối tượng của dạng sau:
[p,A,q]
trong đó, q và p là các trạng thái. Cho biến [p,A,q] được thay thế bởi a (
Λ
hoặc
là một kí tự cuối), nó phải là trường hợp mà tại đó PDA đi chuyển đọc a, lấy A ra
từ ngăn xếp và làm cho máy chuyển từ trạng thái p sang trạng thái q. Tổng quát
hơn, dẫn xuất này bao gồm biến [p,A,q] dùng làm đại diện cho bất kì sự di chuyển
nào làm cho PDA chuyển từ trạng thái p sang trạng thái q, và có tác dụng cuối
cùng là việc gỡ bỏ A từ stack.
Nếu biến [p,A,q] xuất hiện trong chuỗi kí tự hiện tại của một dẫn xuất, thì mục
tiêu của chúng ta là thay thế nó bởi
Λ
hoặc một kí tự kết thúc. Điều này có thể xảy
ra nếu có một sự di chuyển làm cho PDA chuyển từ trạng thái p sang trạng thái q
và lấy A ra khỏi stack. Tuy nhiên, giả sử một cách khác rằng có một sự dịch
chuyển từ p sang trạng thái p1 nào đó, nó đọc a và thay thế A trên stack bởi B1,
B2,… Bm. Cách này thích hợp để đưa a vào chuỗi hiện tại của chúng ta tại điểm
này, bởi vì chúng ta muốn chuỗi khởi tạo của các kí tự kết thúc tương ứng với kí tự
đọc vào sau này. Nhưng cho tới giờ thì mục tiêu ban đầu của chúng ta đang bị thay
Trang 4
đổi vì tất cả các kí tự mới được đưa vào stack. Cách trực tiếp nhất để loại bỏ những
kí tự mới B1, B2,… Bm là: bắt đầu từ trạng thái p1 và thực hiện một chuỗi các dịch
chuyển-kết thúc tại trạng thái p2 nào đó, hay nói khác kết quả là B1 sẽ được gỡ bỏ
từ ngăn xếp. Sau đó thực hiện một vài sự dịch chuyển nữa và gỡ bỏ B2 trong quá
trình dịch chuyển từ p2 sang trạng thái P3 nào đó,…Tóm lại, để dịch chuyển từ
Pm-1 sang Pm nào đó thì gỡ bỏ Bm-1, và cuối cùng dịch chuyển từ P
m
đến q và gỡ
bỏ B
m
. Những sự dịch chuyển thực sự của PDA có thể không hoàn thành những
bước này một cách trực tiếp, nhưng nó là cách tốt nhất để đạt được điều chúng ta
muốn. Bởi vì nó không ảnh hưởng đến các trạng thái p2, p3, pm, chúng ta sẽ cho
phép bất kì một chuỗi có dạng sau
a[p
1
,B,p
2
][ p
2
,B
2
,p
3
]…[ p
m
,B
m
,q]
thay thế [p,A,q] trong chuỗi hiện tại. Nói cách khác chúng ta sẽ có kết quả sau:
[p,A,q] → a[p
1
,B,p
2
][ p
2
,B
2
,p
3
]…[ p
m
,B
m
,q]
cho tất cả các trạng thái có thể trong chuỗi trạng thái p2, p3, pm. Một vài chuỗi
như vậy sẽ bị tắc ngẽn, có nghĩa là sẽ không có chuỗi dịch chuyển nào theo sau
chuỗi trạng thái này và không có được kết quả tối ưu trên. Nhưng sẽ không có tổn
hại nào từ những kết quả này, bởi vì với bất kì sự dẫn xuất nào trong số những
chuỗi tắc ngẽn đó xuất hiện, sẽ có ít nhất một biến không thể bị loại trừ từ chuỗi kí
tự và vì vậy sự dẫn xuất sẽ không đưa ra một chuỗi kí tự của những kí tự kết thúc.
Nếu chúng ta biểu thị S làm kí tự đầu tiên của grammar, và kết quả mà chũng ta
cần có dạng sau
S
→
[q
0
, Z
0
, q]
Trong đó q0 là trạng thái ban đầu. Khi chúng ta thừa nhận các chuỗi bằng stack
rỗng thì trạng thái cuối cùng là không liên quan, vì vậy chúng ta phải kể đến một
kết quả của loại này cho mỗi trạng thái q có thể.
Bây giờ thì chúng ta đã có bằng chứng rằng CFG mà chúng ta mô tả tạo ra ngôn
ngữ được thừa nhận bởi M.
Định lí 7.4
Cho M=(Q, Σ, Γ, q
0
, Z
0
, A,
δ
) là một máy đẩy xuống thừa nhận ngôn ngữ L
bởi ngăn xếp rỗng; L=L
e
(M). Có một ngữ cảnh-phi ngữ pháp G với L(G)=L.
Chứng minh
Chúng ta định nghĩa G=(V, Σ, S, P ) như sau:
V={S}
∪
{[ p, A,q]
A
∈
Γ, p,q
∈
Q }
Tập P chứa các luật sinh cho sau và chỉ có :
1. Với mỗi q
∈
Q, thì S
→
[q
0
, Z
0
, q] nằm trong P
2. Với mỗi q, q
1
∈
Q, a
∈
Σ
∪
{Λ} và A
∈
Γ, nếu
δ
(q, a, A) chứa (q
1
,
Λ
), thì [q,
A, q
1
]
→
a nằm trong P.
3. Với mỗi q, q
1
∈
Q, a
∈
Σ
∪
{Λ, A
∈
Γ và m>=1, nếu
δ
(q, a,
Λ
) chứa (q
1
,B
1
,
B
2
…B
m
), với B
1
, B
2
…B
m
∈
Γ, thì với mỗi sự lựa chọn q
2
,…,q
m+1
∈
Q, thì
Trang 5
[q,A,q
m+1
] → a[q
1
,B
1
,q
2
][ q
2
,B
2
,q
3
]…[ q
m
, B
m
,, q
m+1
]
nằm trong P
YÙ tưởng chính của sự chứng minh này là mô tả những chuỗi các kí tự kết thúc
có thể được dẫn xuất từ một biến [q, A, q’]; cụ thể, để thấy rằng với mỗi q, q’
∈
Q,
A
∈
Γ và x
∈
Σ
*
, thì
(1) [q, A, q’]
⇒
*
G
x nếu và chỉ nếu (q, x, A)
*
M
(q’,
Λ
,
Λ
)
Từ kết quả này định lí sẽ xảy ra. Một mặt, nếu x
∈
L
e
(M), thì (q
0
, x, Z
0
)
*
M
(q,
Λ
,
Λ
) với q
∈
Q thì (1) đưa đến là [q
0
, Z
0
, q]
⇒
*
G
x; vì vậy x
∈
L(G) bởi vì chúng ta
có thể bắt đầu một dẫn xuất với một luật sinh của loại 1. Mặc khác, nếu x
∈
L(G)
thì bước đầu tiên trong bất kì một dẫn xuất nào của x phải là S
⇒
[q
0
, Z
0
, q] cho mỗi
q
∈
Q, có nghĩa là [q
0
, Z
0
, q]
⇒
*
G
x. suy ra từ (1) thì x
∈
L
e
(M).
Cả hai phần của (1) đều được chứng minh bằng cách sử dụng thuật toán qui
nạp. Chúng ta đưa các kí hiệu
⇒
n
và
n
(n là một số nguyên dương) để chỉ n bước
dẫn xuất trong grammar và chuỗi n di chuyển của PDA theo thứ tự đã cho.
Đầu tiên chúng ta biểu diễn với mỗi n>=1,
(2) Nếu [q, A, q’]
⇒
n
G
x, thì (q, x, A)
*
M
(q’,
Λ
,
Λ
)
Bước cơ bản của sự chứng minh là chỉ ra rằng [q, A, q’]
⇒
1
G
x. Chỉ có thể suy
diễn rằng một bước dẫn xuất này là thuộc loại 2, và điều này chỉ xảy ra nếu x là
Λ
hoặc là một phần tử của Σ và
δ
(q, x, A) chứa (q’,
Λ
). Trong trường hợp này thì (q,
x, A
(q’,
Λ
,
Λ
) là hiển nhiên đúng.
Bước tiếp theo là với k>=1 và khi (q, A,q’)
⇒
n
G
x với n<=k thì (q, x, A
*
(q’,
Λ
,
Λ
). Bây giờ, giả sử rằng, (q, A, q’)
⇒
k+1
x thì chúng ta cần chứng minh rằng (q,
x, A
*
(q’,
Λ
,
Λ
). Bởi vì khi k>=1 thì bước dẫn xuất đầu tiên của x phải là
[q,A,q’]
⇒
a[q
1
, B
1
, q
2
][ q
2
, B
2
, q
3
]…[ q
m
, B
m
,, q’]
Với m ≥1, a
∈
Σ
∪
{
Λ
}, một vài chuỗi B
1,
B
2
B
m
∈
Γ, và một vài chuỗi q
1,
q
2
q
m
∈
Q để cho
δ
(q, a, A) chứa (q
1,
B
1.
B
m
). Phần còn lại của sự dẫn xuất sẽ làm
cho mỗi biến trong [q
i
, B
i
, q
i+1
] thành chuỗi x
i
nào đó, và biến [q
m
, B
m
, q’] thành
một chuỗi x
m
. Chuỗi x
1,
…x
m
thỏa mãn công thức ax
1
…x
m
= x, và mỗi x
i
được dẫn
xuất từ biến tương ứng của nó qua k bước hoặc ít hơn. Vì vậy, giả thuyết qui nạp
đưa đến rằng với mỗi i với 1
≤
i
≤
m-1 thì
(q
i
, x
i
, B
i
)
*
(q
i+1
,
Λ
,
Λ
)
và
(q
m
, x
m
, B
m
)
*
(q’,
Λ
,
Λ
)
Giả sử rằng M thuộc cấu hình (configuration) (q, x, A)= (q, ax
1
x
2
…x
m
, A). Bởi
vì
δ
(q, a, A) chứa (q
1,
B
1.
B
m
) nên M có thể đi đến một bước trong cấu hình
(q
1
, x
1
x
2
…x
m
, B
1
B
2
B
m
)
sau đó M có thể đi đến một chuỗi của bước
(q
2
, x
2
…x
m
, B
2
B
m
)
Trang 6
sau đó đến (q
3
, x
3
…x
m
, B
3
B
m
) và cuối cùng là đến bước (q’,
Λ
,
Λ
). Cứ như
vậy thì là kết quả kéo theo (2).
Để hoàn thành việc chứng minh (1) chúng ta chứng tỏ với mỗi n ≥ 1,
(3) Nếu (q, x, A
n
(q’,
Λ
,
Λ
) thì [q, A, q’]
⇒
*
x
Trong trường hợp n=1, một chuỗi kí tự x thỏa mãn giả thuyết (3) phải có độ dài
là 0 hoặc 1, và
δ
(q, x, A) phải chứa (q’,
Λ
). Trong trường hợp này có thể chúng ta
phải dẫn xuất x từ [q, A, q’] sử dụng một kết quả của loại 2.
Giả sử rằng k ≥ 1 và với bất kì n ≤ k, và bất kì sự kết hợp nào của q,q’
∈
Q, x
∈Σ
*
, và A
∈
Γ, nếu (q, x, A)
n
(q’,
Λ
,
Λ
), thì [q, A, q’]
⇒
*
x. Tiếp theo ta giả sử rằng
(q, x, A)
k +1
(q’,
Λ
,
Λ
), và điều chúng ta muốn là chứng tỏ rằng [q, A, q’]
⇒
*
x.
Chúng ta đã biết rằng với mỗi a
∈
Σ
∪
{
Λ
} và y
∈Σ
*
, x=ay và bước di chuyển đầu
tiên của k+1 là
(q, x, A)=(q, ay, A)
(q
1
, y, B
1
B
2
… B
m
)
ở đây m ≥ 1 vì k ≥ 1 và B
i
là những phần tử của Γ. Nói cách khác,
δ
(q, a, A)
chứa (q
1
,
B
1
B
m
). Dãy con k di chyển đến điểm kết thúc trong cấu hình (q’,
Λ
,
Λ
),vậy thì, mỗi i với 1
≤
i
≤
m thì đây là một điểm trung gian mà tại đó stack phải
chứa chính xác chuỗi B
i
, B
i+1
B
m
. Với mỗi i như vậy, đưa q
i
vào làm trạng thái của
M trong lần đầu tiên ngăn xếp chứa B
i
B
m
và x
i
là phần kí tự đọc vào nó được dùng
để di chuyển từ q
i
đến q
i+1
(hoặc nếu i=m, thì trong sự di chuyển từ q
m
đến cấu hình
(q’,
Λ
,
Λ
). Sau đó nó phải là trường hợp mà
(q
i
, x
i
, B
i
)
*
(q
i+1
,
Λ
,
Λ
)
mỗi i với 1
≤
i
≤
m-1 và
(q
m
, x
m
, B
m
)
*
(q’,
Λ
,
Λ
)
trong đó, mỗi chuỗi di chuyển được đưa ra bao gồm tối đa k bước. Vì vậy, bằng
cách đưa ra giả thuyết
(q
i
, B
i
, q’)
*
G
x
i
mỗi i với 1
≤
i
≤
m-1 và
(q
m
, B
m
, q’)
*
x
m
Vì
δ
(q, a, A) chứa (q
1
,
B
1
B
m
), chúng ta đã biết rằng
[q, A, a’]
⇒
*
ax
1
x
2
x
m
= x
Điều này đã hoàn thành việc đưa vào và chứng minh được định lí.
Tồn tại một CFG từ một PDA thừa nhận câu vào đơn giản và đối xứng
Chúng ta trở lại vấn đề ngôn ngữ L = {xcx
r
x
∈
{a,b}
*
} mà chúng ta đã dùng để
đưa vào việc xây dựng định lí 7.4. Trong thảo luận đó chúng ta đã chấp nhận PDA
với bảng các chuyển tiếp của nó được cho dưới đây. (nó đã được sửa đổi một phần
trong ví dụ 7.1, cả 2 cách sử dụng các kí tự in hoa cho các kí tự của stack và việc
thừa nhận bởi stack rỗng).
Move State Input Stack symbol Move(s)
Trang 7
number
1 q
0
a Z
0
(q
0
,
AZ
0
)
2 q
0
b Z
0
(q
0
,
BZ
0
)
3 q
0
a A (q
0
, AA)
4 q
0
b A (q
0
, BA)
5 q
0
a B (q
0
, AB)
6 q
0
b B (q
0
, BB)
7 q
0
c Z
0
(q
1
, Z
0
)
8 q
0
c A (q
1
, A)
9 q
0
c B (q
1
, B)
10 q
1
a A
(q
1
, Λ)
11 q
1
b B
(q
1
, Λ)
12 q
1
Λ
Z
0
(q
1
, Λ)
(tất cả các kí tự khác) none
Trong grammar G = (V,
Σ
, S, P) thu được từ việc xây dựng định lí 7.4, V chưa
S là một đối tượng hợp lí của dạng [p, X, q], trong đó X là một kí tự của stack và p,
q có thể là q
0
hoặc q
1
. Những loại dẫn xuất dưới đây chứa trong P:
(0) S → [q
0
, Z
0
, q]
(1) [q
0
, Z
0
, q] → a[q
0
, A, p][p, Z
0
, q]
(2) [q
0
, Z
0
, q] → b[q
0
, B, p][p, Z
0
, q]
(3) [q
0
, A, q] → a[q
0
, A, p][p, A, q]
(4) [q
0
, A, q] → b[q
0
, B, p][p, A, q]
(5) [q
0
, B, q] → a[q
0
, A, p][p, B, q]
(6) [q
0
, B, q] → b[q
0
, B, p][p, B, q]
(7) [q
0
, Z
0
, q] → c[q
1
, Z
0
, q]
(8) [q
0
, A, q] → c[q
1
, A, q]
(9) [q
0
, B, q] → c[q
1
, B, q]
(10) [q
1
, A, q
1
] → a
(11) [q
1
, B, q
1
] → b
(12) [q
1
, Z
0
, q
1
] → Λ
Việc cho phép sự kết hợp của p và q thì sẽ nhận được tất cả 35 dẫn xuất.
Xem xét chuỗi bacab. PDA chấp nhận nó bởi chuỗi các dịch chuyển sau:
(q
0
,bacab, Z
0
) (q
0
, acab, BZ
0
)
(q
0
, cab, ABZ
0
)
(q
1
, ab, ABZ
0
)
(q
1
, b, BZ
0
)
Trang 8
(q
1
,
Λ
,Z
0
)
(q
1
,
Λ
,
Λ
)
Các phép đạo hàm tương ứng trong grammar là
S
⇒
[q
0
, Z
0
, q
1
]
⇒
b[q
0
, B, q
1
][q
1
, Z
0
, q
1
]
⇒
ba[q
0
, A, q
1
][q
1
, B, q
1
][q
1
, Z
0
, q
1
]
⇒
bac[q
1
, A, q
1
][q
1
, B, q
1
][q
1
, Z
0
, q
1
]
⇒
baca[q
1
, B, q
1
][q
1
, Z
0
, q
1
]
⇒
bacab[q
1
, Z
0
, q
1
]
⇒
bacab
Từ các chuỗi dịch chuyển của PDA có thể thấy rằng có một vài sự lựa chọn của
cac phép đạo hàm. Ví dụ, giả sử chúng ta bắt đầu với dẫn xuất S → [q
0
, Z
0
, q
0
].
Tuy nhiên, nhớ rằng [q
0
, Z
0
, q] biểu diễn cho chuỗi di chuyển từ trạng thái q
0
sang
trạng thái q và gỡ bỏ Z
0
ra khỏi stack. Bởi vì PDA kết thúc ở trạng thái q
1
, vậy rõ
ràng là q nên là q
1
. Tương tự như vậy, thì bước thứ 2 sẽ là:
[q
0
, Z
0
, q
1
]
⇒
b[q
0
, B, q
0
][q
0
, Z
0
, q
1
]
Tuy nhiên, chuỗi các dịch chuyển của PDA bắt đầu tại q
0
và laoị bỏ B từ stack
và kết thúc ở trạng thái q
1
chứ không phải q
0
. thật vậy, bởi vì mỗi di chuyển đến
trạng thái q
0
thì thêm vào stack các biến [q
0
, B, q
0
] trong grammar là vô ích. Không
có chuỗi các kí tự kết thúc nào có thể được nhận được từ nó.
Trang 9
2. Phân tích cú pháp:
Cho G là một văn phạm phi ngữ cảnh trên một bộ chữ ∑. Phân tích câu x
∑
∈
*
x
( kiểm tra x được sinh ra từ văn phạm G, hoặc xác định không được sinh
ra) điều này rất hữu ích. Phân tích cú pháp trong ngôn ngữ lập trình, là ví dụ rất
cần thiết để phân loại lớp cú pháp; phân tích một biểu thức đại số cơ bản cho phép
để đánh giá biểu thức đó. Vấn đề là tìm kiếm một thuật toán hiệu quả để phân tích
cú pháp là vấn đề lớn trong nghiên cứu khoa học, và có rất nhiều kỹ thuật thiết kế
phụ thuộc vào thuộc tính của văn phạm.
Trong phần này chúng ta trở lại hai cách cơ bản đã được trình bày trong
phần 7.4 thu được từ một PDA chấp nhận ngôn ngữ L(G). Trong cả hai trường
hợp PDA không chỉ chấp nhận câu trong L(G) mà còn nêu lên nguồn gốc của quá
trình tạo ra câu x (trong trường hợp này nó bắt nguồn từ bên cực trái, trường hợp
khác bên cực phải). Mặc dù câu trả lời của một PDA là đúng hoặc sai, nó đủ để
làm tăng lên tính nổi bật của cái máy có đầu đọc di chuyển. Vì vậy đó là lý do mà
các chuỗi được chấp nhận và được hiển thị. Tuy nhiên cấu trúc câu cũng không thể
nói là nó sinh ra từ phân tích thuật toán. Bởi vì cả hai PDA vốn đã không đơn định.
Trong mỗi trường hợp, xác định trạng thái kế tiếp đúng đắn, nó sẽ được xác
nhận cuối cùng bởi PDA.
Phương pháp tiếp cận thu được ở thuật toán phân tích câu sẽ xem xét dự
đoán tất cả các trường hợp có thể để cấu tạo nên PDA, để xem một trong số đó dẫn
đến sự chấp nhận. Ví dụ 7.47 giải thuật quay lui với văn phạm đơn giản CFG. Tuy
nhiên có thể thực hiện với hai kiểu PDA không đơn định và không đơn định trực
tiếp: hơn là tạo ra một lựa chọn tuỳ ý và cố gắng xác nhận điều đó là đúng , và cố
gắng sử dụng tất cả thông tin để có sự lựa chọn chính xác. Trong phần còn lại của
phần này, chúng ta tập trung trên hai lớp văn phạm cơ bản, cho băng vào là ký tự
và ký tự trên đỉnh ngăn xếp phần còn lại là PDA cung cấp đủ các thông tin mỗi
bước chuyển kế tiếp. Hơn nữa các văn phạm tổng quát cách tiếp cận ít nhất phải
cung cấp một điểm bắt đầu cho việc phân tích câu hiệu quả.
2.1 Phân tích cú pháp từ trên xuống:
Chúng ta xem xét ngôn ngữ các câu là dấu ngoặc đơn, để thuận lợi chúng ta
thêm vào ký tự $ ở cuối mỗi câu, ngôn ngữ mới nhận được đó là L. Nếu chúng ta
sử dụng [] như là dấu ngoặc đơn ,văn phạm phi ngữ cảnh với các luật sinh như sau:
S→T$
T→[T] T | A
Rõ ràng CFG sinh ra L. Phân tích PDA từ trên xuống thu được từ văn phạm này,
nó chỉ xuất hiện không đơn định khi giá trị T trên đỉnh ngăn xếp, và chúng có một
lựa chọn của hai trạng thái chuyển sử dụng giá trị vào A. Nếu ký tự kế tiếp là [, khi
đó trạng thái chuyển là đúng (hoặc có thể tìm được trình tự chuyển) phải sinh ra
một ký tự [ ở trên đỉnh ngăn xếp mới phù hợp. Thay thế T bởi [T]T điều này là rõ
Trang 10
ràng sẽ thực hiện điều này; Thay thế T bởi A chỉ đúng nếu ký tự bên dưới T là T
hoặc [, và rất khó thấy và nó không bao giờ xuất hiện. Nó xuất hiện, cho nên, nếu
T trên đỉnh ngăn xếp T sẽ được thay thế bởi [T]T nếu ký tự vào kế tiếp là [ và A
nếu ký tự vào kế tiếp là ] hoặc $. Không đơn định đã được khử bởi được nhìn vào
trước (lookahead)- sử dụng ký tự vào kế tiếp tốt như ký tự trong ngăn xếp của
trạng thái chuyển đơn định,
Trong trường hợp này khi T ở trên đỉnh ngăn xếp và ký tự vào kế tiếp là ]
hoặc $, lấy T ra khỏi ngăn xếp chỉ được chấp nhận nếu ký tự dưới nó giống ký tự
vào. Như vậy PDA cần nhớ ký tự vào đủ dài nó giống như một ngăn xếp ký tự
mới. Chúng ta có thể thực hiện điều này bằng cách tạo ra hai trạng thái q
]
và q
$
mà
PDA có thể di chuyển tương ứng với ký tự vào khi T ở trên đỉnh ngăn xếp. Các
trạng thái chỉ chuyển đúng ký tự được lấy ra tương ứng với ký tự ở ngăn xếp và
chuyển về trạng thái q
1
. Lợi ích chắn chắn, chúng ta sử dụng trạng thái q
[
cho
trường hợp khi T trên đỉnh ngăn xếp và ký tự vào kế tiếp là [. Mặc dù trong trường
hợp này T được thay thế trên đỉnh ngăn xếp bởi xâu dài hơn [T]T, Trạng thái
chuyển từ q
]
chỉ lấy ra ký tự [ từ ngăn xếp và chuyển về trạng thái q
1
. Sự lựa chọn
này hy vọng sẽ hiệu quả hơn, thay thế hai bước chuyển thành một và q
1
ở PDA và
thay thế T trên đỉnh ngăn xếp bởi T]T.
Trong bảng dịch chuyển trạng thái của PDA không đơn định ban đầu ở biểu
diễn ở hình 7.8. và 7.9 miêu tả PDA đơn định kết hợp với nhìn vào đầu.
Tuần tự của bước chuyển mà PDA chấp nhận chuỗi []$, tương ứng với bước
chuyển cực trái của chuỗi này, được biểu diễn bên dưới.
( q
0
, []$, Z
0
)
├ ( q
1
, []$, SZ
0
) S
├ ( q
1
, []$, T$Z
0
) =>T$
├ ( q
1
, ]$, [T]TZ
0
) =>[T]T$
├ ( q
1
, ]$, T]TZ
0
)
Bảng 7.8 Phân tích PDA không đơn định các câu dấu ngoặc:
Bước
chuyển
Trạng thái Băng vào Ngăn xếp Chuyển
1 q
0
A Z
0
(q
1
,
SZ
0
)
2 q
1
A S (q
1
, S$)
3 q
1
A T (q
1
, [T]T),(q
1
,A)
4 q
1
[ [ (q
1
, A)
5 q
1
] ] (q
1
, A)
6 q
1
$ $ (q
1
, A)
7 A Z
0
(q
2
, Z
0
)
(Các trường hợp khác)
Trang 11
Bảng 7.9
1 q
0
A Z
0
(q
1
,
SZ
0
)
2 q
1
A S (q
1
, T$)
3 q
1
[ T (q
[
, [T]T)
4 q
1
A [ (q
1
, A)
5 q
1
] T (q
]
, A)
6 q
1
A ] (q
1
, A)
7 q
1
$ T (q
$
, A)
8 q
1
A $ (q
1
, A)
9 q
1
[ [ (q
1
, A)
10 q
1
] ] (q
1
, A)
11 q
1
$ $ (q
1
, Z
0
)
12 q
1
A Z
0
(q
1
, Z
0
)
(Các trường hợp khác)
├ ( q
]
,$ ,]T$Z
0
) =>[]T$
├ ( q
1
,$, T$Z
0
)
├ ( q
$
,A, $Z
0
) =>[]$
├ ( q
1
,A, Z
0
)
├ ( q
2
,A, Z
0
)
Chúng ta có lẽ đã thấy các trạng thái di chuyển 9, 10, và 11 của PDA đơn
định, và nó còn giữ lại các máy không đơn định, sẽ chưa bao giờ thực sự được sử
dụng. Các ví dụ mang tính tổng quát về chuyển trạng thái vẫn rất cần thiết. Mặc dù
không chứng minh được rằng PDA chấp nhận ngôn ngữ, có thể tìm trạng thái di
chuyển cho băng vào các xâu dài hơn.
Trong phần phân tích từ trên xuống của PDA từ văn phạm phi ngữ cảnh như
đã định nghĩa ở phần 7.4, nhìn vào giá trị băng vào kế tiếp không đủ để xác định
bước chuyển kế tiếp. Tuy nhiên, đôi khi thay đổi cấu trúc văn phạm đủ để thiết lập
thuộc tính này, như là hai ví dụ kế tiếp sau đây.
Rõ ràng khác vớ ngôn ngữ CFG ở ví dụ 7.8 ở đây ta có luật sinh :
S → T $
T→ T [T] | A
Phân tích PDA trên xuống các luật sinh văn phạm này cũng giống như ví dụ
7.8, ngoại trừ câu được thay thế bởi T trên đỉnh stack ở trạng thái di chuyển đầu ở
Trang 12
dòng 3. chúng ta thấy vấn đề ở đây là thay thế các chuỗi [][][]$, mà bắt đầu từ bên
cực trái.
S => T$ => T[T]$ => T[T][T]$ => T[T][T][T]$ => => [][][]$
Tuần tự các bước chuyển của câu vào trước khi bắt đầu:
(q
0
, [][][]$,Z
0
) ├ (q
1
, [][][]$,SZ
0
)
├ (q
1
, [][][]$,T$Z
0
)
├ (q
1
, [][][]$,T[T]$Z
0
)
├ (q
1
, [][][]$,T[T][T]$Z
0
)
├ (q
1
, [][][]$,T[T][T][T]$Z
0
)
Trong bốn cấu hình được liệt kê, ký tự vào kế tiếp là [ và trên đỉnh ngăn xếp
là T, nhưng tuần tự chuyển bắt đầu của bốn điểm là khác nhau. Từ đó tất các băng
vào còn lại rất giống nhau ở tất cả các cấu hình, nhìn vào phía trên của ký tự vào kế
tiếp, thậm chí trên phía trước, sẽ không có sự trợ giúp nào; không có sự lựa chọn
cơ bản nào cho bước chuyển kế tiếp của băng vào.
Một vấn đề xuất hiện ở đây là, bởi vì luật sinh T → T[T], luật sinh này xuất
hiện đệ quy trái. Bởi vì phía bên phải bắt đầu bằng T, PDA phải chứa một số bước
chuyển nhất định trước khi nó làm bất cứ gì khác, và nhìn vào băng vào và nó có
quyết định như thế nào. Trong trường hợp này chúng ta có thể loại trừ đệ quy trái
bằng cách thay đổi ngữ pháp, giả sử ta có T- luật sinh tổng quát cho một văn phạm:
T → T α | β
ở đây chuỗi β không được bắt đầu bằng T, cho phép tất cả các chuỗi βα
n
, cho n ≥
0, nếu ở đây ta có hai luật sinh được thay như sau:
T → β U
U → αU | A
ngôn ngữ không bị thay đổi và vấn đề đệ quy trái đã được loại bỏ, Ví dụ với, α =
[T] và β = A, chúng ta thay thế
T → T [T] | A
bởi T → U
U → [T] U | A
và kết quả cho phép văn phạm xây dựng nhiều PDA đơn định như ví dụ 7.8
Cho văn phạm phi ngữ cảnh có các luật sinh như sau:
S → T $
T → [T] | [] T | [T] T | []
Trang 13
Điều này thu được không rõ ràng từ một ví dụ 7.8 bởi loại bỏ một luật sinh T
→ [T] | A; Ngôn ngữ không thay đổi chỉ có điều nó không còn chứa chuỗi $.
Mặc dù ở đây không có đệ quy trái trong CFG, chúng ta có thể nói rằng
ngay sau khi biết ký tự tiếp theo đưa vào sẽ không có lựa chọn cho PDA không
đơn định chuyển bước tiếp theo khi T ở trên đỉnh của ngăn xếp. Vấn đề ở đây cho
bốn luật sinh T bắt đầu với các ký tự giống nhau, Một giải pháp thích hợp là phân
tích chúng thành bên phải như ở dưới đây:
T → [U U → T] | ] T | T] T | ]
Phân tích được thành nhiều luật sinh là rất cần thiết bởi vì luật sinh U; trong
trường hợp này ở bên phải bắt đầu bằng T, chúng ta có thể phân tích T]. Chúng
chứa luật sinh:
S → T $ T → [U
U → T ] W | ] W W → T | A
Chúng ta đơn giản hoá bằng cách loại trừ biến T, và chúng chứa
S → [U $ U → [U] W | ] W W → [U | A
DPDA kết hợp nhìn vào trạng thái đầu được miêu tả bảng 7.10 ở dưới đây:
1 q
0
A Z
0
(q
1
,SZ
0
)
2 q
1
A S (q
1
,[U$)
3 q
1
[ U (q
[
,[U]W)
4 q
[
A [ (q
1
,A)
5 q
1
] U (q
[
,]W)
6 q
]
A ] (q
1
,A)
7 q
1
[ W (q
[
,[U)
8 q
1
[ W (q
]
,A)
9 q
1
$ W (q
$
,A)
10 q
$
A $ (q
1
,A)
11 q
1
[ [ (q
1
,A)
12 q
1
] ] (q
1
,A)
13 q
1
$ $ (q
1
,A)
14 q
1
A Z
0
(q
2
,A)
all other combinations none
Trong ví đụ 7.9 và 7.10, chúng ta đã phân tích và loại trừ đệ quy trái biến đổi
CFG gọi là một văn phạm LL(1), điều đó có nghĩa là phân tích các luật sinh từ trên
xuông của PDA không đơn định từ văn phạm có thể chuyển sang phân tích từ trên
xuống của PDA đơn định bằng cách nhìn băng vào của ký tự kế tiếp. Một văn
Trang 14
phạm LL(k) nếu nó nhìn vào phía trước k ký tự ở băng vào thì luôn luôn có đủ các
lựa chọn cho bước chuyển kế tiếp của PDA.
Như vậy một văn phạm cho phép xây dựng phân tích câu đơn định từ trên
xuống, và ở đây hệ thống các phương pháp cho đơn định dù CFG là LL(k) và xây
dựng từ bên ngoài (xem chỉ dẫn).
Cho một văn phạm phi ngữ cảnh LL(1), một PDA đơn định một cách hình
thức miêu tả các bước chấp nhân câu bằng cách nhìn vào ký tự vào kế tiếp. Phương
pháp đệ quy lùi cũng là một cách khác. Tên tham chiếu đến tập hợp các thủ tục đệ
quy tương ứng với các biến trong văn phạm.
Phân tích đệ quy lùi cho văn phạm LL(1) trong ví dụ 7.10:
Cho văn phạm phi ngữ cảnh có luật sinh như sau:
S → [ US
U → ] W | [U] W
W → [ U | A
Chúng ta có một phiên bản C++ dùng phân tích đệ quy lùi. Thuật ngữ đoán
nhận câu chính xác hơn phân tích, mặt dù nó không khó thêm vào các câu lệnh cho
một chương trình, điều đó cho phép xây dựng lại nguồn gốc nhận biết xâu.
Chương trình bao gồm các hàm s, u và w tương ứng với ba biến. Gọi tương
ứng ba hàm tương ứng thay thế các biến trong suốt quá trình xử lý. hoặc thay thế
các biến trên ngăn xếp trong quá trình thực hiện PDA. Ở đây có một biến cục bộ
curr_ch giá trị của nó được tạo trước khi ba hàm trên được gọi. Nếu ký tự hiện tại
là trong số hàm chờ đợi., Thì nó phù hợp thì các hàm vào được gọi và đọc và nhảy
đến ký tự kế tiếp, ngoài ra nếu hàm quản lý lỗi được gọi và nó hiển thị ký tự nào đã
vào; trong trường hợp này chương trình được ngắt và hiển thị thông báo lỗi.
Chú ý chương trình đúng phụ thuộc trên văn phạm LL(1), cho đến khi mỗi
hàm có thể lựa chọn hành động đúng trên cơ sở ký tự vào hiện tại.
#include <iostream.h>
#include <stdlib.h>
char curr_ch; //Ký tụ hiện tại
void s(), u(), w(); //khai báo s, u, w
void match(char);
void get_ch();
void error(char*); // hiển thị lỗi và bỏ qua. Tham số là chuỗi
void error(char); // Hiển thị lỗi và bỏ qua. Tham số là ký tự
void main()
{
get_ch(); s();
cout <<end1<< "Phân tích hoàn thành"
Trang 15
<<"Câu trên là thuộc ngôn ngữ"<<end1;
}
void s() // nhận biết [U$
{
match('['); u(); match('$');
}
void u() // nhận biết ]W|[U]W
{
switch (curr_ch)
{
case ']': match(']'); w(); break;
case '[':
match('['); u(); match(']'); w(); break;
default: error("[ or ]");
}
}
void w()
{
if (curr_ch=='[') {match ('['); u();}
}
void get_ch()
{
if (cin >>curr_ch) cout<<curr_ch;
if (cin.eof() && curr_ch !='$')
{ cout << "Kết thúc"; error("[ or ]");}
}
void match(char this_ch)
{ if (curr_ch==this_ch) get_ch(); else error(this_ch);}
void error( char* some_chars)
{ cout <<"\n Lỗi:"<<some_chars<<"\n";
exit(0);
}
void error( char a_char)
{
cout <<"\n Lỗi:"<<a_char<<"\n";
exit(0);
}
Chương trình không được đầy đủ nó chỉ quan tâm vài chi tiết cụ thể. Trong
trường hợp một chuỗi không thuộc ngôn ngữ, nó đọc và chỉ hiển thị ký tự không
hợp lệ đầu tiên, đó là điểm được DPDA khai thác, ngoài ra nó không đọc được $;
Trang 16
nếu chuỗi ký tự vào là [] $ . Ví dụ, chương trình chỉ đơn thuần hiển thị các ký tự []
$] trong ngôn ngữ. Cuối cùng các thông báo lỗi có thể là vấn đề nhỏ, Có hai lỗi
trong ví dụ này là tìm ra trong hàm s, sau khi được trả về khi gọi hàm u. Luật sinh
là S → [U S; hàm mà ta mong đợi thấy ký tự $ ở điểm này, mặc dù ký tự không
khác với $ sẽ đưa ra thông báo lỗi. Ký tự [ sẽ hợp lệ ở đây và sẽ có kết quả là trình
tự các hàm gọi khác nhau. Sao cho chương trình sẽ không phải thực hiện điều này
ở trong s.
2.2 Phân tích cú pháp từ dưới lên:
Phân tích cú pháp đơn định từ dưới lên cho một CFG:
Cho G là một văn phạm phi ngữ cảnh với các luật sinh như sau:
(0) S → S
t
$
(1) S
t
→ S
t
+ T
(2) S
t
→ T
(3) T → T * a
(4) T → a
Về bản chất có bốn luật sinh cuối; ký tự $ ở cuối của luật (0) rất có hữu ích ở đây,
giống như nó là vấn đề được bàn luận ở phần phân tích cú pháp từ trên xuống.
Trạng thái Băng vào Ngăn xếp Dịch chuyển (s)
Dịch chuyển ( σ và X là bất kỳ)
Q σ X (q, σ, X)
Dịch chuyển để biến đổi S
1
S theo S
Q A S (q
0,1
, A)
q
0,1
A S
1
(q, S)
Dịch chuyển để biến đổi S
1
+ T theo S
1
Q A T (q
1,1
, A)
q
1,1
A + (q
1,2
, A)
q
1,2
A S
1
(q, S
1
)
Dịch chuyển để biến đổi T theo S
1
Q A T (q, S
1
)
Dịch chuyển để biến đổi T * a theo T
Q A a (q
3,1
, A)
q
3,1
A * (q
3,2
, A)
q
3,2
A T (q, T)
Dịch chuyển để biến đổi a theo T
Q A a (q, T)
Dịch chuyển và chấp nhận
Q A S (q
1
, T)
Tất cả các sự kết hợp khác
Trang 17
Hình vẽ 7.11 trình bày PDA không đơn định ở ví dụ 7.6 và giảm bớt luật (0).
Điểm khác rất nhỏ từ PDA ở ví dụ 7.6 bởi vì ký tự bắt đầu bằng $ chỉ xuất hiện ở
luật (0) ở văn phạm, PDA có thể chuyển chấp nhận trạng thái như $ ở trên đỉnh
ngăn xếp.
PDA không đơn định được trình bày hai cách. Thứ nhất có thể lựa chọn ký
tự vào ở trên ngăn xếp hoặc làm giảm bớt chuỗi ở trên ngăn xếp. Ví dụ, nếu T là ký
tự trên ngăn xếp, thứ nhất lựa chọn là đúng nếu phía phải T là T * a, và thứ hai nếu
T là một trong các luật sinh S
t
. Ví dụ, chúng ta có hai luật sinh mà phía bên phải
tận cùng là a, Câu trả lời cho vấn đề thứ hai là dễ dàng. Khi chúng lấy a từ ngăn
xếp, nếu chúng tìm thấy * ở dưới nó, thì sẽ biến đổi T *a thành T, và ngoài ra,
chúng biến đỗi a thành T, trong trường hợp này chúng sẽ làm giảm đi độ dài của
các câu.
Trở lại với câu hỏi thứ nhất, cho trên đỉnh ngăn xếp là T, và cho các khả
năng của ký tự vào. Nêu nó là +, chúng ta có chuỗi S
1
+ T, đảo ngược thứ tự trên
đỉnh ngăn xếp, và trạng thái chuyển đúng của điểm này là biến đổi T hoặc S
1
+ T
( phụ thuộc vào bên dưới của ngăn xếp) thành T. Nếu ký tự kế tiếp là * thì sẽ thay
thế T * a thành a, cho đến khi T đã hoàn thành. Cuối cùng, nếu nó là $ chúng sẽ
thay thế T hoặc S
1
+ T thành T cho phép biến đổi S
1
$. Trong bất kỳ trường hợp
chúng có thể quyết định các ký tự cơ bản của băng vào kế tiếp. Điều này đúng cho
ví dụ này điều đó có sự kết hợp chắc chắn các ký tự trên đỉnh ngăn xếp và ký tự
vào thì sự biến đổi luôn luôn phù hợp, một sự thay đổi đúng cho tất cả sự kết hợp
khác. Các bộ cặp đôi được thay đổi phù hợp là một ví dụ của quan hệ thứ tự. (nó là
quan hệ từ Γ đến ∑ ở phần 1.4). Một số loại văn phạm ưu tiên, quan hệ ưu tiên có
thể được sử dụng
Một PDA đơn định hoạt động như phân văn phạm như bảng 7.12. Khác với
PDA không đơn định, chúng ta thực hiện một vài quan sát. Bộ ký tự trong ngăn
xếp có thể được chia làm 3 nhóm: (1) ký tự yêu cầu trên ngăn xếp không quan tâm
đến ký tự vào kế tiếp là gì (chúng như Z
0
, S
1
, + và *); (2) yêu cầu biến đổi hoặc đi
đến chấp nhận (a, $, và S); và (3) T chỉ có lựa chọn đúng chỉ khi có thể tham khảo
đầu vào kết tiếp. Ở trong DPDA, Việc dịch chuyển ký tự trên đỉnh ngăn xếp có hai
loại có thể bỏ qua. vì chúng không chấp nhận bất kỳ xâu nào và sẽ giới thiệu ở
phần không đơn định. Các ký tự được chuyển đầu tiên trong ngăn xếp sẽ được gán
nhãn dịch chuyển. Nếu ký tự trên đỉnh ngăn xếp thuộc loại hai, những bước chuyển
gọi tắt là bước chuyển A. Nếu PDA đọc một ký tự và quyết định giảm bớt, ký tự
vào sẽ dần dần đưa lên ngăn xếp, quá trình biến đổi hoàn thành ( máy phải nhớ ký
tự vào trong suốt quá trình thay thế); quá trình dịch chuyển cho thấy không phải ở
dưới là "quá trình dịch chuyển" nhưng phần bên dưới bảng, như quá trình dịch
chuyển biến đổi tuần tự có thể thu được hữu hạn các bước chuyển dịch trong quá
trình phân tích.
Bảng 7.12
Trang 18
Bước chuyển Trạng thái Băng vào Ngăn xếp Dịch chuyển
Dịch chuyển
1 q σ X (q, σX)
(σ là bất kỳ; X là Z
0
, S
1
, +, hoặc *)
2 q σ T (q, σT)
(σ là ký tự vào bất kỳ khác + hoặc $)
Dịch chuyển biến đổi S
1
$ theo $
3 q A S (q
$
, A)
4 q
$
A S
1
(q, S)
Dịch chuyển biến đổi a hoặc T * a theo T
5 q A a (q
a,1
,A)
6 q
a,1
A * (q
a,2
,A)
7 q
a,2
A T (q, T)
8 q
a,1
A X (q, TX)
X là bất kỳ trong ngăn xếp khác *
Dịch chuyển biến đổi S
1
+ T hoặc T và chuyển sang ký băng vào
9 q σ T (q
T,σ
, A)
10 q
T,σ
A + (q
T,σ
, A)
11 q
T,σ
A S
1
(q, σS
1
)
12 q
T,σ
A X (q, σS
1
X)
σ là + hoặc $; X là ký tự trên ngăn xếp bất kỳ khác +
Dịch chuyển và chấp nhận
13 q A S (q
1
, A)
Các trường hợp khác
Trạng thái dịch chuyển PDA cho xâu vào a+a* a$
(q, a + a * a$, Z
0
) ├(q , +a*a$, aZ
0
) (Chuyển 1)
├(q
a,1
, +a*a$, aZ
0
)
├(q , +a*a$, TZ
0
)
├(q
T,+
, a*a$,Z
0
)
├(q , a*a$, +S
1
Z
0
)
├(q, *a$, a+S
1
Z
0
)
├(q
a,1
, *a$, +S
1
Z
0
)
├(q, *a$, T+S
1
Z
0
)
├(q, a$, *T+S
1
Z
0
)
├(q
a,1
, $, T+S
1
Z
0
)
├(q, $, T+S
1
Z
0
)
├(q
T,$
, A, +S
1
Z
0
)
├(q
T,$
, A, S
1
Z
0
)
├(q , A ,$S
1
Z
0
)
├(q
$
, A ,S
1
Z
0
)
Trang 19
├(q , A ,SZ
0
)
├(q
1
, A ,Z
0
)
chấp nhận.
Trang 20
Phần 2. BÀI TẬP
Đề số 12: Cho
},,{ cba=Σ
và một số nguyên n.
Hãy viết một chương trình trong một ngôn ngữ tùy chọn để liệt kê:
- Dãy n câu đầu tiên trên bảng chữ
Σ
(liệt kê theo độ dài câu tăng dần, theo thứ tự
a, b, c) ?
- Dãy n cặp câu có độ dài tùy ý trên bảng chữ
Σ
?
- Dãy tùy ý các câu trên bảng chữ
Σ
có độ dài n?
BÀI LÀM
Chương trình sử dụng ngôn ngữ c#.
1. thuật toán sinh kế tiếp của chuỗi s:
Đầu vào là chuỗi
}, ,,{
21 n
sssS =
, với
Σ∈
i
s
Đầu ra là chuỗi kế tiếp của chuỗi S’ (theo độ dài câu tăng dần, theo thứ tự a, b, c)
Vt=n;
While(VT>0)
{
If s
vt
là ký tự a hoặc ký tư b thì thoát khỏi vòng lặp while
Ngược lại vt=vt-1
}
If(vt>0)
{
If s
vt
là ký tự a thì gán lại bằng ký tự b
Ngược lại thì gán s
vt
là ký tự c
Gán s
i
bằng ký tự a với i= (vt+1, n)
}
Ngược lại S=
1
+n
aaa
2. Sinh dãy n câu đầu tiên:
S
1
=”a”;
For i=2 to n
S
i
= chuỗi kế tiếp của S
i-1
3. Dãy n cặp câu có độ dài tùy ý:
Q={}; // Dùng để lưu trữ các chuỗi ký tự theo yêu cầu
I=0;
While (i<n)
{
Sinh ngẫu nhiên một độ dài y
Nếu độ dài này chưa được sinh thì
Sinh cặp câu có độ dài y giả sử là S
SQQ ∪=
i=i+1
Trang 21
}
Return Q;
4. Dãy tùy ý các câu trên bảng chữ
Σ
:
Q={}; // dùng để lưu trữ các chuỗi theo yêu cầu
I=0;
While(i<n)
{
Sinh một chuỗi có độ dài tùy ý S
If (
QS ∉
) thì
{
SQQ ∪=
I=i+1
}
}
Return Q;
KẾT QUẢ THỰC HIỆN CHƯƠNG TRÌNH VỚI N=20
Trang 22
CHƯƠNG TRÌNH NGUỒN
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.Collections;
namespace WindowsApplication1
Trang 23
{
public partial class frmBT : Form
{
static char[] ch ={ 'a', 'b', 'c' };
public frmBT()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
//StringS(4);
}
//them
/* static void Main(string[] args)
{
//DoubleString(20);
//System.Console.WriteLine(radomString(2) + "&&" + radomString(2));
StringS(20);
//NSentenceStart(100);
System.Console.ReadLine();
}*/
//sinh mot chuoi ky tu ke tiep theo nguyen tat tim ky dau tien ben phai de thay the
// thay ky tu do thanh ky tu tiep theo va chuyen cac ky tu con lai thanh ky tu nho
nhat
int myRandom(int n)
{
return (DateTime.Now.Millisecond + DateTime.Now.Second * 60 +
DateTime.Now.Minute * 3600 + DateTime.Now.Month * 10000) % n;
}
String nextString(String st)
{
String kq = "";
int n = st.Length-1,k=-1;
while ((n >= 0 )&& (k == -1))
{
if ((st[n] =='a') || (st[n] == 'b')) k = n;
else n ;
}
if (k > -1)
{
int i;
Trang 24
for (i = 0; i < k; i++) kq = kq + st[i];
if (st[k] == 'a') kq =kq+ 'b';
else kq = kq + 'c';
for (i = k + 1; i < st.Length; i++) kq =kq+ 'a';
}
else
{
for (int i = 0; i < st.Length; i++) kq = kq+'a';
kq=kq+'a';
}
return kq;
}
// sinh mot chuoi ky tu theo do dai n
String radomString(int n)
{
String kq = "";
for (int i = 0; i < n; i++)
kq = kq + ch[myRandom(3+i)%3];
return kq;
}
// day n cap cau co do dai tuy y tren bang chu cai
void DoubleString(int n)
{
bool[] check=new bool[2*n];
String st1, st2;
int i;
for(i=0;i<2*n;i++)check[i]=false;
check[0] = true;
i = 0;
while (i < n)
{
// lay mot do dai tuy y
int len = myRandom(2 * n);
while (check[len]) len = myRandom(2 * n);
check[len] = true;
// sinh mot cap chuoi co do dai tuy y
st1 = radomString(len);
st2 = radomString(len);
while (st2 == st1) st2 = radomString(len);
//System.Console.WriteLine(st1 + "&&" + st2);
this.lstKetqua.Items.Add(""+(i+1)+": "+st1 + " & " + st2);
i++;
}
Trang 25