Введение
Модель вычисления
Имеется вычислительная машина, на которой отрабатывают алгоритмы
- RAM машина (память имеет произвольный доступ)
- имеются целые и вещественные числа, операции над которыми занимают константное время
Вычисление чисел Фиббоначи
Числа Фиббоначи определяются следующим образом:
$F_0 = 0$ $F_1 = 1$ $F_n = F_{n-1}+F_{n-2}$
Утверждение: $F_n <= 2^n.$
По индукции: $F_n = F_{n-1}+F_{n-2}<=2^{n-1}+2^{n-2}<=2^n$
Утверждение: $F_n \geqslant 2^{n/2}$, начиная с $n=6.$
$F_n = F_{n-1}+F_{n-2} \geqslant 2^{(n-1)/2} + 2^{(n-2)/2} > 2^{n/2}$
Утверждение: Существует $\exists a$: $F_n = a^n$
$a^n = a^{n-1}+a^{n-2}$ $a^2-a-1=0$ $a=\frac{1+\sqrt{2}}{2} = \varphi = 1\sigma$
def fib1(n):
if n < 2:
return n
return fib1(n-1) + fib1(n-2)
Утверждение: Количество вызовов $fib1$ при вычислении функции $fib1(n)$ больше чем $Fib(n) \Rightarrow$ экспоненциальное число вызовов.
Доказательство. $T(f_1(n)) = T(f_1(n-1)) + T(f_1(n-2)) + 1$ — кол-во вызовов $fib1$ в вызове $fib1(n).$
$T(f_1(0)) = T(f_1(1)) = 1$ $T(f_1(n)) \geqslant Fib(n) \geqslant 1\sigma^n$
def fib2(n):
a = [0, 1] # 0-n операций (зависит от машины)
for i in range(2, n + 1):
a.append(a[i-1] + a[i-2]) # n операций
return a[n]
Итого порядка $2n$ операций и $n$ памяти
def fib3(n):
if n == 0:
return 0
a, b = 0, 1
for _ in range(2, n + 1):
a, b = b, a + b # 3 операции
return b
Итого $3n$ оперций, константая память.
Если потребуется вычислять большие числа, то они довольно быстро вылезут за пределы машинного слова (в $F_n \simeq n$ цифр), соотвественно сложение из константного станет линейным, а алгоритм — квадратичным.
$\left(\begin{array}{cc}1 & 1 \\ 1 & 0\end{array}\right)^n$
В $(1, 0)$ и $(0, 1)$ ячейках будет $n$-ое число, скорость работы логарифмическая.
Ресурсы
Ресуры, интересующие нас при построении алгоритма:
- количество операций
- количество памяти
- количество чтений с диска(ленты)
- количество сообщений, передающихся по сети
Оценки сложности
- $f(n) = O(g(n))$ — оценка сверху $\exists c:\ \exists n_0\ \ \forall n > n_0\ \ f(n) \leqslant c \cdot g(n)$
- $f(n) = \Omega(g(n))$ — оценка снизу $\exists c:\ \exists n_0\ \ \forall n > n_0\ \ f(n) \geqslant c \cdot g(n)$
- $f(n) = \Theta(g(n))$ — точная оценка $\exists c_1, c_2: \exists n_0\ \ \forall n\ \ c_1\cdot g(n) \leqslant f(n) \leqslant c_2\cdot g(n)$ то же самое, что и $f(n) = O(g(n)) = \Omega(g(n))$
Всякие утверждения: 1. $n^k = O(n^l), l \geqslant k$ 2. $\log^kn = O(\log^ln), l\geqslant k$ 3. $a^n = O(c^n), c \geqslant a \geqslant 1$
Соотношения: 1. $\log^kn = O(n^l)$ 2. $n^k = O(a^n)$ 3. $n\log_2n = \Theta(n\log_3n)$ 4. $2^{n\log_2n} = n^n$ $2^{n\log_3n} = n^{n \log_32}$ $2^{n\log_2n} = \Omega(2^{n\log_3n})$