Я должен проверить, может ли строка быть получена из заданного свободного контекста, который находится в нормальной форме Хомского. Я использую C ++.
Там очень красиво псевдокод в статье в Википедии, посвященной алгоритму CYK, но я не очень хорошо понимаю его.
Будет ли кто-то так любезен, чтобы помочь мне, предоставив мне другой псевдокод для алгоритма CYK, или, может быть, объяснить, что в статье вики?
Алгоритм CYK принимает в качестве входных данных CFG в нормальной форме Хомского. Это означает, что каждое производство имеет форму
Теперь представьте, что у вас есть строка w, и вы хотите узнать, можете ли вы извлечь ее из грамматики, начальный символ которой — S. Есть два варианта:
Обратите внимание, что здесь опция (2) оказывается проблемой рекурсивного разбора: чтобы узнать, можете ли вы вывести w из S, посмотрите, можете ли вы вывести x из A и y из B.
С этим пониманием, вот псевдокод для рекурсивной функции, которую вы можете использовать, чтобы увидеть, выводит ли нетерминал S строку w:
bool canDerive(nonterminal S, string w) {
return canDeriveRec(S, w, 0, w.size());
}
/* Can you derive the substring [start, end) of w from S? */
bool canDeriveRec(nonterminal S, string w, int start, int end) {
/* Base case: Single characters */
if (end - start == 1) {
return whether there is a production S -> a, where a = w[start];
}
/* Recursive case: Try all possible splits */
for (each production S -> AB) {
for (int mid = start + 1; mid < end; mid++) {
if (canDeriveRec(A, w, start, mid) &&
canDeriveRec(B, w, mid, end)) {
return true;
}
}
}
return false;
}
Этот алгоритм работает правильно, но если вы наметите форму рекурсии, вы обнаружите, что
На самом деле количество возможных вызовов составляет O (n2 N), где n — длина входной строки (для каждой возможной комбинации начального и конечного индексов), а N — количество нетерминалов в грамматике. Эти наблюдения показывают, что этот алгоритм выиграл бы либо от запоминания, либо от динамического программирования, в зависимости от того, какой подход вам кажется более приятным.
Алгоритм CYK — это то, что вы получаете, когда вы берете вышеупомянутый рекурсивный алгоритм и запоминаете результат, или эквивалентно, когда вы конвертируете вышеупомянутый рекурсивный алгоритм в задачу динамического программирования.
Есть O (n2 N) возможные рекурсивные вызовы. Для каждого пробного производства это делает O (n) работу. Если для нетерминала имеется в среднем P производств, это означает, что общее время выполнения равно O (n3 NP), который является O (n3) для фиксированной грамматики.
Других решений пока нет …