Как работает алгоритм CYK?

Я должен проверить, может ли строка быть получена из данного контекста без контекста, который находится в нормальной форме Хомского. Я использую С++.

В статье Википедии, посвященной алгоритму CYK, есть очень хороший псевдокод, но я не могу я очень хорошо это понимаю.

Будет ли кто-нибудь так любезен помочь мне, предоставив мне другой псевдокод для алгоритма CYK, или, может быть, объяснит тот, что в статье на вики?


person neojb1989    schedule 05.12.2012    source источник
comment
Как бы мне ни нравилась Википедия, это не всегда самый читаемый источник. Для получения технической информации для непосвященных обычно лучше искать альтернативные источники. Вы гуглили другие места для CYK?   -  person RonaldBarzell    schedule 05.12.2012
comment
я выполнил поиск в Google, но я либо нахожу фактический код, сделанный кем-то на уровне, который я не могу понять, либо я нахожу алгоритм для выполнения этого вручную, который у меня было время даже начать кодировать .   -  person neojb1989    schedule 05.12.2012
comment
Да, многие ссылки не очень читаемы. Если вы хотите с ней ознакомиться, есть демонстрация: diotavelli.net/people. /void/demos/cky.html. Кроме того, вот ряд слайдов, которые кажутся более удобочитаемыми: cs .ucdavis.edu/~rogaway/classes/120/winter12/CYK.pdf. Наконец, вот реализация на C++: nitishkr.wordpress.com/2011/ 29 марта/cyk-алгоритм-реализация   -  person RonaldBarzell    schedule 05.12.2012
comment
Хорошо, большое спасибо!   -  person neojb1989    schedule 05.12.2012
comment
Я хотел бы добавить, что это все, что я нашел ранее, но я перечитал его, так как вы тоже его нашли. Хотя у меня не лучше :/   -  person neojb1989    schedule 06.12.2012
comment
Жаль это слышать... Я думаю, что понимаю алгоритм CYK, проблема в том, что его не обязательно легко объяснить, поскольку может потребоваться использование какой-то другой терминологии, которая не обязательно является CS. Если вы хотите обсудить это в чате, возможно, я могу помочь, но это не подходит для ответа, так как это потребует переписки.   -  person RonaldBarzell    schedule 06.12.2012
comment
На самом деле я понял это! После просмотра этой демонстрации снова и снова я придумал циклы for, которые имитируют этот шаблон проверки, и все это прекрасно совпало :)   -  person neojb1989    schedule 07.12.2012
comment
Чудесно! Я рад это слышать.   -  person RonaldBarzell    schedule 07.12.2012


Ответы (1)


Алгоритм CYK принимает на вход CFG в нормальной форме Хомского. Это означает, что всякая продукция либо имеет вид

  • S a, для некоторого терминала a, или
  • S AB для некоторых нетерминалов A и B.

Теперь представьте, что у вас есть строка w, и вы хотите узнать, можно ли вывести ее из грамматики, начальным символом которой является S. Есть два варианта:

  1. Если w состоит из одного символа, то единственный способ разобрать его — использовать произведение формы S a для некоторого символа a. Итак, посмотрите, будет ли какое-либо из односимвольных произведений соответствовать a.
  2. Если w имеет длину более одного символа, то единственный способ разобрать его — использовать произведение вида S AB для некоторых нетерминалов A и B. Это означает, что нам нужно разделить строку w на две части x и y, где A выводит x, а B выводит y. Один из способов сделать это — попробовать все возможные способы разбиения w на две части и посмотреть, работает ли какой-либо из них.

Обратите внимание, что вариант (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;
}

Этот алгоритм работает правильно, но если вы наметите форму рекурсии, вы обнаружите, что

  1. он делает массу избыточных рекурсивных вызовов, но
  2. существует не так много различных возможных рекурсивных вызовов.

На самом деле количество различных возможных вызовов равно O(n2 N), где n — длина входной строки (для каждой возможной комбинации начального и конечного индексов), а N — количество нетерминалов в грамматике. Эти наблюдения показывают, что этот алгоритм выиграл бы либо от мемоизации, либо от динамического программирования, в зависимости от того, какой подход вы считаете более приятным.

Алгоритм CYK — это то, что вы получаете, когда берете приведенный выше рекурсивный алгоритм и запоминаете результат, или, что то же самое, когда вы преобразуете приведенный выше рекурсивный алгоритм в задачу динамического программирования.

Существует O(n2 N) возможных рекурсивных вызовов. Для каждой проверенной продукции это работает O (n). Если для нетерминала в среднем имеется P продуктов, это означает, что общее время выполнения составляет O(n3 NP), то есть O(n3) для фиксированного грамматика.

person templatetypedef    schedule 19.07.2017
comment
Спасибо за ваше отличное объяснение, просто интересно, это дизайн снизу вверх или сверху вниз, так как я немного запутался? - person SMH; 19.09.2017
comment
И то, и другое понемногу. Если вы думаете об этом как об алгоритме динамического программирования, то это восходящий подход, который определяет все возможные произведения, которые могут дать каждую из различных подстрок в порядке возрастания длины. Если вы думаете об этом как о подходе к запоминанию, то это сверху вниз. Многие алгоритмы синтаксического анализа имеют ощущение смешивания и сопоставления. - person templatetypedef; 19.09.2017
comment
Спасибо за ответ, псевдокод здесь en.wikipedia.org/wiki/CYK_algorithm#As_pseudocode это подход «снизу вверх», и мне интересно, что я должен изменить, чтобы сделать его дизайном «сверху вниз»? - person SMH; 19.09.2017
comment
Псевдокод, который я дал в своем ответе, является хорошим шаблоном для того, как вы будете реализовывать вещи сверху вниз. Просто добавьте запоминание, и все готово! (Кроме того, в то время как CYK обычно преподают как хороший общий алгоритм синтаксического анализа, алгоритм Эрли намного более гибкий — ему не нужно, чтобы что-то было в CNF — и на практике может быть намного быстрее. Один из моих ТА реализовал его как часть инструмента автоградации CFG, и мы добились большого успеха с ним.) - person templatetypedef; 19.09.2017
comment
Спасибо, я добавлю запоминание к вашему ответу и добавлю новый вопрос с моим новым кодом, поэтому было бы здорово, если бы вы могли его проверить, хорошо? - person SMH; 19.09.2017
comment
Я бы предпочел, чтобы вы оставили этот ответ без изменений или позволили мне попробовать его, но не стесняйтесь задавать свой вопрос! - person templatetypedef; 19.09.2017
comment
Было бы здорово, если бы вы могли обновить его, добавив запоминание, так как новый вопрос будет почти таким же. - person SMH; 19.09.2017