Вы находитесь на странице: 1из 9

Оценка объёма памяти

Вычислительная ёмкостная сложность (space complexity) — это максимальный


возможный размер занятой алгоритмом дополнительной памяти, как функция от
размера входных данных. Это функция. Зависит от размера входных данных.
Возвращает максимум операций/памяти на всех входах такого размера.

Не всегда возможно оценить точный объём памяти необходимый программе или


алгоритму, если существуют динамичные элементы или нетривиальные
конструкции, чья память запрашивается в runtime. Существуют разные стандарты
(ASCII, UTF-8, IEEE-734, ...) и типы представления данных в памяти компьютера (int,
char, float, ...), согласно которым можно вычислить размер памяти, но он может
варьировать от языка к языку.

С/С++

1 байт = 8 бит

Бит - мельчайшая единица измерения информации в двоичной системе счисления.

Размер (в
Тип данных Охват
байтах)
Размер (в
Тип данных Охват
байтах)

short int 2 -32,768 to 32,767


unsigned short int 2 0 to 65,535

unsigned int 4 0 to 4,294,967,295


int 4 -2,147,483,648 to 2,147,483,647
long int 4 -2,147,483,648 to 2,147,483,647
unsigned long int 4 0 to 4,294,967,295
long long int 8 -(2^63) to (2^63)-1

unsigned long
8 0 to 18,446,744,073,709,551,615
long int

signed char 1 -128 to 127


unsigned char 1 0 to 255

float 4 ±3.40282347E+38F i.e. 6-7 significant digits


±1.79769313486231570E+308 i.e. 15-16
double 8
significant digits

long double 12
wchar_t 2-4 1 wide character

Типы данных с плавающей точкой могут лишь увеличивать свою точность, но в виду
некоторых проблем некоторые числа из десятичной системы невозможно
представить в двоичной.

К примеру для оценки размера статичного массива единого типа в C++ необходимо
размер массива разделить на размер элемента (указатели исключение, так как они
весят 4 либо 8 байт в зависимости от машинного слова, а размер массива
недоступен). Для оценки объёма памяти алгоритма со статичными переменными
необходимо учесть размер всех переменных с учётом рекурсий и их глубины, если
таковые имеются.

Тут три ключевых части:

1. Это функция.
2. Зависит от размера входных данных.
3. Возвращает максимум операций/памяти на всех входах такого размера. То есть
для худшего, самого затратного случая.
С++

for (int i=0; i < n; i++) count++;

Посчитаем количество элементарных операций:

1 для int i = 0
n+1 для i < n
2n для i++ (что эквивалентно i = i + 1, а это две операции: присваивание и
сложение)
2n для сount++

Получаем, что временную сложность алгоритма

C(n) = 2 + 5n

Ёмкостную сложность как правило оценивать проще временной.

Скорость алгоритма
Временная сложность алгоритма (в худшем случае) — это функция от
размера входных данных, равная максимальному количеству
элементарных операций, проделываемых алгоритмом для решения
экземпляра задачи указанного размера.
Скорость алгоритма может иметь лучший, средний и худший случай и скорость
принято обозначать через O().

К примеру скорость хеш-таблицы с хорошей противоколлизией равна в лучшем


и среднем случае О(1), а в худшем O(n), что означает что все элементы массива
попали в единственную ячейку.

Есть алгоритмы с постоянной скоростью, к примеру пузырьковая сортировка


независимо от случае займёт О(n^2) времени.
Могут быть алгоритмы, выполняющие одинаковую задачу, но разные по
скорости, к примеру бинарный поиск по отсортированному массиву займёт
O(ln(n)) в среднем и худшем, а в лучшем O(1). Линейный поиск в лучшем О(1), но
в отличии от бинарного в среднем и худшем О(n), что означает перебор всего
массива.

Даже зная n (число шагов), невозможно точно определить точное время


выполнения алгоритма без тригеров, так как оно зависит от железа, ОС, ПО, языка,
метода хранения переменных, нагрузки на систему так и от самих драйверов,
отвечающих за методы взаимодействия с железом.

Пример разницы скоростей

Классификация
Сложность Значение при
Тип зависимости 10
O(f (n)) n = 2 = 1024

O(1) Постоянная 1

O(n) Линейная 1024

O(n ∗ log2n) Логорифмическая 10240


Сложность Значение при
Тип зависимости 10
O(f (n)) n = 2 = 1024

2
6
O(n )
Полиномиальная  10

3 (геометрическая) 9
O(n ) 10

n
O(2 ) Экспоненциальная  10
300

Циклы
Существуют два способа создания цикла: итеративный и рекурсивный.

Итеративный цикл использует цикл с итератором, то есть любой переменной


зачасную числового типа, чьё значение растёт на произвольное число после
каждого оборота. Переменные сохраняют результат деятельности цикла.
Рекурсивный подход тратит больше памяти и иногда времени, но и занимает
маньше места по объёму кода и некоторые задачи без него не решить, как к
примеру перебор графа. Рекурсивная функция вызывает сама себя, пока не
встретит условие, при котором перестанет это делать и совокупность
результатов вызовов станет результатом её деятельности. Рекурсивная
функция возвращает значение, а переменные покидают область видимости,
соответственно они уничтожаются. Рекурсии иногда в некоторых языках имеют
лимит глубины стека.

