Архитектура Аудит Военная наука Иностранные языки Медицина Металлургия Метрология
Образование Политология Производство Психология Стандартизация Технологии


Элементарные и неэлементарные операции



 

Пример: цикл for – это неэлементарная операции языка.

int a[10];

int s = 0;

for (int i = 0; i < 10 & & a[i] % 10! = 5; i++) s += a[i];

 

Массив – это неэлементарный тип, в данном примере его представитель a.

int – это элементарный тип, в данном примере его представитель s.

Элементарная операция присваивания (инициализации): s = 0.

for – это неэлементарная операция, состоящая из операций присваивания (i = 0), двух операций сравнения, и т. д.

 

Представление типов

 

Целые числа – двоичное представление.

 

Простые элементарные операции: сложение, вычитание, присваивание и т.д.

 

Сложные элементарные операции: целочисленное умножение, деление (не на степень двойки), переход.

 

Приведём пример кода:

if (x > 0) t = 1;

else t = 0;

Этот код выполнится выполняется намного медленнее, чем t = x > 0; (потому что для t = x > 0; есть машинная команда).

 

Инварианты. Индуктивные функции

Индуктивное программирование. Индуктивные функции

 

Пусть имеется множество M. Пусть аргументами функции f будут последовательности элементов множества M, а значениями – элементы множества N.

 

Тогда, если её значение на последовательности x1, x2, ..., xn можно восстановить по её значению на последовательности x1, x2, ..., xn-1 и xn, то такая функция называется индуктивной.

 

Пример: Если мы хотим найти наибольшее значение среди всех элементов последовательности, то функция maximum индуктивна, т.к. maximum (x1, x2, ..., xn) = max (maximum (x1, x2, ..., xn-1), xn)

 

Инварианты и предикаты алгоритма

 

Значение функции для подмножества является инвариантом, т.е. предикатом, значение которого всегда истинно.

int m = a[0];

for (int i = 1; i < N; i++)

{

if (a[i] > m) m = a[i];

}

Предикат: ∀ i < N: mi есть наибольшее значение из элементов a[0]... a[i], т.е. mi = maximum.

∀ – это квантор общности.

 

Абстракции. Интерфейс абстракции

Понятие абстракции

 

Как только появляются объекты, появляются абстракции – механизм разделения сложных объектов на более простые, без деталировки подробностей разделения.

 

Функциональная абстракция – разделение функций, методов, которые манипулируют с объектами, с их реализацией.

 

Интерфейс абстракции – набор методов, характерных для данной абстракции.

 

Пример: абстракция массива

 

1) create – создать массив (статический или динамический).

int a[100]; // статический

int *b = calloc(100, sizeof(int)); // динамический

int *c = new int[100]; // динамический

2) destroy – удалить массив (статический или динамический).

free(b);

delete c; // можно и delete [] c

3) fetch – обратиться к элементу массива.

int q1 = a[i];

int q2 = b[i];

int q3 = c[i];

 

Для массива основная операция – это доступ к элементу. Она выглядит одинаково для всех представлений.

 

Абстракция “стек”

 

Одна из удобных абстракций – стек.

 

Стек должен предоставлять нам методы:

1) create – создание стека (может быть, потребуется аргумент, определяющий максимальный размер стека);

2) push – занесение элемента в стек (размер стека увеличивается на единицу, занесённый элемент становится вершиной стека);

3) pop – извлечь элемент, являющийся вершиной стека и уменьшить размер стека на единицу (если стек пуст, то значение операции не определено);

4) peek – получить значение элемента, находящегося на вершине стека, не изменяя стека (если стек пуст, значение операции не определено);

5) empty – предикат истинен, когда стек пуст;

6) destroy – уничтожить стек.

 

Абстракция “множество”

 

Множество – это совокупность однотипных элементов, на которых определена операция сравнения.

 

Множество обозначается списком значений внутри фигурных скобок.

 

Пустое множество: s = {}.

 

Множество должно предоставлять нам методы:

 

1) insert – добавление элемента в множество.

{1, 2, 3}.insert(5) -> {1, 2, 3, 5}

{1, 2, 3}.insert{2} -> {1, 2, 3}

2) remove – удалить элемент из множества.

{1, 2, 3}.remove(3) -> {1, 2}

{1, 2, 3}.remove(5) -> не определено.

3) in – определить принадлежность множеству.

{1, 2, 3}.in(2) -> true

{1, 2, 3}.in(5) -> false

4) size – определить количество элементов в множестве.

 

Рекурсия. Принцип разделяй и властвуй

Числа Фибоначчи. Рекуррентная форма

 

{0, 1, 1, 2, 3, 5, 8, 13, 21, 34, ...}

 

Рекуррентная форма определения:

 

Много алгоритмов первично определяются рекуррентными зависимостями.

 

Рекуррентность и рекурсия

 

Рекуррентная форма -> рекурсивный алгоритм.

int fibo(int n)

{

if (n == 0) return 0;

if (n == 1) return 1;

return fibo(n-1) + fibo(n-2);

}

 

Три вопроса:

1) корректен ли он?

2) как оценить время его работы?

3) как его ускорить?

 

Дерево вызовов функции для n = 5

 

 

Оценка времени вычисления алгоритма

 

Пусть t(n) – количество вызовов функции для аргумента n.

 

t(0) = 1

t(0) > F0

t(1) = 1

t(1) = F1

 

Для n > 1:

t(n) = t(n - 1) + t(n - 2) ≥ Fn

 

Оценка требуемой для исполнения памяти

 

Каждый вызов функции создаёт новый контекст функции или фрейм вызова.

 

Каждый фрейм вызова содержит все аргументы, локальные переменные и служебную информацию.

 

Максимально создаётся количество фреймов, равное глубине рекурсии.

 

Сложность алгоритма по занимаемой памяти равна O(N).

 


Поделиться:



Последнее изменение этой страницы: 2017-03-17; Просмотров: 527; Нарушение авторского права страницы


lektsia.com 2007 - 2024 год. Все материалы представленные на сайте исключительно с целью ознакомления читателями и не преследуют коммерческих целей или нарушение авторских прав! (0.021 с.)
Главная | Случайная страница | Обратная связь