Архитектура Аудит Военная наука Иностранные языки Медицина Металлургия Метрология Образование Политология Производство Психология Стандартизация Технологии |
Определение порядка числа вызовов ⇐ ПредыдущаяСтр 4 из 4
Числа Фибоначчи удовлетворяют отношению где , т.е.
Сложность этого алгоритма – это .
Как ускорить?
Проблема в том, что мы много раз повторно вычисляем значение функции от одних и тех же аргументов.
Третий вопрос: можно ли ускорить алгоритм?
Вводим добавочный массив.
int fibo(int n) { const int MAXN = 1000; static int c[MAXN]; if (n == 0) return 0; if (n == 1) return 1; if (c[n] > 0) return c[n]; return c[n] = fibo(n-1) + fibo(n-2); }
Дерево вызовов модифицированной функции для n = 5
Проблема с представимостью данных
Значение функции растёт слишком быстро, и уже при небольших значениях n число выйдет за пределы разрядной сетки.
В реальных программах имеются ограничения на операнды машинных команд.
X86, X64 -> int есть 32 бита, longlong есть 64 бита.
X86: сложение 32-битных ≈ 1 такт, сложение 64-битных ≈ 3 такта. X64: сложение 32-битных ≈ 1 такт, сложение 64-битных ≈ 1 такт.
X86: умножение 32-битных ≈ 3-4 тактов, умножение 64-битных ≈ 15-50 тактов. X64: умножение 32-битных ≈ 3-4 такта, умножение 64-битных ≈ 4-5 тактов.
Как работать с длинными числами?
На 32-битной архитектуре сложение двух 64-разрядных -> сложение младших разрядов и прибавление бита переноса к сумме старших разрядов. Три машинных команды.
Числа (n) – те, которые занимают ровно n элементарных типов.
int – числа (1), longlong – числа (2).
Большие числа требуют представления в виде массивов из элементарных типов.
Сколько операций потребуется для сложения двух чисел (n)?
Как умножать длинные числа?
Школьный алгоритм: O(n2)
Алгоритм быстрого умножения
Можно ли быстрее?
Да. Используя принцип разделяй и властвуй.
Быстрый алгоритм умножения был изобретён Гауссом в 19 веке и переизобретён Анатолием Карацубой в 1960-м году.
Разделим число (n) на две примерно равные половины: N1 = Tx1 + y1 N2 = Tx2 + y2
При умножении в столбик N1 × N2 = T2x1x2 + T (x1y2 + x2y1) + y1y2
Это – четыре операции умножения и три операции сложения.
Число T определяет, сколько нулей нужно добавить к концу числа в соответствующей системе счисления.
Алгоритм Карацубы
Алгоритм Карацубы находит произведение по другой формуле:
N1 × N2 = T2x1x2 + T (x1 + y1)(x2 + y2) - x1x2 - y1y2) + y1y2 N1 = 56, N2 = 78, T = 10 x1 = 5, y1 = 6 x2 = 7, y2 = 8 x1x2 = 5 × 7 = 35 (x1 + y1) (x2 + y2) = (5 + 6) (7 + 8) = 11 ∗ 15 = 165 y1y2 = 6 × 8 = 48 N1 × N2 = 35 ∗ 100 + (165 - 35 - 48) ∗ 10 + 48 = 4368
Т.е. 3 операции умножения и 6 сложения.
Основная теорема о рекурсии Оценка асимптотического времени алгоритма
Как определить, какой порядок сложности будет иметь рекурсивная функция, не проводя вычислительных экспериментов?
Рекурсия – разбиение задачи на подзадачи с последующей консолидацией результата.
Пусть: 1) a – количество подзадач; 2) размер каждой подзадачи уменьшается в b раз и становится ; 3) сложность консолидации пусть – это O(nd).
Время работы такого алгоритма, выраженное рекуррентно – это
Основная теорема о рекурсии
Пусть для некоторых a > 0, b > 1, d ≥ 0. Тогда
Оценка сложности алгоритма Карацубы
Коэффициент порождения задач a = 3.
Коэффициент уменьшения размера подзадачи b = 2.
Консолидация решения производится за время O(n) -> d = 1
Так как 1 < log23, то это третий случай теоремы -> сложность алгоритма – это
Операция умножения чисел (n) при умножении в столбик имеет порядок сложности O(n2).
Много операций сложения -> при малых N выгоднее “школьный” алгоритм.
Ещё о сложности Введём вектор-столбец , состоящий их двух элементов последовательности Фибоначчи, и умножим его справа на матрицу :
Для вектора-столбца из элементов Fn-1 и Fn умножение на ту же матрицу даст:
Таким образом,
Возведение в степень
Для нахождения n-го числа Фибоначчи достаточно вычислить
Можно ли возвести число в n-ую степень за число операций, меньших n - 1?
Быстрое вычисление степеней
Возведение в квадрат – это умножение на себя.
х16 = (х8)2 = ((х4)2)2 = (((х2)2)2)2
х18 = (х9)2 = ((х8 * х)2)2 = (((х2)2)2 * х)2
Рекуррентная формула
Рекурсивная функция быстрого умножения
SomeType – некий тип данных.
SomeType pow(SomeType x, int n) { if (n == 0) return (SomeType)1; if (n % 2! = 0) return pow(x, n-1) * x; SomeType y = pow(x, n/2); return y*y; }
Оценка сложности быстрого умножения
25 = 110012
n – нечётное -> обнуление последнего разряда.
n – чётное -> вычёркивание последнего разряда.
Каждую из единиц требуется уничтожить, не изменяя количества разрядов.
Каждый из разрядов требуется уничтожить, не изменяя количества единиц.
Сложность – O(log N). |
Последнее изменение этой страницы: 2017-03-17; Просмотров: 387; Нарушение авторского права страницы