- •20 Понятие качества программного средства.
- •13. Алгоритмы поиска, выборки и сортировки.
- •12. Основные вопросы анализа алгоритмов. Понятие алгоритмической сложности.
- •19 Специфика разработки программных средств.
- •18. Жизненный цикл программного средства.
- •17 Программа как формализованное описание процесса обработки данных.
- •16.Методы разработки алгоритмов. Метод декомпозиции.
- •14.Алгоритмы решения задач на графах.
К
настоящему времени специалисты по
вычислительной технике разработали
ряд эффективных
методов, которые нередко позволяют
получать эффективные алгоритмы решения
больших классов задач. Некоторые из
наиболее важных методов, такие как
"разделяй и властвуй" (декомпозиции),
динамическое программирование, "жадные"
методы, поиск с возвратом и локальный
поиск, представлены в этой главе. Пытаясь
разработать алгоритм решения той или
иной задачи, часто бывает полезно задать
вопрос: "Какой тип решения позволяет
получить метод декомпозиции, динамическое
программирование, "жадный" метод
или какой-либо другой стандартный
метод?" Возможно,
самым важным и наиболее широко применимым
методом проектирования эффективных
алгоритмов является метод, называемый
методом декомпозиции(или метод "разделяй
и властвуй", или метод разбиения).
Этот метод предполагает такую декомпозицию
(разбиение) задачи размера n
на более мелкие задачи, что на основе
решений этих более мелких задач можно
легко получить решение исходной задачи.
Мы уже знакомы с рядом применений этого
метода, например в сортировке слиянием
или в деревьях двоичного поиска. Чтобы
проиллюстрировать этот метод, рассмотрим
хорошо известную головоломку "Ханойские
башни". Имеются три стержня А, В и С.
Вначале на стержень А нанизаны несколько
дисков: диск наибольшего диаметра
находится внизу, а выше — диски последовательно
уменьшающегося диаметра, как показано
на рис.. Цель головоломки — перемещать
диски (по одному) со стержня на стержень
так, чтобы диск большего диаметра
никогда не размещался выше диска
меньшего диаметра и чтобы в конце концов
все диски оказались нанизанными на
стержень В. Стержень С можно использовать
для временного хранения дисков.
Для
решения этой головоломки подходит
следующий простой алгоритм. Представьте,
что стержни являются вершинами
треугольника. Пронумеруем все перемещения
дисков. Тогда при перемещениях с
нечетными номерами наименьший диск
нужно перемещать в треугольнике на
соседний стержень по часовой стрелке.
При перемещениях с четными номерами
выполняются другие допустимые
перемещения,не связанные с наименьшим
диском. Задачу
перемещения n наименьших дисков со
стержня A на стержень B можно представить
себе состоящей из двух подзадач размера
n – 1.Сначала нужно переместить n – 1
наименьших дисков со стержня A на
стержень C, оставив на стержне A n-й
наибольший диск. Затем этот диск нужно
переместить с A на B. Потом следует
переместить n – 1 дисков
со стержня C на стержень B. Это перемещение
n – 1 дисков выполняется путем рекурсивного
применения указанного метода. Поменьше
тех,которые в перемещении не участвуют,
не нужно задумываться над тем,что
находится под перемещаемыми дисками
на стержнях A, B или C. Хотя
фактическое перемещение отдельных
дисков не столь очевидно, а моделирование
вручную выполнить непросто из-за
образования стеков рекурсивных вызовов,
с концептуальной точки зрения этот
алгоритм все же довольно прост для
понимания и доказательства его правильно- сти
(а если говорить о быстроте разработки,
то ему вообще нет равных). Именно легкость
разработки алгоритмов по методу
декомпозиции обусловила огромную
популярность этого метода; к тому же
во многих случаях эти алгоритмы
оказываются более эффективными, чем
алгоритмы,
разработанные традиционными методами. Метод
декомпозиции получил широкое применение
не только при разработке алгоритмов,
но и в проектировании электронных схем,
построении математических доказательств
и в других сферах. В качестве иллюстрации
приведем лишь один пример. Рассмотрим
составление расписания проведения
теннисного турнира по круговой схеме
для п = 2* игроков. Каждый игрок должен
сыграть со всеми другими игроками, при
этом каждый игрок должен играть по
одному матчу в день в течение п — 1 дней
— минимального количества дней,
необходимых для проведения всего
турнира.Расписание проведения турнира,
таким образом, представляет собой
таблицу, состоящую из л строк и п — 1
столбцов; элементом на пересечении
строки i
и столбца j
является номер игрока, с которым игрок
i
должен провести матч в у'-й день. Метод
декомпозиции позволяет составить
расписание для половины игроков. Это
расписание составляется на основе
рекурсивного применения данного
алгоритма для половины этой половины
игроков и т.д. Когда количество игроков
будет сокращено до двух, возникнет
"базовая ситуация", в которой мы
просто устанавливаем порядок проведения
встреч между ними. Допустим, в турнире
участвуют восемь игроков. Расписание
для игроков 1 - 4 заполняет верхний левый
угол (4 строкихЗ столбца) составляемого
расписания. Нижний левый угол (4 строкихЗ
столбца) этого расписания должен свести
между собой игроков с более высокими
номерами (5 - 8). Эта часть расписания
получается путем прибавления числа 4
к каждому элементу в верхнем левом
углу. Итак,
нам удалось упростить задачу. Теперь
остается свести между собой игроков с
низкими и более высокими номерами.
Сделать это нетрудно: надо на 4-й день
свести в пары игроков, имеющих номера
1 - 4, с игроками 5-8 соответственно, а в
последующие дни просто циклически
переставлять номера 5 - 8. Этот процесс
показан на рис. 10.3. Надеемся, теперь
читатель сможет обобщить описанный
алгоритм и составить расписание для
2* игроков при любом значении k.
Граф
это множество точек или вершин и
множество линий или ребер, соединяющих
между собой все или часть этих точек.
Вершины,
прилегающие к одному и тому же ребру,
называются смежными.
Если ребра
ориентированны, что обычно показывают
стрелками,
то они называются дугами,
и граф с такими ребрами называется
ориентированным
графом.
Если ребра
не имеют ориентации,
граф называется неориентированным.
Определим граф как конечное множество
вершин V и набор E неупорядоченных и
упорядоченных пар вершин и обозначим
G=(V,E). Мощности множеств V и E будем
обозначать буквами N и M. Неупорядоченная
пара вершин называется ребром, а
упорядоченная пара - дугой. Граф,
содержащий только ребра, называется
неориентированным; граф, содержащий
только дуги, - ориентированным, или
орграфом. Вершины, соединенные ребром,
называются смежными. Ребра, имеющие
общую вершину, также называются смежными.
Ребро и любая из его двух вершин
называются инцидентными. Говорят, что
ребро (u, v) соединяет вершины u и v. Каждый
граф можно представить на плоскости
множеством точек, соответствующих
вершинам, которые соединены линиями,
соответствующими ребрам. В трехмерном
пространстве любой граф можно представить
таким образом, что линии (ребра) не будут
пересекаться.
Способы
описания. Выбор
соответствующей структуры данных для
представления графа имеет принципиальное
значение при разработке эффективных
алгоритмов. При решении задач используются
следующие четыре основных способа
описания графа: матрица инциденций;
матрица смежности; списки связи и
перечни ребер. Мы будем использовать
только два: матрицу смежности и перечень
ребер. Матрица
смежности - это двумерный массив
размерности N*N. A[i,j]= Для
хранения перечня ребер необходим
двумерный массив R
размерности
M*2. Строка массива описывает ребро. Множество
алгоритмов на графах требует просмотра
вершин графа. Рассмотрим их. Поиск
в глубину Идея
метода.
Поиск начинается с некоторой фиксированной
вершины v. Рассматривается вершина u,
смежная с v. Она выбирается. Процесс
повторяется с вершиной u. Если на
очередном шаге мы работаем с вершиной
q и нет вершин, смежных с q и не рассмотренных
ранее (новых), то возвращаемся из вершины
q к вершине, которая была до нее. В том
случае, когда это вершина v, процесс
просмотра закончен. Очевидно, что для
фиксации признака, просмотрена вершина
графа или нет, требуется структура
данных типа: Nnew
: array[1..N] of boolean.
Пример.
Пусть граф описан матрицей смежности
A. Поиск начинается с первой вершины.
На левом рисунке приведен исходный
граф, а на правом рисунке у вершин в
скобках указана та очередность, в
которой вершины графа просматривались
в процессе поиска в глубину.
Логика. procedure
Pg(v:integer);{Массивы Nnew и A глобальные} var
j:integer; begin Nnew[v]:=false;
write(v:3); for
j:=1 to N do if (A[v,j]<>0) and Nnew[j] then Pg(j); end; Фрагмент
основной
логики. ... FillChar(Nnew,SizeOf(Nnew),true); for
i:=1 to N do if Nnew[i] then Pg(i); ... В
силу важности данного алгоритма
рассмотрим его нерекурсивную реализацию.
Глобальные структуры данных прежние:
A - матрица смежностей; Nnew - массив
признаков. Номера просмотренных вершин
графа запоминаются в стеке St, указатель
стека - переменная yk. procedure
PG1(v:integer); var
St:array[1..N] of integer;yk:integer;t,j:integer;pp:boolean; begin
FillChar(St,SizeOf(St),0);
yk:=0; Inc(yk);St[yk]:=v;Nnew[v]:=false; while
yk<>0 do begin {пока стек не пуст} t:=St[yk]; {выбор
“самой верхней“ вершины из стека} j:=0;pp:=false; repeat if
(A[t,j+1] <>0) and Nnew[j+1] then pp:=true
else
Inc(j); until
pp or (j>=N); {найдена новая вершина или
все вершины, связанные с данной
вершиной, просмотрены}
if
pp then begin
Inc(yk);St[yk]:=j+1;Nnew[j+1]:=false;{добавляем
в
стек} end
else Dec(yk); {“убираем” вершину из стека} end; end; Поиск
в ширину Идея
метода.
Суть (в сжатой формулировке) заключается
в том, чтобы рассмотреть все вершины,
связанные с текущей. Принцип выбора
следующей вершины - выбирается та,
которая была раньше рассмотрена. Для
реализации данного принципа необходима
структура данных “очередь”.
Пример.
Исходный граф на левом рисунке. На
правом рисунке рядом с вершинами в
скобках указана очередность просмотра
вершин графа. Приведем
процедуру реализации данного метода
обхода вершин графа.Кратчайшие пути Кратчайший
путь
из u
в v
– это любой путь p из u
в v,
для которого w(p)
= δ(u,
v),
где:w(p)
= сумма весов всех ребер пути p;
δ(u,
v)
= min{w(p):
по всем путям p
из u
в v},
если существует путь из u
в v;
∞ - в противном случае Алгоритм
Дейкстры Алгоритм
Дейкстры решает задачу о кратчайших
путях из одной вершины для взвешенного
ориентированного графа G
= (V,
E)
с исходной вершиной s,
в котором веса всех ребер неотрицательны. Алгоритм.d[u]
– расстояние до вершины u
из исходной вершины s Q
– очередь с приоритетами. Извлекается
вершина, для которой d[u]
минимально Initialize-Single-Source(G,
s)
for
(для) всех вершин v
из V[G] do
d[v]
= ∞
родитель[v]
= NIL d[s]
= 0 Relax(u,
v, w) if
d[v] > d[u] + w(u, v) then
d[v] = d[u] + w(u, v) предок[v]
= u Dijkstra(G,
w, s)
Initialize-Single-Source(G,
s)
S
= Ø Q
← V[G] while
Q <> Ø do
u
← извлечь вершину, для которой d[u]
минимально
S
= S
U
{u}
for
(для) всех вершин v
смежных с u
do
Relax(u, v, w) Суть
алгоритма. d[u]
– эта оценка пути по каждой из вершин
на данном шаге. Изначально нам известно,
что до исходной вершины путь 0, до
остальных оценка на первом шаге
бесконечность. На каждом шаге берем
вершину u
для которой оценка минимальна. Для неё
можно доказать, что оценка является
кратчайшим путем. Для смежных с ней
вершин v,
если оценка через вершину u
оказывается меньше, чем текущая оценка
вершины v,
то уменьшаем оценку (релаксация).
16.Методы разработки алгоритмов. Метод декомпозиции.
14.Алгоритмы решения задач на графах.