К примеру если писать на Python факториал итератором, то

fact = 1; n = 10

while n >= 1: fact *= n

а рекурсией

def fact(n): return 1 if n <= 1 else n*fact(n-1)

Результатом обоих алгоритмов, при n = 10, будет число 3628800, что подтверждает
их сходство.

Метод полного перебора


Под полным перебором понимается методика разрешения задач математики путем
рассмотрения всех возможных вариантов. Уровень сложности при полном переборе
напрямую связан с количеством допустимых решений задачи. В случае, когда
область решений огромна, время полного перебора может исчисляться десятками и
даже сотнями лет, и при этом итоговый результат возможно ещё не будет найден.
Все задачи класса NP (non-deterministic polynomial) могут быть решены при помощи
полного перебора. В криптографии оценивается криптографическая стойкость
шифрования на базе вычислительной сложности полного перебора. Например,
криптостойкость шифра будет на должном уровне, если нет методики его взлома,
которая позволяет это сделать быстрее, чем путём полного перебора всего
диапазона возможных ключей. К таким алгоритмам можно для примера отнести
RSA, построенный на степенях и остатках от деления невероятно больших чисел.
Ввиду наличия тысяч чисел с таким же остатком, вычислить приватный ключ можно
зачастую лишь полным перебором. Хакерские атаки в области криптографии,
которые основаны на методике полного перебора, считаются наиболее
универсальными, но при этом требуют максимальных временных затрат
(bruteforce). Такой метод взлома используют иногда при попытке взломать аккаунты
без кода подтверждения или wifi.

Метод Greedy
Greedy Algorithm - это алгоритмическая стратегия, используемая для того, чтобы
сделать наилучший необязательный выбор на очень маленькой стадии и в конечном
итоге вывести глобально оптимальное решение. Этот алгоритм выбирает лучшее
решение, которое возможно в данный момент, без учета каких-либо последствий.
Жадный метод говорит, что проблема должна решаться поэтапно, при этом каждый
вход рассматривается с учетом того, что этот вклад возможен. Поскольку этот
подход фокусируется только на немедленном результате без учета общей картины,
он считается жадным.

Метод перехода с возвратом


Это общий метод нахождения решений задачи, в которой требуется полный перебор
всех возможных вариантов в некотором множестве М. Решение задачи методом
поиска с возвратом сводится к последовательному расширению частичного
решения. Если на очередном шаге такое расширение провести не удается, то
возвращаются к более короткому частичному решению и продолжают поиск
дальше. Данный алгоритм позволяет найти все решения поставленной задачи, если
они существуют. Для ускорения метода стараются вычисления организовать таким
образом, чтобы как можно раньше выявлять заведомо неподходящие варианты.
Зачастую это позволяет значительно уменьшить время нахождения решения.
Незначительные модификации метода поиска с возвратом, связанные с
представлением данных или особенностями реализации, имеют и иные названия:
метод ветвей и границ, поиск в глубину, метод проб и ошибок и т. д.

Визуализировать данный метод можно при помощи графа, имеющего пути решения
и метод предполагает перебор всех возможных путей по очереди и в случае
отсутствия дальнейшего пути вернуться на 1 ребро назад для аналогичной
рекурсивной проверки.

Классическим примером использования алгоритма поиска с возвратом является


задача о восьми ферзях. Её формулировка такова: «Расставить на стандартной 64-
клеточной шахматной доске 8 ферзей так, чтобы ни один из них не находился под боем
другого». Сперва на доску ставят одного ферзя, а потом пытаются поставить
каждого следующего ферзя так, чтобы его не били уже установленные ферзи. Если
на очередном шаге такую установку сделать нельзя — возвращаются на шаг назад
и пытаются поставить ранее установленного ферзя на другое место.

Помимо этого, метод поиска с возвратом позволяет решать множество других


переборных задач. Например, с помощью него можно получить все подмножества,
размещения, перестановки, сочетания данного множества М.

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


проектировать и программировать алгоритмы решения задач с использованием
этого метода. Однако время нахождения решения может быть очень велико даже
при небольших размерностях задачи (количестве исходных данных), причём
настолько велико (может составлять годы или даже века), что о практическом
применении не может быть и речи. Поэтому при проектировании таких алгоритмов,
обязательно нужно теоретически оценивать время их работы на конкретных
данных.

Для создания алгоритма, соответствующего такому методу, могут использовать


goto возвраты к флагам или рекурсии.

Метод "разделяй и властвуй"


Он коротко и ясно описан в книге "Грокаем Алгоритмы". Цитируя издание могу
сказать, что алгоритмы на базе этой стратегии являются рекурсивными.

Решение задачи методом "разделяй и властвуй" состоит из двух шагов:

1. Сначала определяется базовый случай. Это должен быть простейший случай из


всех возможных.
2. Задача делится или сокращается до тех пор, пока не будет сведена к базовому
случаю.

Вам также может понравиться