\documentclass[12pt,fleqn,a4paper]{book}
% 
% Packages used:
%
\usepackage[koi8-r]{inputenc}
\usepackage[russian,english]{babel}
\usepackage{amsmath}
\usepackage{amssymb}
\usepackage{amsfonts}
\usepackage{amsthm}
%
% Common customization:
%
\input defs
%
\newcommand{\sNP}{\class{\widetilde{NP}}}
\newcommand{\sP}{\class{\widetilde{P}}}
%
% The document
%
\begin{document}
\selectlanguage{russian}
%
% Lecture title
%
\lecture{2}{Оптимальный алгоритм для $\sNP$-задачи. 
Не $\NP$-полные задачи в $\NP\setminus\P$.
Унарные и редкие языки.}{В. Моргенштерн}
%
\section{Почти оптимальный алгоритм для задачи из
$\sNP$.} Так как решение  любой задачи из
$\sNP$ может быть проверено за полиномиальное
время, то каждая задача из $\sNP$ решается
некоторым алгоритмом. В худшем случае этот алгоритм просто
проверяет все возможные ответы, ограниченные по длине тем
полиномом, который фиксирован для данной задачи. Алгоритм обязательно
остановится и выдаст правильный ответ через конечное время. 
Можно, однако, построить \emph{в явном виде} некоторый ``оптимальный''
алгоритм, который будет решать задачу из $\sNP$ ``почти столь же быстро'',
как и любой другой. (В частности, если $\sP=\sNP$,
то наш алгоритм будет полиномиальным.) 
Именно, для каждого алгоритма $M$ наш ``оптимальный'' алгоритм
будет тратить на входе (достаточно большой) длины $n$ не более $Ct_M(n)+p(n)$ шагов, где
$t_M(n)$ --- время работы алгоритма $M$,
константа $C$ зависит только от задачи и алгоритма $M$ (но не от $n$), 
а $p$ --- полином, фиксированный для всех задач и алгоритмов $M$ сразу.

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

Каждую машину Тьюринга можно закодировать. Значит, их все можно
пронумеровать. 
%Будем рассматривать только машины, которые всегда
%останавливаются. Это не умоляет общности, так как в каждую машину,
%решающую некоторую задачу из $\sNP$, можно встроить
%счетчик, зависящий от длины входа. Он будет останавливать машину,
%когда она исчерпала время, отведенное для данной задачи и, значит,
%показала, что решение не существует. 
Каждую машину при этом слегка модифицируем:
вместо того, чтобы останавливаться, 
машина проверяет, что найденное решение действительно
удовлетворяет условию, и останавливается только
если решение правильное (по определению $\sNP$ это можно
сделать за полиномиальное время).
Итак, у нас есть
последовательность (проверяющих свой результат)
машин Тьюринга $M_1, \ldots ,M_l, \ldots $. Далее,
пусть наша универсальная машина $M$ на шаге с номером
$i=2^l(1+2k)$ моделирует $k$-ый шаг $l$-ой машины Тьюринга в
этом списке. 
% Когда некоторая машина заканчивает работу, мы проверяем решение. Если оно правильное, то мы заканчиваем работу.
Если машина заканчивает работу (стало быть, она нашла правильное решение),
мы тоже заканчиваем работу.
Если нет, то продолжаем моделировать работу оставшихся машин. Легко
проверить, что 
$
%\forall l\,
%\forall k\,
%\forall l'\,
%\forall k' 
%\ 
%(
(l,k)\neq(l',k')\Rightarrow 2^l(1+2k) \neq 2^{l'}(1+2k')
%)
$.
Поэтому алгоритм работает корректно и тратит на решение количество шагов,
отличающееся от времени оптимального алгоритма лишь в полином раз\footnote{%
Достичь заявленной выше оценки нам мешает необходимость передвигать головку
между текущим состоянием памяти для различных моделируемых нами машин;
для RAM-машин удалось бы от этого избавиться.}.

\begin{exercise}
Однако, чтобы уточнить время работы,
необходимо зафиксировать модель вычислений (причем желательно
такую же, как и у моделируемого устройства!): 
заметим, что нам, например, необходимо одновременно поддерживать
память каждой из моделируемых машин Тьюринга. \qed
\end{exercise}

\begin{remark}
\begin{itemize}
\item В случае, когда решений нет, алгоритм зацикливается!
      Поэтому оптимален от только на входах, имеющих решение.
\item Можно подумать, что из этого алгоритма можно сделать
      алгоритм, оптимальный на входах соответствующей 
      задачи распознавания. Но это не так, поскольку
      это требует применения самосводимости ($F$ выполнима $\iff$
      $F[x]$ выполнима или $F[\lnot x]$ выполнима),
      которая заменяет вход другим.
\end{itemize}
\end{remark}

\section{Задачи из $\NP$, 
которые не являются $\NP$-полными,
но и не лежат в $\P$.}

