📜  Cocke–Younger–Kasami (CYK) 算法

📅  最后修改于: 2021-09-22 10:14:45             🧑  作者: Mango

语法表示自然语言会话的句法规则。但在形式语言理论中,语法被定义为一组可以生成字符串的规则。可以从文法生成的所有字符串的集合称为文法的语言。

上下文无关语法:
我们得到一个上下文无关文法 G = (V, X, R, S) 和一个字符串w,其中:

  • V是一组有限的变量或非终结符,
  • X是一个有限的终结符号集,
  • R是一组有限的规则,
  • S是起始符号,是V 的一个不同元素,并且
  • 假设VX是不相交的集合。

成员问题定义为:语法 G 生成语言 L(G)。给定的字符串是 L(G) 的成员吗?

乔姆斯基范式:
如果G 的每个规则都具有以下形式,则上下文无关语法 G 是乔姆斯基范式 (CNF):

  • A –> BC , [ RHS 上最多有两个非终结符 ]
  • A –> a , 或 [ RHS 上的一个终端符号 ]
  • S –> nullstring , [ null 字符串 ]

Cocke-Younger-Kasami 算法
它用于使用动态规划方法解决成员资格问题。该算法基于问题[i, j]的解可以由子问题[i, k]的解和子问题[k, j]的解构成的原理该算法要求语法G为乔姆斯基范式 (CNF)。请注意,任何上下文无关文法都可以系统地转换为 CNF。使用此限制是为了使每个问题只能分为两个子问题而不是更多 – 以限制时间复杂度。

CYK 算法如何工作?

对于长度为N的字符串,构造一个大小为N x N的表T。T[i, j]中的每个单元格都是可以产生从位置 i 到 j 的子串的所有成分的集合。该过程涉及用自底向上解析过程中遇到的子问题的解决方案填充表格。因此,单元格将从左到右、从下到上填充。

 

1

2

3

4

5

1 [1, 1] [1, 2] [1, 3] [1, 4] [1, 5]
2   [2, 2] [2, 3] [2, 4] [2, 5]
3     [3, 3] [3, 4] [3, 5]
4       [4, 4] [4, 5]
5         [5, 5]

在 T[i, j] 中,行号i表示开始索引,列号j表示结束索引。

A \in T[i, j] \text{ if and only if } B \in T[i, k],  C \in T[k, j] \text{ and } A \rightarrow BC \text{ is a rule of G}

如果可以从非终结符 K生成从ij的字母序列,则该算法会考虑字母的每个可能子序列,并将K加到T[i, j] 中。对于长度大于等于 2 的子序列,它将子序列的每个可能划分都考虑为两个部分,并检查是否存在A ? BC语法中的 B 和 C 可以根据T 中已经存在的条目分别生成两个部分。只有当整个字符串与起始符号匹配时,即如果ST[1, n]的成员,语法才能产生句子

考虑乔姆斯基范式的示例语法:

NP   -->  Det | Nom
Nom  -->  AP | Nom
AP  -->  Adv | A
Det  -->  a | an
Adv  -->  very | extremely
AP   -->  heavy | orange | tall
A   -->  heavy | orange | tall | muscular
Nom -->  book | orange | man

现在考虑这句话,“一本非常沉重的橙皮书”:

a(1) very(2) heavy (3) orange(4) book(5)

让我们根据上述规则从左到右、从下到上开始填充表格:

 

1
a

2
very

3
heavy

4
orange

5
book

1
a

Det

NP

NP

2
very

 

Adv

AP

Nom

Nom

3
heavy

   

A, AP

Nom

Nom

4
orange

      Nom, A, AP

Nom

5
book

       

Nom

