1. Несложно о сложности. Примеры алгоритмов.
Н.Н. Кузюрин С.А. Фомин
10 октября 2008 г.
1 / 46
2. История алгоритмов
Понятие алгоритма известно с незапамятных времен
Алгоритм Евклида (Euclidean algorithm) — 500-300 гг. д.н.э.
Решето Эратосфена (Sieve of Eratosthenes) — 300-200 гг. д.н.э.
Формализация понятия — 1930–1940 годы.
Курт Гёдель ⇒ «неполнота формальной арифметики», 1931.
Алан Тьюринг ⇒ «halting problem», 1936.
Эмиль Пост ⇒ «машина Поста», 1936.
Алонзо Чёрч ⇒ «An Unsolvable Problem of Elementary Number
Theory», 1936.
Стивен Клини ⇒ «Тезис Черча-Тьюринга», 1943.
2 / 46
3. История алгоритмов
Понятие алгоритма известно с незапамятных времен
Алгоритм Евклида (Euclidean algorithm) — 500-300 гг. д.н.э.
Решето Эратосфена (Sieve of Eratosthenes) — 300-200 гг. д.н.э.
Формализация понятия — 1930–1940 годы.
Курт Гёдель ⇒ «неполнота формальной арифметики», 1931.
Алан Тьюринг ⇒ «halting problem», 1936.
Эмиль Пост ⇒ «машина Поста», 1936.
Алонзо Чёрч ⇒ «An Unsolvable Problem of Elementary Number
Theory», 1936.
Стивен Клини ⇒ «Тезис Черча-Тьюринга», 1943.
3 / 46
4. История алгоритмов
Понятие алгоритма известно с незапамятных времен
Алгоритм Евклида (Euclidean algorithm) — 500-300 гг. д.н.э.
Решето Эратосфена (Sieve of Eratosthenes) — 300-200 гг. д.н.э.
Формализация понятия — 1930–1940 годы.
Курт Гёдель ⇒ «неполнота формальной арифметики», 1931.
Алан Тьюринг ⇒ «halting problem», 1936.
Эмиль Пост ⇒ «машина Поста», 1936.
Алонзо Чёрч ⇒ «An Unsolvable Problem of Elementary Number
Theory», 1936.
Стивен Клини ⇒ «Тезис Черча-Тьюринга», 1943.
4 / 46
5. История алгоритмов
Понятие алгоритма известно с незапамятных времен
Алгоритм Евклида (Euclidean algorithm) — 500-300 гг. д.н.э.
Решето Эратосфена (Sieve of Eratosthenes) — 300-200 гг. д.н.э.
Формализация понятия — 1930–1940 годы.
Курт Гёдель ⇒ «неполнота формальной арифметики», 1931.
Алан Тьюринг ⇒ «halting problem», 1936.
Эмиль Пост ⇒ «машина Поста», 1936.
Алонзо Чёрч ⇒ «An Unsolvable Problem of Elementary Number
Theory», 1936.
Стивен Клини ⇒ «Тезис Черча-Тьюринга», 1943.
5 / 46
6. Теория сложности
Ресурсные ограничения
Время
Память
Объем коммуникаций
Переборные алгоритмы
Пригодны для большинства задач дискретной оптимизации, но время
работы растет экспоненциально.
6 / 46
7. Теория сложности
Ресурсные ограничения
Время
Память
Объем коммуникаций
Переборные алгоритмы
Пригодны для большинства задач дискретной оптимизации, но время
работы растет экспоненциально.
7 / 46
8. Обозначения
f (n) = O(g (n))
Существует константа C > 0 такая, что начиная с некоторого n
выполнено f (n) ≤ Cg (n).
f (n) = Ω(g (n))
Существует константа C > 0 такая, что начиная с некоторого n
выполнено f (n) ≥ Cg (n).
Описание алгоритмов
«Псевдокод» — неполнота, «C/C++/Java» — нечитаемый синтаксис
и слабая абстракция.
Мы используем популярный и рекомендованный для преподавания
алгоритмов язык Python.
8 / 46
9. Обозначения
f (n) = O(g (n))
Существует константа C > 0 такая, что начиная с некоторого n
выполнено f (n) ≤ Cg (n).
f (n) = Ω(g (n))
Существует константа C > 0 такая, что начиная с некоторого n
выполнено f (n) ≥ Cg (n).
Описание алгоритмов
«Псевдокод» — неполнота, «C/C++/Java» — нечитаемый синтаксис
и слабая абстракция.
Мы используем популярный и рекомендованный для преподавания
алгоритмов язык Python.
9 / 46
10. Тривиальное вычисление y = x n mod m
13 16 47
13
28
35
def mod_exp (x, n, m) : 32
print x, n, m 40
y ←1 3
for i ∈ range (n) : 39
y ←y ∗x %m 37
11
print y
2
return y 26
9
Количество умножений — O(n) 23
17
33
6
y = 13^16 (mod 47) = 6
10 / 46
11. Разумное вычисление y = x n mod m
k
k i i i
xn = x i=0 ai 2 = x ai 2 = x2
i=0 {i:ai >0}
def mod_exp (x, n, m) :
print x, n, m 13 16 47
y ←1 28 8 1
X ←x 32 4 1
N←n 37 2 1
while N > 0 : 6 1 1
if N % 2 = 1 : 36 0 6
y ←y ∗X %m y = 13^16 (mod 47) = 6
X ←X ∗X %m
N ← N/2 Количество арифметических
print X , N, y операций — O(log(n)).
return y
Длина (бит) Умножений в алг. 1 Умножений в алг. 2
56 256 ≈ 7.2 · 1016 ≈ 100
128 2128 ≈ 3.4 · 1038 ≈ 130
11 / 46
12. Вычисление факториала y = n! mod m
def factorial (n, m) :
print n, m 13 131
y ←1 1
for i ∈ range (1, n + 1) : 2
y ←y ∗i %m 6
print y 24
return y 120
65
Количество умножений — O(n). 62
Временная сложность измеряется 103
относительно длины входа: 10
log n + log m. 100
Поэтому cложность 52
экспоненциальна ! 100
Возможно ли лучше — открытая 121
проблема. y =13! mod 131 = 121
12 / 46
13. Дискретный логарифм
Задача
«Дискретный логарифм» (Discrete logarithm) Даны натуральные a, b,
и p — нечетное простое число. Найти минимальный x, такой, что
ax ≡ b (mod p).
Вычисление дискретного логарифма является очень сложной задачей.
Самые быстрые из известных алгоритмов требуют
сверхполиномиального времени.
На этом свойстве односторонней вычислимости модульной экспоненты
основано множество алгоритмов современной криптографии.
13 / 46
14. Дискретный логарифм
Задача
«Дискретный логарифм» (Discrete logarithm) Даны натуральные a, b,
и p — нечетное простое число. Найти минимальный x, такой, что
ax ≡ b (mod p).
Вычисление дискретного логарифма является очень сложной задачей.
Самые быстрые из известных алгоритмов требуют
сверхполиномиального времени.
На этом свойстве односторонней вычислимости модульной экспоненты
основано множество алгоритмов современной криптографии.
14 / 46
15. Дискретный логарифм
Задача
«Дискретный логарифм» (Discrete logarithm) Даны натуральные a, b,
и p — нечетное простое число. Найти минимальный x, такой, что
ax ≡ b (mod p).
Вычисление дискретного логарифма является очень сложной задачей.
Самые быстрые из известных алгоритмов требуют
сверхполиномиального времени.
На этом свойстве односторонней вычислимости модульной экспоненты
основано множество алгоритмов современной криптографии.
15 / 46
17. Алгоритм Евклида
Базовое соотношение
gcd(a, b) = gcd(a, r ), b = at + r , 0 ≤ r < a.
Calculating gcd(123456, 6122256):
123456 6122256
72912 123456
50544 72912
def gcd (a, b) : 22368 50544
5808 22368
print a, b 4944 5808
if a = 0 : 864 4944
return b 624 864
240 624
return gcd (b % a, a) 144 240
96 144
48 96
0 48
gcd(123456, 6122256) = 48
17 / 46
18. Алгоритм Евклида
Базовое соотношение
gcd(a, b) = gcd(a, r ), b = at + r , 0 ≤ r < a.
Calculating gcd(123456, 6122256):
123456 6122256
72912 123456
50544 72912
def gcd (a, b) : 22368 50544
5808 22368
print a, b 4944 5808
if a = 0 : 864 4944
return b 624 864
240 624
return gcd (b % a, a) 144 240
96 144
48 96
0 48
gcd(123456, 6122256) = 48
18 / 46
19. Алгоритм Евклида: Время выполнения
Лемма
Время работы алгоритма Евклида — O(log a + log b) арифметических
операций.
Доказательство.
1 Имеем b ≥ a + (b mod a) ≥ 2(b mod a) ≡ 2r .
2 Отсюда получаем, что r ≤ b .
2
3 ar ≤ ab/2 (на каждой итерации ab уменьшается вдвое).
4 После log(ab) итераций ab станет нулем.
5 ab = 0 → a = 0 → НОД найден.
19 / 46
20. Алгоритм Евклида: Время выполнения
Лемма
Время работы алгоритма Евклида — O(log a + log b) арифметических
операций.
Доказательство.
1 Имеем b ≥ a + (b mod a) ≥ 2(b mod a) ≡ 2r .
2 Отсюда получаем, что r ≤ b .
2
3 ar ≤ ab/2 (на каждой итерации ab уменьшается вдвое).
4 После log(ab) итераций ab станет нулем.
5 ab = 0 → a = 0 → НОД найден.
20 / 46
21. Алгоритм Евклида: Время выполнения
Лемма
Время работы алгоритма Евклида — O(log a + log b) арифметических
операций.
Доказательство.
1 Имеем b ≥ a + (b mod a) ≥ 2(b mod a) ≡ 2r .
2 Отсюда получаем, что r ≤ b .
2
3 ar ≤ ab/2 (на каждой итерации ab уменьшается вдвое).
4 После log(ab) итераций ab станет нулем.
5 ab = 0 → a = 0 → НОД найден.
21 / 46
22. Алгоритм Евклида: Время выполнения
Лемма
Время работы алгоритма Евклида — O(log a + log b) арифметических
операций.
Доказательство.
1 Имеем b ≥ a + (b mod a) ≥ 2(b mod a) ≡ 2r .
2 Отсюда получаем, что r ≤ b .
2
3 ar ≤ ab/2 (на каждой итерации ab уменьшается вдвое).
4 После log(ab) итераций ab станет нулем.
5 ab = 0 → a = 0 → НОД найден.
22 / 46
23. Алгоритм Евклида: Время выполнения
Лемма
Время работы алгоритма Евклида — O(log a + log b) арифметических
операций.
Доказательство.
1 Имеем b ≥ a + (b mod a) ≥ 2(b mod a) ≡ 2r .
2 Отсюда получаем, что r ≤ b .
2
3 ar ≤ ab/2 (на каждой итерации ab уменьшается вдвое).
4 После log(ab) итераций ab станет нулем.
5 ab = 0 → a = 0 → НОД найден.
23 / 46
24. Алгоритм Евклида: Время выполнения
Лемма
Время работы алгоритма Евклида — O(log a + log b) арифметических
операций.
Доказательство.
1 Имеем b ≥ a + (b mod a) ≥ 2(b mod a) ≡ 2r .
2 Отсюда получаем, что r ≤ b .
2
3 ar ≤ ab/2 (на каждой итерации ab уменьшается вдвое).
4 После log(ab) итераций ab станет нулем.
5 ab = 0 → a = 0 → НОД найден.
24 / 46
25. Задача Коммивояжера
Задача
«Коммивояжер», «TSPa ». Заданы неориентированный граф из n
вершин-городов, и dij ≡ d(vi , vj ) — положительные целые расстояния
между городами.
Чему равна наименьшая возможная длина кольцевого маршрута,
проходящего по одному разу через все города? Т.е. нужно найти
минимально возможное значение суммы
n−1
min dpi ,pi+1 + dpn ,p1 , (1)
1 2 . . . n i=1
p∈
. . . .
где минимум берется по всем перестановкам p чисел 1, . . . , n.
a
В англоязычной литературе — Traveling Salesman Problem.
25 / 46
26. Перебор для TSP
Moscow
$50
$10
Berlin $30
$60
$15 Kiev
$50 $20
$80
$15
NY $120
Minsk
26 / 46
27. Кратчайшие пути в графе
Задача
«Кратчайший путь в графе»a .
Заданы n вершин графа (узлов сети) v1 , v2 , . . . , vn и положительные
целые длины дуг dij ≡ d(vi , vj ) между ними.
Нужно для всех k ∈ (2 . . . n) найти минимальную длину пути из v1 в vk .
a
В англоязычной литературе — Shortest Path Problem.
27 / 46
28. Кратчайшие пути в графе с отрицательными весами
Задача
«Кратчайшие пути с отрицательными расстояниями».
Задан ориентированный граф G = (V , E ) и весовая функция на дугах
we : e → Z , отображающая ребра в целые числа, такая, что в графе
нет цикла отрицательной длины. Найти минимальные длины путей
между всеми парами вершин.
28 / 46
29. Алгоритм Флойда-Уоршолла
for k ∈ range (N) : # N — размер матрицы
DD ← array (D) # Сохраняем Dk−1 в DD
for v1 ∈ range (N) :
for v2 ∈ range (N) :
D[v1, v2] ← min (DD[v1, v2], DD[v1, k] + DD[k, v2])
29 / 46
35. Минимальное остовное дерево
Задача
«Минимальное остовное дерево» (Minimum Spanning Tree)
Задан связный неориентированный граф G = (V , E ), где
V —множество вершин, |V | = n, E —множество ребер между ними,
и весовая функция w : E → Z +. Иными словами, есть n вершин
v1 , . . . , vn и положительные целые веса дуг wij ≡ w (vi , vj )a между ними.
Требуется найти наименьший возможный вес остовного дерева, т.е.
min w (vi , vj ), (2)
(i,j)∈T
где минимум берется по всем остовным деревьям на n вершинах (по
всем множествам T из (n − 1) дуг, связывающим все n вершин
в единую сеть).
a
Можно вводить веса на ребрах, как we , e ∈ E .
35 / 46
36. Алгоритм Прима
def MST_Prim (G , s) :
MST ← {} # мин. остовное дерево, хэш (вершина:предшественник)
ToVisit ← {s : 0} # хэш, граничащих с MST, (узел:стоимость)
Predecessor ← {s : s} # хэш: вершины из которых включают другие
while ToVisit : # пока есть непосещенные вершины
v ← argmin (ToVisit) # ближайшая достижимая вершина
MST[v ] ← Predecessor[v ]; # запоминаем, откуда пришли
del ToVisit[v ]; del Predecessor[v ]; # больше не посещать
for w ∈ G .neighbors (v ) : # для всех соседей вершины v
if w ∈ MST : # которые еще не в MST
обновляем стоимость включения в MST
if w ∈ ToVisit ∨ G .get_edge (v , w ) < ToVisit[w ] :
ToVisit[w ] ← G .get_edge (v , w ) # кандидат
Predecessor[w ] ← v
return MST
36 / 46
37. Выполнение алгоритма Прима
Berlin ($20)
$50
Moscow ($15) $10 $30
Kiev ($15)
$60 $$ 2 0
$15 0
5
$80 $15
NY
Minsk
Start
Итерация № 1 Стоимость MST: 0
37 / 46
38. Выполнение алгоритма Прима
Berlin ($20)
$50
Moscow $30
$10
$ 5 0$ 2 0 Kiev ($10)
$60 $80
$15
$15
NY ($60)
Minsk
Start
Итерация № 2 Стоимость MST: 15
38 / 46
39. Выполнение алгоритма Прима
Berlin ($20)
$50
Moscow $10 $30
$50 $20 Kiev
$60
$15 $15
$80
NY ($60)
Minsk
Start
Итерация № 3 Стоимость MST: 25
39 / 46
40. Выполнение алгоритма Прима
Berlin
$50
Moscow $10 $30
$50 $20 Kiev
$60
$15 $15
$80
NY ($50)
Minsk
Start
Итерация № 4 Стоимость MST: 45
40 / 46
41. Выполнение алгоритма Прима
Berlin
$50
Moscow $10 $30
$50 $20 Kiev
$60
$15 $15
$80
NY
Minsk
Start
Итерация № 5 Стоимость MST: 95
41 / 46
42. Приближенные алгоритмы
Часто алгоритмы, используемые на практике, не находят точное
решение, а довольствуются нахождением приближенного решения.
Такие алгоритмы называются приближенными. Часто приближенные
алгоритмы применяют для решения N P-трудных задач.
Задача
«Составление расписаний» Имеется m одинаковых машин и n
независимых работ с длительностями исполнения t1 , . . . , tn .
Распределить эти работы по машинам так, чтобы минимизировать
максимальную загрузку (загрузка машины равна сумме длительностей
работ, приписанных данной машине).
42 / 46
43. Составление расписаний
Даже для случая m = 2 она остается N P-трудной, поскольку к ней
сводится N P-трудная задача о камнях: для заданного множества из n
камней с весами t1 , . . . , tn выяснить, можно ли разбить это множество
на два так, чтобы суммы весов в них были равны. Это условие можно
записать в виде булева уравнения:
n
xi ti = A/2,
i=1
n
где A = i=1 ti , xi ∈ {0, 1}.
Традиционный подход к задачам такого рода состоит в использовании
простых эвристик, одну из которых мы сейчас проанализируем.
43 / 46
44. Составление расписаний
Эвристика: Берется произвольная работа и помещается на машину,
имеющую наименьшую загрузку (загрузка равна сумме длин работ на
данной машине).
Эта эвристика обладает следующим очевидным свойством.
Лемма
В любой момент работы этой эвристики разница в загрузке между
наиболее и наименее загруженными машинами не превосходит
tmax = maxi ti .
44 / 46
45. Составление расписаний
Лемма
Построенное расписание отличается от оптимального (по критерию
минимизации максимальной загрузки) не более чем в два раза.
Доказательство.
Пусть T ∗ — длина оптимального расписания (сумма длин работ на
наиболее загруженной машине), T A — длина расписания, которое
A
построено нашей эвристикой, Tmin — сумма длительностей работ на
наименее загруженной машине. Очевидно:
T ∗ ≥ Tmin ,
A
T ∗ ≥ tmax .
Оценим качество получаемого расписания:
TA T A + tmax T ∗ + tmax tmax
∗
≤ min ∗ ≤ ∗
≤ 1 + ∗ ≤ 1 + 1 = 2.
T T T T
45 / 46
46. Приближенные алгоритмы: Задача коммивояжера
Предположим, что существует эффективный алгоритм A, который для
любого входа задачи строит гамильтонов цикл (цикла, проходящего
через все вершины в графе) веса не более чем в K раз превосходящего
оптимум (K = f (n), где n — число вершин графа). Подадим на вход
алгоритма A набор весов специального вида. Пусть дан произвольный
граф G = (V , E ), |V | = n. Тогда вес ребра e в полном графе зададим
формулой: w (e) = 1, если e ∈ E , и w (e) = nK , если e ∈ E .
/
Если в графе G = (V , E ) есть гамильтонов цикл, то алгоритм должен
выдать число, не превосходящее Kn. Если же гамильтонова цикла в G
нет, то алгоритм выдает число, не меньшее nK + n − 1.
Значит, такого алгоритма A не должно существовать
в предположении, что P = N P.
46 / 46