\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:
%
\pagestyle{headings}
\newtheorem{theorem}{Теорема}[chapter]
\newtheorem{lemma}{Лемма}[chapter]
\newtheorem{proposition}{Утверждение}[chapter]
\newtheorem{fact}{Факт}[chapter]
\theoremstyle{definition}
\newtheorem{problem}{Задача}[chapter]
\newtheorem{exercise}{Упражнение}[chapter]
\newtheorem{example}{Пример}[chapter]
\newtheorem{definition}{Определение}[chapter]
\newtheorem{remark}{Замечание}[chapter]
\newtheorem{algorithm}{Алгоритм}[chapter]
\def\gap{\medskip\centerline{\fbox{\Huge\bfseries{ПРОБЕЛ В КОНСПЕКТЕ.}}}\medskip}
\advance\headheight by 7pt
\def\headsep{15mm}
\newcommand{\lecture}[3]{%
\def\rightmark{\fbox{\parbox{125mm}{\lecturername: \coursetitle%
\hfil\phantom{.}}}}
\def\leftmark{\fbox{\parbox{125mm}{Лекция {#1}. {#2}%
\hfil\phantom{.}}}}
\renewcommand\chaptername{Лекция}\renewcommand\thechapter{#1}\chapter{{#2}\\{\small (Конспект: {#3})}}
\thispagestyle{headings}}
%
% Things to customize for the course are here:
%
\newcommand\lecturername{Э. А. Гирш}
\newcommand\coursetitle{с/к ``Эффективные алгоритмы''}
%
\begin{document} 
\selectlanguage{russian} 
% Lecture title
\lecture{2}{Алгоритм Шенхаге-Штрассена для умножения чисел}{А. Бережной}
% The lecture
Пусть нужно перемножить два числа, $a_1$ и $a_2$: каждое длиной $n$
бит. Обычный алгоритм умножения ``в столбик'' имеет сложность
$O(n^2)$. Посмотрим, как это можно улучшить. 

\section{Простой алгоритм.}
Во-первых, построим простой рекурсивный алгоритм,
разделяя числа на ``половинки'' и рекурсивно перемножая эти ``половинки''. 
Cчитаем, что $n=2^k$. Тогда
\begin{eqnarray*}
a_1=u_1+u_2 2^{k-1}&;&\ a_2=v_1+v_2 2^{k-1}\\
&\Downarrow&\\
a_1 a_2 &=& u_1v_ 1+2^{k-1}(u_1 v_2+u_2 v_1)+2^k u_2 v_2.
\end{eqnarray*}
Итого 4 умножения (умножение на степень двойки --- это не умножение, а
сдвиг). Но пусть: \begin{center} $x=u_1 v_1;$
$y=(u_1+u-2)(v_1+v_2);$ $z=u_2 v_2.$ \end{center} Тогда:
\begin{center} $a_1 a_2=x+2^{k-1}(y-x-z)+2^k z$
\end{center} Это уже только 3 умножения. Теперь посчитаем сложность алгоритма.
Очевидно, $T(n)=3T(\frac{n}{2})+cn$. Отсюда, аналогично теореме~1.2
из первой лекции, имеем $$T(n)=O(n^{log_2 3}).$$ Это лучше,
чем $O(n^2)$, но есть еще более быстрый способ. Для него
понадобится понятие \emph{Дискретного Преобразования Фурье (ДПФ)}.

\section{Дискретное преобразование Фурье и его вычисление.}
\begin{definition}
Пусть $\omega$ --- первообразный корень степени $b$. 
Рассмотрим какой-нибудь вектор $(a_{b-1},...,a_0)$.
Его ДПФ --- это вектор, состоящий из значений полинома 
$a_{b-1}x^{b-1}+\ldots+a_1x+a_0$ в точках $1, \omega, \omega^2,...$.
\end{definition}

\paragraph{Быстрое вычисление ДПФ.}
Пусть $f(x)$ --- это тот самый полином, вычисляющий ДПФ. По теореме
Безу, $f(x) \mod (x-\omega^i) = f(\omega^i)$. Все, что нам
нужно --- это быстро находить остатки. Построим двоичное дерево;
его узлы будут помечены полиномами. Листья дерева помечены полиномами
$x-\omega^i$. Полином в любом узле является произведением
полиномов в потомках. Упорядочим листья так, чтобы каждый узел был
помечен полиномом вида $x^m-\omega^k$. Это делается так: в качестве
$k$-го листа берем полином $x-\omega^{rev(k)}$,
где $rev$ -- операция переворачивания битов числа;
например, если у нас всего 32 листа (т.е. 5 битов),
то $rev(2)=8$: в двоичной системе, $rev(00010)=01000$.
Очевидно, что $x\vdots2=>rev_n(\frac{x}{2})=2rev_n(x)$. Пользуясь этим, можно
доказать (индукцией), что дерево получилось такое, как
предполагалось. Легко проверить, что
\[
\sum_{i=0}^{2t-1}a_i x^i \mod (x^t-\omega^s)=
\sum_{j=0}^{t-1} (a_j+\omega^s a_{j+t})x^j.
\]
Так мы можем быстро вычислить остаток любого полинома по модулю $x^t-w^s$.
Спускаясь по
дереву, мы будем заменять имеющийся полином на остаток, пока не
спустимся до листьев. Так мы получим ДПФ,
ведь \[f(x) \mod (x-\omega^i) = 
f(x) \mod q_1 \mod q_2 \mod \ldots \mod x-\omega^i,\]
где $q_1\vdots\, q_2\vdots\, \ldots \vdots \,x-\omega^i$.

\section{Умножение при помощи ДПФ.}
Вернемся к
умножению. Пусть опять $n=2^k$. На этот раз разобьем оба числа не
на два блока, а на $b$: каждый длиной $l$ бит, причем так, что
$b\approx l$, т.е., либо $b=l$, либо $b=\frac{l}{2}$ i
(это зависит от четности $k$, ибо
$b\times l=n$). Тогда 
\begin{eqnarray*}
a_1&=&u_0+u_1 2^l+...+u_{b-1}2^{l(b-1)},\\
a_2&=&v_0+v_1 2^l+...+v_{b-1}2^{l(b-1)},\\
a_1\times a_2 &=& w_0+w_1 2^l+...+ w_{2b-2}2^{l(2b-2)},
\end{eqnarray*}
где 
\[
w_i=y_i+y_{b+i},\ 
y_i=\sum_{j=0}^{b-1}u_j v_{i-j}\mbox{ (а }
y_{b+i}=\sum_{j=0}^{b-1}u_j v_{b+i-j})
\]
(если какой-то блок под суммой не существует, считаем его нулем).

Вычислять $w_i$ будем так: сначала найдем 
\[
w_i^\prime=w_i \!\mod b
\quad\mbox{ и }\quad w_i^{\prime\prime} = w_i \!\mod (2^{2l}+1).
\] 
После этого, очевидно, $w_i$ находится по формуле: 
\[
w_i = w_i^\prime(2^{2l}+1)-w_i^{\prime\prime}2^{2l}.
\]

\paragraph{Сначала получим $w_i^\prime$.} Построим два числа следующим образом.
Эти числа состоят из $b$ блоков длиной $3 \log{b}$ каждый. Блок,
занимающий в 1-м числе $i$-ю позицию (справа) состоит из
$2\log{b}$ нулей и числа $u_i$. Во втором числе то же самое, но
вместо $u_i$ всюду $v_i$. Перемножить эти два числа можно нашим
первым простым алгоритмом за
$O((3b\log{b})^{\log{3}}) = O(b^2)=O(n)$; при этом мы получим 
число, состоящее из блоков
$y_i \mod b$, а стало быть и $w_i^\prime$.

\paragraph{Как найти $w_i^{\prime\prime}$?} Тут поможет ДПФ. У нас есть два
вектора: $(u_{b-1}\psi^{b-1},...,u_0\psi)$ и
$(v_{b-1}\psi^{b-1},...,v_0\psi)$, где $\psi^2=\omega$
(в нашем случае $\psi=2^{2l/b}$; легко проверить, что $\psi^2$ ---
действительно первообразный корень $b$-ой степени в $\mathbb{Z}_{2^{2l}+1}$). 
Мы вычислим от них ДПФ и почленно перемножим. Утверждается, что
результатом будет ДПФ от вектора $(w_{b-1}\psi^{b-1},...,w_0\psi)$.
Это утверждение называется \emph{Теоремой об отрицательно обернутой
свертке}. Докажем ее:
\[
(\mbox{ДПФ}(w_i\psi^i))_j=\sum_i\omega^{ij}w_i\psi^i=\sum_s
\omega^{sj}(\sum_m u_m v_{s-m} - \sum_m u_m v_{b+s-m})\psi^s,
\]
\[
(\sum_i \omega^{ij}u_i \psi^i)(\sum_k\omega^{kj}v_i\psi^k)
=\sum(\omega^{(i+k)j}\psi^{i+k}\sum_m u_m v_{i+k-m}).
\]
Правые части равны: $(i+j)$ во втором равенстве играют роль $s$
в первом. Осталось посчитать сложность. Докажем сначала, что ДПФ
вычисляется за время $O(n\log{n})$. Дерево имеет высоту $\log{b}$.
Пусть на уровне вычисляется $s$ остатков. На каждый остаток
выполняется $\frac{b}{s}$ операций: каждая сложностью $2l$. Итого
$O(b\log{b}2l) = O(n\log{n})$. Теперь общее время на вычисление
свертки: $$T(bl)=bT(2l)+cn\log{n}.$$
Остается решить это рекуррентное уравнение\ldots 
$$T^\prime(s)=\frac{T(s)}{s},$$
$$T^\prime(n)=2T^\prime(2l)+c\log{n},$$
$$T^\prime(n)=2T^\prime(2\sqrt{n})+c\log{n},$$
$$T^{\prime\prime}(s)=T^\prime(2^s),$$
$$T^{\prime\prime}(\log{n})=2T^{\prime\prime}(\frac{\log{n}+1)}{2})+c\log{n},$$
$$T^{\prime\prime}(s)=2T^{\prime\prime}(\frac{s}{2}+1)+cs.$$
Итого,
$$T(n)=n\log{n}\log{\log{n}}.$$ 
\end{document}

