Uploaded by Елена Кондратёнок

Лекция 1 Сложность алгоритмов

advertisement
1
Лекция1. Введение в дисциплину
1. Введение
2. Логическая и физическая структуры данных
3. Основные операции над структурами данных
4. Анализ сложности алгоритмов
1. Введение
Основной характеристикой алгоритма обработки данных является его производительность.
Наиболее универсальной характеристикой производительности является время выполнения
программы, которое зависит от скорости выполнения операций и количества операций в
программе
M
t =  ti N i
(1)
i =1
Где M-количество типов операций в программе;
ti – время выполнения операции i-го типа;
Ni – количество операций i-го типа в программе.
В таблице 1 приведены значения времени в секундах и их перевод в привычное время.
Секунды
102
1,7 минут
4
10
2,8 часа
5
10
1,1 дня
106
1,6 недели
7
10
3,8 месяца
108
3,1 года
9
10
3,1 десятилетия
1010
3,1 столетия
11
10
никогда
Из формулы (1) следует, что для оптимизации кода по времени требуется
1. Уменьшать количество операций в программе
2. Использовать операции с минимальным временем выполнения.
Уменьшать количество типов операций в программе не всегда возможно, так как типы операций
определяются требованиями к ПО – исходными данными и требуемыми результатами.
Основной целью дисциплины СТРУКТУРЫ И АЛГОРИТМЫ ОБРАБОТКИ ДАННЫХ
является изучение способов представления исходных данных и оптимизация их обработки с
целью уменьшения времени выполнения программы.
Объем дисциплины
Лекции – 34 часа (17 лекций)
Лабораторные работы – 34 часа для ПОИТ
18 часов для ИСИТ
Курсовой проект
Зачет
2. Логическая и физическая структуры данных
Структурой данных (data structure) называют совокупность (множество) данных и отношений
между ними. Один и тот же набор данных можно представить по-разному. Пусть имеется пять
данных одного типа типа: А, B, С, D и Е. Из этой совокупности значений можно сформировать
как линейную последовательность элементов, так и дерево или сеть, как показано на рисунке 1.1.
2
Рисунок 1.1 – Представление множества из пяти элементов разными структурами:
а – линейная последовательность, б – дерево, в - сеть
Как видно из рисунка 1.1, способ построения структур данных определяется характером связей
между элементами. Все связи одного элемента данных с другими образуют элемент отношений.
Пару, содержащую элемент данных и ассоциированный с ним элемент отношений, называют
элементом структуры данных. Заметим, что элементом структуры данных может быть другая
структура данных, например, элементом сети может быть запись.
Графическое представление структур данных, подобное представлению на рисунке 1.1
называется графом. Представление в виде графа часто используют на практике для показа
логической структуры. Вершины графа соответствуют элементам данных, а ребра – отношениям
между этими элементами.
Используя термин «структура данных», следует различать понятия логической и
физической структур.
Логическая структура - это абстрактная схема расположения данных, которую
представляет себе пользователь или программист.
Физическая структура – это способ (или схема) конкретного размещения данных в памяти
вычислительной машины. Физическую структуру иногда называют структурой хранения. В
общем случае логическая и физическая структуры одних и тех же данных не совпадают.
Например, последовательность записей, имеющая логическую структуру, изображенную на
рисунке 1.2, представляется программисту как непрерывная последовательность строк
одинакового размера. Однако при хранении на диске в виде файла эти записи могут
располагаться не «вплотную» друг к другу. Коды логически смежных записей могут размещаться
в далеко отстоящих друг от друга физических областях диска, при этом между такими записями
будут размещены другие файлы (или части других файлов). Такая «разбросанность» разных
частей (фрагментов) одного и того же файла по разным физическим участкам диска называется
фрагментацией.
Запись 1
Запись 2
...
Запись N
Рисунок 1.2 – Логическая структура таблицы
Логическая структура двумерного массива чисел - это прямоугольная двумерная фигура
элементов или матрица, в которой каждый элемент однозначно идентифицируется парой
индексов строки и столбца, на пересечении которых он находится. Физической же структурой
двумерного массива является линейная последовательность ячеек оперативной памяти
компьютера, каждая из которых однозначно определяется своим единственным адресом. На
рисунке 1.3 показаны логическая и физическая структуры двумерного массива, состоящего их
трех строк и двух столбцов.
Одна и та же логическая структура может по-разному храниться в памяти разных ЭВМ
(различная конфигурация памяти) или для разных компиляторов.
3
Ячейка ОЗУ
a
x [0, 0]
x [1, 0]
x [2, 0]
x [0, 1]
x [1, 1]
x [2, 1]
Адрес (смещение)
б
x [0, 0]
$0A56
x [0, 1]
$0A58
x [1, 0]
$0A5А
x [1, 1]
$0A5С
x [2, 0]
$0A5Е
x [2, 1]
$0A60
Рисунок 1.3 – Логическая (а) и физическая (б) структуры матрицы
3. Основные операции над структурами данных
К основным операциям над структурами данных относятся следующие операции:
- формирование структуры;
- вставка элемента структуры
- удаление элемента структуры;
- сдвиг;
- изменение содержимого элемента;
- сортировка;
- просмотр данных в структуре.
Большинство из перечисленных операций связано с корректировкой (updating) структуры
данных. Под корректировкой структуры данных понимают алгоритм, применение которого
позволяет изменить содержимое отдельных элементов структуры, либо сами структуры
(количество элементов, характер отношений между элементами).
Формирование структуры – это создание в памяти компьютера физической структуры данных,
соответствующей ее логическому представлению.
Вставка элемента структуры(insertion) - это ввод нового данного в структуру данных. При
вставке указываются элементы, между которыми в логической структуре расположится новый
элемент; эти элементы определяют точку вставки. Хотя на расположение точки вставки могут
быть наложены ограничения (например, включение в очередь возможно только со стороны
«хвоста»), обычно, употребляя термин «вставка», подразумевают возможность включения нового
элемента в любое место исходной структуры. При вставке в массивах выполняется сдвиг
некоторого количества элементов, чтобы освободить место для вставляемого элемента. Вставка в
динамические структуры такого сдвига не требует: просто изменяются адреса связей без
физического перемещения данных в памяти.
Удаление элемента структуры (deletion)- это исключение некоторого элемента из структуры
данных. При удалении в массивах удаляемый элемент либо помечают как удаленный (такое удаление без физического уничтожения называется логическим удалением –logical deletion), либо
осуществляют сдвиг некоторого количества элементов, при котором в ячейку с адресом
удаляемого элемента заносится значение соседнего сдвигаемого элемента. В динамических
структурах просто изменяются адреса связей без физического перемещения данных, а ячейка, в
которой размещался удаляемый элемент, включается в список свободных ячеек, доступных для
вставки.
Сдвиг (shift)- это перемещение некоторых элементов данных в одном из направлений: либо от
логического начала структуры к ее логическому концу, либо наоборот. При сдвиге сохраняется
порядок следования сдвигаемых элементов.
Изменение содержимого элемента структуры (data modification) – это присваивание этому
элементу нового значения или набора значений.
Сортировка (sorting)- это распределение элементов некоторого множества с целью их
расположения в соответствии с некоторыми правилами. Разновидностью сортировки является
упорядочение данных по возрастанию или убыванию значений некоторого признака или ключа
сортировки. Часто сортировка выполняется как переупорядочение (reordering) ранее
4
упорядоченной последовательности по другому признаку (полю). Сортировка массивов предполагает, что перестановки, приводящие элементы в порядок, должны выполняться «на том же месте».
Сортировка динамической структуры предполагает изменение адресов связей.
Просмотр (scan, pass)- последовательное выполнение над элементами структуры одной и той же
операции, например, сравнение их содержимого с некоторым заданным значением. Просмотр
может выполняться с целью контроля содержимого элементов или для подсчета их числа или для
поиска определенного элемента в структуре данных.
4. Анализ сложности алгоритмов
Сложность алгоритма — это количество ресурсов, необходимых для выполнения всех
операций алгоритма.
Существует ряд важных причин для анализа алгоритмов. Одной из них является
необходимость получения оценок или границ для объема памяти или времени работы, которое
потребуется алгоритму для успешной обработки конкретных данных. Для оценки качества
алгоритма вводится понятие сложность алгоритма, или обратное понятие — эффективность
алгоритма. Чем большее время и объем памяти требуются для реализации алгоритма, тем больше
его сложность и соответственно ниже эффективность. Сложность алгоритма делится на временную
и емкостную.
Временная сложность — это показатель, характеризующий временные затраты на
реализацию алгоритма.
Емкостная сложность — это показатель, характеризующий затраты памяти на те же цели.
В зависимости от конкретной формы этих показателей сложность алгоритма в свою очередь
подразделяется на практическую и теоретическую.
Практическая временная сложность обычно оценивается во временных единицах (секунды,
миллисекунды, количество временных тактов процессора, количество выполнения циклов и т.п.).
Практическая емкостная сложность выражается, как правило, в битах, байтах, словах и т.п.
Основные факторы, от которых может зависеть сложность алгоритма:
− быстродействие компьютера и его емкостные ресурсы (в первую очередь — объем
оперативной памяти). Чем ниже тактовая частота процессора и меньше объем оперативного
запоминающего устройства, тем медленнее выполняются арифметические и логические операции,
тем чаще (для больших задач) приходится обращаться к медленно действующей внешней памяти,
и, следовательно, больше времени уходит на реализацию алгоритма;
− выбранный язык программирования. Задача, запрограммированная, например, на языке
Ассемблера, в общем случае решится быстрее, чем по тому же самому алгоритму, но
запрограммированному на языке более высокого уровня, например на С++ или на Паскале;
− выбранный математический метод решения задачи;
− искусство и опыт программиста. В общем случае по одному и тому же алгоритму опытный
программист напишет более эффективно работающую программу, чем его начинающий коллега.
Лучший способ сравнения эффективностей алгоритмов состоит в сопоставлении их
сложности. Этот метод применим как к временной, так и емкостной сложности
Анализ сложности также позволяет объяснить, как будет вести себя алгоритм при возрастании
входного потока данных. Если алгоритм выполняется одну секунду при 1000 элементах на входе,
то как он себя поведёт, если мы удвоим это значение? Будет работать также быстро, в полтора раза
быстрее или в четыре раза медленнее? В практике программирования такие предсказания крайне
важны. Например, если создан алгоритм для web-приложения, работающего с тысячей
пользователей, и измерено его время выполнения, то используя анализ сложности, можно получить
представление о том, что случится, когда число пользователей возрастёт до двух тысяч.
Оценка сложности алгоритма может производиться следующими способами.
1. Математический.
5
Для некоторых задач можно математически определить зависимость количества операций
алгоритма от объема обрабатываемых данных.
2. Экспериментальный.
Можно экспериментально определить время выполнения алгоритма при различных
количествах обрабатываемых данных и затем построить зависимость времени выполнения
алгоритма от объема обрабатываемых данных и аппроксимировать полученную
зависимость математически.
3. Подсчетом количества операций в программе, реализующей алгоритм.
4.1
Способы оценки сложности алгоритмов. Подсчёт инструкций.
Если в качестве структуры данных используется массив, содержащий n элементов, то с точки
зрения сложности алгоритма обработки этого массива необходимо определить зависимость
количества операций алгоритма от размера массива n.
Пусть дан массив целых чисел А размером n и требуется найти максимальное число в этом
массиве. Программа нахождения максимального числа в массиве имеет вид:
int max=A[0];
for (int i=0; i<n; i++)
{
if (A[i]>max)
max=A[i];
}
Определив количество операций в каждой строке программы можно за писать их в виде
комментария в той же строке. В этом случае программа будет иметь вид:
int max=A[0]; //2
for (int i=0; i<n; i++) //1+2n
{
if (A[i]>max) //n
max=A[i]; // 0 или n
}
Для первой строки в вышериведенном коде выше:
int max=A[0];
требуются две инструкции: для поиска A[0] и для присвоения значения max. Эти две инструкции
будут требоваться алгоритму, вне зависимости от величины n. Инициализация цикла for
предполагает одну операцию присваивания (int i=0), n операций сравнения (i<n) и n операций
инкремента (i++). Всего во второй строке программы производится 1+2n операций. Операция
сравнения if(A[i]>max) производится n раз. Количество выполнений операции присваивания
max=A[i] зависит от расположения чисел в массиве. Минимальное количество выполнения этой
операции 0 раз, максимальное – n раз.
В наилучшем случае, когда операция присваивания max=A[i] ни разу не выполняется функцию
сложности, определяющую зависимость количества операций в программе от размера массива n
можно представить в виде:
f(n)=1+1+2n+n+0 = 2+3n
В наихудшем случае, когда операция присваивания max=A[i] выполняется n раз функцию
сложности можно представить в виде:
f(n)=1+1+2n+n+n=2+4n
6
Из полученных выражений для функции сложности видно, что количество операций в программе,
а значит в алгоритме, линейно возрастает с увеличением размера структуры данных (размера
массива n).
Сложность алгоритма необходимо определить независимо от аппаратной части компьютера,
на котором выполняется программа, реализующая алгоритм. На различных компьютерах
различные операции могут выполняться за разное время. Кроме того, часто бывает очень сложно
определить точный вид функции сложности. Поэтому для характеристики сложности алгоритма
используется асимптотический вид функции сложности, для получения которого в функции
1. отбрасываются все константы (так как при неограниченном росте n они становятся
пренебрежимо малы) .
2. Все коэффициенты при n выбираются равными 1 (так как на интересует не точный вид
зависимости, а ее характер).
3. Выбирается значение n в максимальной степени (так как остальные слагаемые
пренебрежимо малы при неограниченном росте значения n)
4. Сложность записывается в виде О- нотации – Заглавная О и в скобках n в максимальной
степени.
Сложность в О-нотации показывает характер зависимости количества операций в алгоритме от
размера обрабатываемой структуры данных.
5.2 Виды функции сложности алгоритмов
Функция сложности 0(1). В алгоритмах константной сложности большинство операций в
программе выполняется один или несколько раз. Любой алгоритм, всегда требующий независимо
от размера данных одного и того же времени, имеет константную сложность.
Функция сложности O(N). Время работы программы линейно, обычно, когда каждый
элемент входных данных требуется обработать лишь линейное число раз.
Функция сложности O(N2), O(NЗ), O(Na)— полиномиальная функция сложности.
Функция сложности O(log2N), O(N · log2N).Такое время работы имеют те алгоритмы,
которые делят большую проблему на множество небольших, а затем, решив их, объединяют
решения.
Функция сложности O(2N). Экспоненциальная сложность. Такие алгоритмы чаще всего
возникают в результате подхода, именуемого «метод грубой силы».
•
Время выполнения алгоритма с определённой сложностью в зависимости от размера
входных данных при скорости 106 операций в секунду:
7
Download