Мы уже знаем, что в классе $\NP$ есть самые трудные ---
$\NP$-полные, и самые простые --- решаемые за полиномиальное время --- 
задачи.
Возникает резонный вопрос, есть ли в $\NP$ промежуточные по
сложности задачи (те, что с одной стороны не лежат в $\P$,
а с другой --- не являются $\NP$-полными). Ответ на этот вопрос
дает следующая теорема.
\begin{theorem}
Если $\P\neq\NP$, то в $\NP\setminus \P$ 
имеются задачи, не являющиеся $\NP$-полными.
\end{theorem}
\begin{proof}
Мы уже знаем, что задача $\SAT$ является $\NP$-полной. 
Сейчас мы немного изменим эту задачу так, чтобы
она больше не была $\NP$-полной, но и не попала в $\P$.

Для начала, каким-либо способом перенумеруем\footnote{Очевидно, 
перечислить машины, которые всегда ``случайно''
останавливаются столь быстро, как нам надо,
мы не сможем. Однако чтобы
перечислить для каждого языка из
$\P$ хотя бы одну принимающую его машину,
достаточно перечислять машины с <<полиномиальным будильником>>.} 
все машины Тьюринга, снабженные
<<полиномиальным будильником>>: считающие свои шаги
и останавливающиеся после $p(n)$ шагов 
(для каждой машины полином $p$ --- свой).
Получим последовательность $M_1, M_2, \ldots$. 
Теперь перенумеруем все полиномиальные сведения по Карпу
(тоже снабженные таким <<будильником>>) --- это тоже машины,
только оракульные.
(Неформально, нас будут интересовать сведения $\SAT$
к нашему языку.) 
Получим другую последовательность $R_1, R_2, \ldots$. 

Далее, пусть строка $x$ кодирует некоторую
булеву формулу. Пусть $S$ --- конкретная машина, которая принимает только
выполнимые формулы (перебирая все наборы значений переменных --- т.е.
работающая экспоненциально долго). 
$S(x)$ обозначает результат работы этой машины на формуле $x$. 
Искомый язык определим так:
$$
\mathcal{K}=\{x\ |\ S(x)=1\ \land\ f(x)\vdots 2\ \},
$$
где функция $f(n)$ вычисляется алгоритмом, описанным ниже. 
Это очень медленно растущая (но 
$\mathop{\longrightarrow}\limits_{n\to\infty} \infty$) функция. 
Она будет пытаться найти полиномиальный алгоритм для $\SAT$
либо сводимость $\SAT \to \mathcal{K}$, и, как только появится
аргумент, при котором ей удастся найти это,
с этого момента окажется константой,
что будет противоречить условию теоремы (но именно это и будет
вытекать из гипотезы $\mathcal{K}\in \P$, равно как и из 
гипотезы, что $\mathcal{K}$ --- $\NP$-полный).

Обозначим $K$ детерминированную машину, выясняющую принадлежность $\mathcal{K}$
(она легко определяется через машину $S$ и 
машину, вычисляющую функцию $f$, и работает экспоненциально долго).

На вход машине Тьюринга, вычисляющей $f$,
подается число $n$ в <<палочковой>> системе счисления: $1^n$. Эта машина
делает $2n$ шагов. %(так что, очевидно, $\mathcal{K}\in\NP$).
За первые $n$ шагов она вычисляет
последовательно $f(0), f(1), \ldots, f(i)$ ---
столько значений, сколько успеет (да, это рекурсивное
задание функции $f$!). (Каждый шаг наша машина сдвигает указатель на ленте,
где у нее записано $1^n$, на одну позицию вправо --- как только вход закончится,
эта фаза прекращается.) Последнее вычисленное значение
обозначим за $k$. 

Вторая фаза вычислений также занимает $n$ шагов,
и будет зависеть от $k$. 
\begin{enumerate}
\item Если $k$ --- четное, то алгоритм запускает машину $M_{k/2}(z)$
последовательно на всех входах в течении $n$ шагов. Если хотя бы
для одного из этих входов $M_{k/2}(z)\neq K(z)$, то возвращаем
$k+1$, иначе возвращаем $k$.
\item Если $k$ --- нечетное, то алгоритм запускает машину
$K(R_{(k-1)/2}(z))$ последовательно на всех входах в течении $n$
шагов. Если хотя бы для одного из этих входов
$K(R_{(k-1)/2}(z))\neq S(z)$, то возвращаем $k+1$, иначе
возвращаем $k$.
\end{enumerate}

Заметим, что все рекурсивные определения корректны, так как за $n$
шагов мы не успеваем добраться до такого $z$, что $K(z)$ еще не
определено.

Ясно, что $\mathcal{K}\in \NP$. Функция $f$ (по определению) 
вычисляется за полиномиальное время, а получив выполняющий набор для формулы $x$
в качестве подсказки, мы с легкостью проверим результат.