该表按以下方式填写:

  1. T[1, 1] = {Det} as Det –> a 是语法规则之一。
  2. T[2, 2] = {Adv} as Adv –>very 是语法规则之一。
  3. T[1, 2] = {} 因为没有观察到匹配规则。
  4. T[3, 3] = {A, AP} as A –> very 和 AP –> very 是语法规则。
  5. T[2, 3] = {AP} as AP –> Adv (T[2, 2]) A (T[3, 3]) 是语法规则。
  6. T[1, 3] = {} 因为没有观察到匹配规则。
  7. T[4, 4] = {Nom, A, AP} as Nom –> orange 和 A –> orange 和 AP –> orange 是语法规则。
  8. T[3, 4] = {Nom} as Nom –> AP (T[3, 3]) Nom (T[3, 4]) 是语法规则。
  9. T[2, 4] = {Nom} as Nom –> AP (T[2, 3]) Nom (T[4, 4]) 是语法规则。
  10. T[1, 4] = {NP} as NP –> Det (T[1, 1]) Nom (T[2, 4]) 是语法规则。
  11. T[5, 5] = {Nom} as Nom –> book 是语法规则。
  12. T[4, 5] = {Nom} as Nom –> AP (T[4, 4]) Nom (T[5, 5]) 是语法规则。
  13. T[3, 5] = {Nom} as Nom –> AP (T[3, 3]) Nom (T[4, 5]) 是语法规则。
  14. T[2, 5] = {Nom} as Nom –> AP (T[2, 3]) Nom (T[4, 5]) 是语法规则。
  15. T[1, 5] = {NP} as NP –> Det (T[1, 1]) Nom (T[2, 5]) 是语法规则。

我们看到 T[1][5] 有NP开始符号,这意味着这个短语是语法G语言的成员。

这个短语的解析树看起来像这样:

让我们看另一个示例短语,“一个非常高大的肌肉男”:

a(1) very(2) tall(3) extremely(4) muscular(5) man(6)

我们现在将使用 CYK 算法来查找此字符串是否是语法G的成员

 

1
a

2
very

3
tall

4
extremely

5
muscular

6
man

1
a

Det

NP

2
very

 

Adv

AP

Nom

3
tall

   

AP, A

Nom

4
extremely

     

Adv

AP

Nom

5
muscular

       

A

6
man

         

Nom

我们看到 T[1][6] 有NP开始符号,这意味着这个短语是语法 G 的语言的成员。

下面是上述算法的实现:

Python3
# Python implementation for the
# CYK Algorithm
  
# Non-terminal symbols
non_terminals = ["NP", "Nom", "Det", "AP", 
                  "Adv", "A"]
terminals = ["book", "orange", "man", 
             "tall", "heavy", 
             "very", "muscular"]
  
# Rules of the grammar
R = {
     "NP": [["Det", "Nom"]],
     "Nom": [["AP", "Nom"], ["book"], 
             ["orange"], ["man"]],
     "AP": [["Adv", "A"], ["heavy"], 
            ["orange"], ["tall"]],
     "Det": [["a"]],
     "Adv": [["very"], ["extremely"]],
     "A": [["heavy"], ["orange"], ["tall"], 
           ["muscular"]]
    }
  
# Function to perform the CYK Algorithm
def cykParse(w):
    n = len(w)
      
    # Initialize the table
    T = [[set([]) for j in range(n)] for i in range(n)]
  
    # Filling in the table
    for j in range(0, n):
  
        # Iterate over the rules
        for lhs, rule in R.items():
            for rhs in rule:
                  
                # If a terminal is found
                if len(rhs) == 1 and \
                rhs[0] == w[j]:
                    T[j][j].add(lhs)
  
        for i in range(j, -1, -1):   
               
            # Iterate over the range i to j + 1   
            for k in range(i, j + 1):     
  
                # Iterate over the rules
                for lhs, rule in R.items():
                    for rhs in rule:
                          
                        # If a terminal is found
                        if len(rhs) == 2 and \
                        rhs[0] in T[i][k] and \
                        rhs[1] in T[k + 1][j]:
                            T[i][j].add(lhs)
  
    # If word can be formed by rules 
    # of given grammar
    if len(T[0][n-1]) != 0:
        print("True")
    else:
        print("False")
      
# Driver Code
  
# Given string
w = "a very heavy orange book".split()
  
# Function Call
cykParse(w)


输出:
True

时间复杂度: O(N 3 )
辅助空间: O(N 2 )

如果您希望与专家一起参加现场课程,请参阅DSA 现场工作专业课程学生竞争性编程现场课程