Семинар 6. Многопоточное программирование на OpenMP (часть 6)
Лекция 12: Методы разработки алгоритмов. Динамическое программирование. Жадные алгоритмы.
1. Лекция 12:
Динамическое программирование.
Жадные алгоритмы.
Курносов Михаил Георгиевич
к.т.н. доцент Кафедры вычислительных систем
Сибирский государственный университет
телекоммуникаций и информатики
http://www.mkurnosov.net
2. Контроль
2
1. В чем основная идея метода декомпозиции?
2. Дать оценку трудоемкости алгоритма быстрой
сортировки с использованием обобщённого
рекуррентного уравнения декомпозиции.
3. Основные методы разработки алгоритмов
33
Метод грубой силы (brute force, исчерпывающий поиск –
полный перебор).
Декомпозиция (decomposition, “разделяй и властвуй”)
Уменьшение размера задачи (“уменьшай и властвуй”)
Преобразование (“преобразуй и властвуй”)
Жадные алгоритмы (greedy algorithms)
Динамическое программирование (dynamic programming)
Поиск с возвратом (backtracking)
Локальный поиск (local search)
4. Динамическое программирование
44
Динамическое программирование (Dynamic
programming) – метод решения задач (преимущественно
оптимизационных) путем разбиения их на более простые
подзадачи. Решение идет от простых подзадач к сложным,
периодически используя ответы для уже решенных подзадач
(как правило, через рекуррентные соотношения).
Основная идея – запоминать решения встречающихся
подзадач на случай, если та же подзадача встретится
вновь.
Теория динамического программирования разработана
Р. Беллманом в 1940-50-х годах.
7. Последовательность Фибоначчи
77
Fibo(5)
Fibo(4) Fibo(3)
Fibo(3) Fibo(2) Fibo(2) Fibo(1)
Fibo(1) Fibo(0)Fibo(2) Fibo(1)
Fibo(1) Fibo(0)
Fibo(1) Fibo(0)
Некоторые элементы последовательности
вычисляются повторно несколько раз
Рекурсивные вызовы функции Fibo
8. В динамическом программировании используются
таблицы, в которых сохраняются решения подзадач
(жертвуем памятью ради времени)
Последовательность Фибоначчи
88
function Fibo(n)
F[0] = 0
F[1] = 1
for i = 2 to n do
F[i] = F[i - 1] + F[i - 2]
end for
return F[n]
end function
9. “Жадные” алгоритмы
99
“Жадный” алгоритм (Greedy algorithms) – алгоритм,
принимающий на каждом шаге локально-оптимальное
решение, предполагая, что конечное решение окажется
оптимальным.
Примеры “жадных” алгоритмов:
o алгоритм Прима
o алгоритм Курскала
o алгоритм Дейкстры
o алгоритм Хаффмана (кодирования)
10. Задача о размене (change-making problem)
1010
Задача. Имеется неограниченное количество монет
номиналом (достоинством) a1 < a2 < … < an. Требуется выдать
сумму S наименьшим количеством монет.
Пример. Имеются монеты достоинством 1, 2, 5 и 10 рублей.
Выдать сумму 27 рублей.
“Жадное” решение: 2 по 10, 1 по 5, 1 по 2.
11. Задача о размене (change-making problem)
1111
Задача. Имеется неограниченное количество монет
номиналом (достоинством) a1 < a2 < … < an. Требуется выдать
сумму S наименьшим количеством монет.
Пример. Имеются монеты достоинством 1, 2, 5 и 10 рублей.
Выдать сумму 27 рублей.
“Жадное” решение: 2 по 10, 1 по 5, 1 по 2.
На каждом шаге берётся наибольшее возможное количество
монет достоинства an.
12. Задача о размене (change-making problem)
1212
Жадные алгоритмы не всегда дают оптимальные решения.
Пример. Имеются монеты достоинством 1, 5 и 7 рублей.
Выдать сумму 24 рубля.
Решение жадным алгоритмом: 3 по 7, 3 по 1 = 6 монет
Оптимальное решение: 2 по 7, 2 по 5 = 4 монеты
13. Код Хаффмана
1313
Деревья Хаффмана (Huffman) и коды Хаффмана
используются для сжатия информации путем
кодирования часто встречающихся символов короткими
последовательностями битов.
Предложен Д. А. Хаффманом в 1952 году (США, MIT).
14. Код Хаффмана
1414
Задано множество символов и известны вероятности их
появления в тексте (сообщении, файле).
A = {a1, a2, …, an} – множество символов (алфавит),
P = {p1, p2, …, pn} – вероятности появления символов.
Требуется каждому символу сопоставить код – битовую
строку (последовательность, codeword).
С(A, P) = {c1, c2, …, cn}
Пример
Символ A B C D _
Код (биты) 11 100 00 01 101
15. Код Хаффмана
1515
Шаг 1. Создается n одноузловых деревьев.
В каждом узле записан символ алфавита и вероятность
его повеления в тексте.
A
0.35
B
0.1
C
0.2
D
0.2
_
0.15
16. Код Хаффмана
1616
Шаг 2. Находим два дерева с наименьшими вероятностями
и делаем их левым и правым поддеревьями нового дерева –
создаем родительский узел.
В созданном узле записываем сумму вероятностей
поддеревьев.
Повторяем шаг 2 пока не получим одно дерево.
На каждом шаге осуществляется “жадный выбор”
(два дерева с наименьшими вероятностями)