Покажем, что $\mathcal{K}\notin \P$. Допустим противное. 
%Пусть $K \in \P$. 
Тогда машина $K$ совпадает с одной из машин нашего списка,
т.е. $K=M_k$ для некоторого $k$. Тогда существует константа $c$ и номер $N_0$,
такой, что для любого $n\geq N_0,\ f(n)=c\leq 2k$. Действительно,
функция $f$ монотонно возрастает,
а достигнув значения
$2k$, алгоритм вычисления $f$ всегда будет попадать в первый случай,
получать равенство на всех значениях $z$ и снова выдавать $2k$ на
выход. Поэтому язык $\mathcal{K}$ совпадает с языком выполнимых формул,
везде, кроме разве что конечного числа слов. Это означает, что
задача выполнимости разрешима за полиномиальное время,
т.е. $\P=\NP$.
Противоречие.

Аналогичные рассуждения в предположении, что $\mathcal{K}$ --- не $\NP$-полон,
показывают, что, начиная с некоторого места, $f$ --- нечетная
константа. Это, в свою очередь, означает, что язык $\mathcal{K}$
конечен. Но в таком случае, конечно, он распознаваем за
полиномиальное время и $\P=\NP$. Противоречие.
\end{proof}

\section{Унарные и редкие языки.}
Теперь мы докажем, что некоторые слишком простые типы языков
не могут быть $\NP$-полными. Конечно, мы предполагаем, что
$\P \neq \NP$.

\begin{definition}
Язык называется унарным, если все его слова состоят из одного и
того же символа. (Например, язык 
$\{\,{\underbrace{0\ldots 0}_p}\,|\, p\in \mathbb{P}\}$.)
\end{definition}

\begin{theorem}\label{th:tally}
Если $\P \neq \NP$, то никакой унарный язык не может быть
$\NP$-трудным по Карпу.
\end{theorem}

\begin{proof}
Пусть $U$ --- $\NP$-трудный унарный язык. Докажем тогда, что
задача выполнимости может быть решена за полиномиальное время.

Заметим, что для любой переменной $x$
формула $f$ выполнима тогда и только тогда, когда
выполнима хотя бы одна из формул $f[x:=true]$ или $f[x:=false]$
(при присваивании дизъюнкции, содержавшие 
подставленное значение $true$ --- оно же $\lnot false$, удаляются,
а из остальных удаляется подставленное значение $false$ ---
оно же $\lnot true$). 
Поэтому выполняющий набор можно найти,
построив дерево поиска. Каждый узел этого дерева содержит две ветви с
двумя возможными подстановками очередной переменной. 

Объем поиска можно
уменьшить, если при поиске в ширину в этом дереве 
на каждом уровне приводить формулы к
каноническому виду и
убирать совпадающие формулы. Раз $U$ --- $\NP$-полна по Карпу, то
существует полиномиальное сведение $g:\SAT\rightarrow U$. При этом
если для формул $f,\ f^\prime$ выполняется $g(f)=g(f^\prime)$, то
$\SAT(f)=\SAT(f^\prime)$ и одну из этих формул можно без риска
убрать из дерева поиска. Но $U$ --- унарный язык 
(пусть $\subseteq \{0^n | n\in\mathbb{N}\cup\{0\}\}$). Поэтому
полиномиальное сведение $g$ порождает лишь полиномиальное
число различных <<разумных>>
образов\footnote{Мы можем не обращать внимание
на те образы, что состоят не только из нулей --- это образы
невыполнимых формул!}
подформул формулы $f$. Значит, начиная с
некоторого места, все уровни дерева поиска содержат число формул,
ограниченное этим полиномом.
Такое дерево можно обойти за полиномиальное число шагов,
что означает, что $\P=\NP$. Противоречие.
\end{proof}

\begin{definition}
Язык $L$ называется редким, если 
для некоторого полинома $p$,
$|\{x\in L\ |\ |x|\leq n\}|\leq p(n)$.
\end{definition}

\begin{theorem}
Если $\P \neq \NP$, то никакой редкий язык не может быть
$\NP$-трудным по Карпу.
\end{theorem}
Доказательство этой теоремы будет приведено на
спец. семинаре.

\begin{remark}
Для сводимости по Куку все гораздо сложнее.
В частности, требуется более сильное предположение,
чем $\P\neq\NP$.
\end{remark}

\begin{definition}
Для любого класса $\mathcal{C}$,
$\co{\mathcal{C}}=\{L\ |\ \overline{L}\in \mathcal{C}\}$,
где $\overline{L}$ содержит в точности те строки в данном алфавите,
которые $L$ не содержит.
\end{definition}

\begin{theorem}\label{th:sparse}
Если $\P \neq \NP$, то никакой редкий язык не может быть
$\coNP$-трудным по Карпу.
\end{theorem}
\begin{proof}
Доказательство похоже на доказательство теоремы~\ref{th:tally}.
Трудность будет заключаться в том, что теперь сведение может выдавать
строки, состоящие не только из нулей. Тем не менее, мы знаем, что
количество <<разумных>> 
образов\footnote{На сей раз --- тех, что являются образами невыполнимых
формул --- мы ведь сводили $\overline{\lang{SAT}}$.}
ограничено полиномом от длины входа.
Следовательно, как только образов станет <<слишком много>>,
мы можем смело говорить, что какая-то из полученных
(тем самым, и исходная) формула --- выполнима.
\end{proof}

\end{document}

