Лемма о накачке для регулярных языков

Как доказать, что язык НЕ регулярный? Главный инструмент — лемма о накачке: у длинных строк регулярного языка есть повторяющийся участок, который можно «качать».

Лемма о накачке: для любого регулярного языка L существует число p (длина накачки) такое, что любую строку s ∈ L длиной ≥ p можно разбить s = xyz, где |y| > 0, |xy| ≤ p, и xyiz ∈ L для всех i ≥ 0.

Откуда берётся накачка

Интуиция проста и опирается на конечность ДКА. Пусть L распознаётся ДКА с p состояниями. Возьмём строку длиной ≥ p. Читая её, автомат проходит ≥ p+1 состояний (считая стартовое). Состояний всего p, значит по принципу Дирихле (принципу ящиков) какое-то состояние повторится! Между двумя визитами в это состояние автомат прочитал непустой кусок y — он образует цикл в диаграмме. А раз есть цикл, по нему можно пройти 0 раз (выкинуть y), 1 раз (исходная строка), 2 раза, 10 раз — и автомат всё равно окажется в том же состоянии. Значит все строки xyiz тоже принимаются.

строка длиной ≥ p проходит цикл по повторённому состоянию q:

  q0 --x--> (q) --y--> (q) --z--> принимающее
               └──┘
             цикл y можно повторять: xz, xyz, xyyz, ... ∈ L

Как доказывают НЕ-регулярность

Лемма — необходимое условие регулярности. Поэтому её используют «от противного»: предположим, что L регулярен. Тогда по лемме есть p. Мы как «противник» выбираем конкретную хитрую строку s ∈ L длиной ≥ p. Лемма обещает разбиение s = xyz с |xy| ≤ p и |y| > 0. Мы показываем, что при любом таком разбиении найдётся i, для которого xyiz ∉ L. Противоречие — значит L не регулярен.

Классика: L = {anbn | n ≥ 0} не регулярен. Берём s = apbp. Условие |xy| ≤ p означает, что x и y состоят только из символов a (первые p символов — это все a). Значит y = ak, k > 0. Накачаем i = 2: xy2z = ap+kbp. Теперь a больше, чем b — строки нет в L. Противоречие. L не регулярен. Конечная память ДКА не может «сосчитать» n.

Демонстрация: цикл реально качается

Покажем механику накачки программно. Возьмём регулярный язык ab*c (a, потом сколько-то b, потом c) — у него y = «b» в цикле, и накачка остаётся в языке. А для anbn накачка вылетает.

import re
regular = re.compile(r"^ab*c$")     # регулярный язык

# s = abc, разбиение x='a', y='b', z='c'
x, y, z = "a", "b", "c"
for i in range(4):
    s = x + y*i + z
    print(f"i={i}: {s:>6} в языке ab*c? {bool(regular.match(s))}")

print("---")
# а вот a^n b^n: накачка ломает баланс
def is_anbn(s):
    n = len(s) // 2
    return s == "a"*n + "b"*n and len(s) % 2 == 0

x, y, z = "aa", "a", "bbb"   # s=aaabbb, y из зоны a
for i in range(3):
    s = x + y*i + z
    print(f"i={i}: {s:>7} в a^n b^n? {is_anbn(s)}")

Вывод:

i=0:     ac в языке ab*c? True
i=1:    abc в языке ab*c? True
i=2:   abbc в языке ab*c? True
i=3:  abbbc в языке ab*c? True
---
i=0:   aabbb в a^n b^n? False
i=1:  aaabbb в a^n b^n? True
i=2: aaaabbb в a^n b^n? False

В регулярном ab*c накачка любого i остаётся в языке. В anbn уже i=0 (и i=2) выбрасывают строку наружу — наглядное противоречие с леммой.

Как работает под капотом

Глубинная причина — принцип Дирихле плюс конечность состояний. Любой автомат с p состояниями на строке длиннее p обязан зациклиться. Лемма — формализация этого факта. Заметьте: лемма даёт необходимое, но не достаточное условие — есть нерегулярные языки, проходящие лемму. Для них применяют усиленные инструменты (лемма Майхилла-Нероуда). Но для большинства учебных задач «накачать и сломать» — рабочий метод.

Частые ошибки

Выбирать разбиение за лемму. Разбиение xyz выбирает «противник» (лемма), а вы должны сломать все допустимые разбиения. Зато строку s выбираете вы — берите самую неудобную.

Забыть условие |xy| ≤ p. Именно оно фиксирует, что y попадает в начальную зону (в anbn — в зону a). Без него доказательство разваливается.

Думать, что лемма доказывает регулярность. Нет, только не-регулярность. Прохождение леммы не гарантирует регулярность.

Итог

  • Лемма о накачке: у регулярного языка есть p, и длинные строки разбиваются s = xyz с накачиваемым y (|y| > 0, |xy| ≤ p).
  • Причина — повтор состояния (принцип Дирихле) образует цикл, который можно проходить любое число раз.
  • Не-регулярность доказывают от противного: выбираем хитрую s и ломаем все разбиения накачкой.
  • Пример: anbn не регулярен — конечная память не считает n; лемма — необходимое, но не достаточное условие.
Проверьте себя
1. Что доказывает лемма о накачке?
Aчто язык регулярный
Bчто язык НЕ регулярный (от противного)
Cчто язык контекстно-свободный
Dчто автомат детерминированный
2. На каком принципе основана лемма о накачке?
Aна индукции по длине строки
Bна принципе Дирихле: в автомате с p состояниями длинная строка вызывает повтор состояния (цикл)
Cна теореме Клини
Dна построении подмножеств
3. Кто выбирает разбиение s = xyz, а кто строку s в доказательстве не-регулярности?
Aоба выбирает доказывающий
Bразбиение даёт лемма (противник), строку выбирает доказывающий
Cоба выбирает лемма
Dстроку выбирает лемма, разбиение — доказывающий