mirror of
https://github.com/krahets/hello-algo.git
synced 2026-07-13 07:46:06 +00:00
build
This commit is contained in:
@@ -4,31 +4,31 @@ comments: true
|
||||
|
||||
# 2.4 Пространственная сложность
|
||||
|
||||
<u>Пространственная сложность (space complexity)</u> используется для оценки того, как меняется объем памяти, занимаемой алгоритмом, по мере роста объема данных. Это понятие очень похоже на временную сложность, только вместо "времени выполнения" мы рассматриваем "объем используемой памяти".
|
||||
<u>Пространственная сложность (space complexity)</u> служит для оценки того, как меняется объем памяти, требуемой алгоритму, по мере роста объема данных. Это понятие очень похоже на временную сложность, только вместо времени выполнения рассматривается объем используемой памяти.
|
||||
|
||||
## 2.4.1 Пространство, связанное с алгоритмом
|
||||
|
||||
Память, которую использует алгоритм во время работы, в основном включает несколько следующих частей.
|
||||
Память, которую использует алгоритм во время работы, в основном делится на следующие части.
|
||||
|
||||
- **Входное пространство**: используется для хранения входных данных алгоритма.
|
||||
- **Временное пространство**: используется для хранения переменных, объектов, контекста функций и других данных, возникающих во время выполнения алгоритма.
|
||||
- **Выходное пространство**: используется для хранения выходных данных алгоритма.
|
||||
|
||||
В общем случае при анализе пространственной сложности в расчет включают "временное пространство" и "выходное пространство".
|
||||
Как правило, при анализе пространственной сложности в расчет включают временное пространство и выходное пространство.
|
||||
|
||||
Временное пространство можно дополнительно разделить на три части.
|
||||
|
||||
- **Временные данные**: используются для хранения различных констант, переменных, объектов и т.д., возникающих во время выполнения алгоритма.
|
||||
- **Пространство кадров стека**: используется для хранения контекстных данных вызываемых функций. Система при каждом вызове функции создает на вершине стека новый кадр; после возврата функции пространство этого кадра освобождается.
|
||||
- **Пространство кадров стека**: используется для хранения контекстных данных вызываемых функций. При каждом вызове функции система создает на вершине стека новый кадр; после возврата функции пространство этого кадра освобождается.
|
||||
- **Пространство инструкций**: используется для хранения скомпилированных инструкций программы и в реальном подсчете обычно не учитывается.
|
||||
|
||||
При анализе пространственной сложности программы **мы обычно учитываем три части: временные данные, пространство кадров стека и выходные данные**, как показано на рисунке 2-15.
|
||||
При анализе пространственной сложности программы **обычно учитываются временные данные, пространство стека и выходные данные**, как показано на рисунке 2-15.
|
||||
|
||||
{ class="animation-figure" }
|
||||
|
||||
<p align="center"> Рисунок 2-15 Пространство, используемое алгоритмом </p>
|
||||
|
||||
Соответствующий код выглядит следующим образом:
|
||||
Ниже приведен соответствующий код:
|
||||
|
||||
=== "Python"
|
||||
|
||||
@@ -369,14 +369,14 @@ comments: true
|
||||
|
||||
## 2.4.2 Метод вывода
|
||||
|
||||
Метод вывода пространственной сложности в целом аналогичен временному анализу: меняется только объект подсчета, с "количества операций" на "размер используемого пространства".
|
||||
Метод вывода пространственной сложности в целом аналогичен выводу временной сложности: меняется только объект подсчета, с количества операций на размер используемого пространства.
|
||||
|
||||
В отличие от временной сложности, **обычно мы рассматриваем только худшую пространственную сложность**. Это связано с тем, что память является жестким ограничением: нам нужно гарантировать, что для любых входных данных у программы будет достаточно памяти.
|
||||
В отличие от временной сложности, **обычно рассматривается только худшая пространственная сложность**. Это связано с тем, что память является жестким ограничением: необходимо гарантировать, что для любых входных данных у программы будет достаточно памяти.
|
||||
|
||||
Рассмотрим следующий код. Слово "худшая" в "худшей пространственной сложности" имеет два значения.
|
||||
Рассмотрим следующий код. Понятие худшей пространственной сложности здесь имеет два значения.
|
||||
|
||||
1. **Ориентир на худшие входные данные**: когда $n < 10$ , пространственная сложность равна $O(1)$ ; но когда $n > 10$ , инициализированный массив `nums` занимает $O(n)$ пространства, поэтому худшая пространственная сложность равна $O(n)$ .
|
||||
2. **Ориентир на пиковое потребление памяти во время выполнения алгоритма**: например, до выполнения последней строки программа занимает $O(1)$ пространства; при инициализации массива `nums` она занимает $O(n)$ пространства, поэтому худшая пространственная сложность равна $O(n)$ .
|
||||
2. **Ориентир на пиковое использование памяти во время выполнения**: например, до выполнения последней строки программа занимает $O(1)$ пространства; при инициализации массива `nums` она занимает $O(n)$ пространства, поэтому худшая пространственная сложность также равна $O(n)$ .
|
||||
|
||||
=== "Python"
|
||||
|
||||
@@ -808,7 +808,7 @@ comments: true
|
||||
|
||||
## 2.4.3 Распространенные типы
|
||||
|
||||
Пусть размер входных данных равен $n$ . На рисунке 2-16 показаны распространенные типы пространственной сложности (в порядке от меньшей к большей).
|
||||
Пусть размер входных данных равен $n$ . На рисунке 2-16 показаны распространенные типы пространственной сложности в порядке от меньшей к большей.
|
||||
|
||||
$$
|
||||
\begin{aligned}
|
||||
@@ -823,7 +823,7 @@ $$
|
||||
|
||||
### 1. Постоянная сложность $O(1)$ {data-toc-label="1. Постоянная сложность"}
|
||||
|
||||
Постоянная сложность часто встречается у констант, переменных и объектов, количество которых не зависит от размера входных данных $n$ .
|
||||
Постоянная сложность обычно встречается у констант, переменных и объектов, количество которых не зависит от размера входных данных $n$ .
|
||||
|
||||
Следует заметить, что память, занятая инициализацией переменных или вызовом функций внутри цикла, освобождается при переходе к следующей итерации, поэтому она не накапливается, и пространственная сложность по-прежнему остается $O(1)$ :
|
||||
|
||||
@@ -1184,7 +1184,7 @@ $$
|
||||
|
||||
### 2. Линейная сложность $O(n)$ {data-toc-label="2. Линейная сложность"}
|
||||
|
||||
Линейная сложность часто встречается у массивов, связных списков, стеков, очередей и других структур, число элементов в которых пропорционально $n$ :
|
||||
Линейная сложность часто встречается у массивов, списков, стеков, очередей и других структур, число элементов в которых пропорционально $n$ :
|
||||
|
||||
=== "Python"
|
||||
|
||||
@@ -1607,7 +1607,19 @@ $$
|
||||
=== "Ruby"
|
||||
|
||||
```ruby title="space_complexity.rb"
|
||||
### Линейная сложность (рекурсивная реализация) ###
|
||||
### Линейная сложность ###
|
||||
def linear(n)
|
||||
# Список длины n занимает O(n) памяти
|
||||
nums = Array.new(n, 0)
|
||||
|
||||
# Хеш-таблица длины n занимает O(n) памяти
|
||||
hmap = {}
|
||||
for i in 0...n
|
||||
hmap[i] = i.to_s
|
||||
end
|
||||
end
|
||||
|
||||
# ## Линейная сложность (рекурсивная реализация) ###
|
||||
def linear_recur(n)
|
||||
puts "Рекурсия n = #{n}"
|
||||
return if n == 1
|
||||
@@ -2019,7 +2031,13 @@ $$
|
||||
=== "Ruby"
|
||||
|
||||
```ruby title="space_complexity.rb"
|
||||
### Квадратичная сложность (рекурсивная реализация) ###
|
||||
### Квадратичная сложность ###
|
||||
def quadratic(n)
|
||||
# Двумерный список занимает O(n^2) памяти
|
||||
Array.new(n) { Array.new(n, 0) }
|
||||
end
|
||||
|
||||
# ## Квадратичная сложность (рекурсивная реализация) ###
|
||||
def quadratic_recur(n)
|
||||
return 0 unless n > 0
|
||||
|
||||
@@ -2040,7 +2058,7 @@ $$
|
||||
|
||||
### 4. Экспоненциальная сложность $O(2^n)$ {data-toc-label="4. Экспоненциальная сложность"}
|
||||
|
||||
Экспоненциальная сложность часто встречается у бинарных деревьев. Обрати внимание на рисунок 2-19: "полное бинарное дерево" с $n$ уровнями содержит $2^n - 1$ узлов и занимает $O(2^n)$ пространства:
|
||||
Экспоненциальная сложность часто встречается у бинарных деревьев. Полное бинарное дерево с $n$ уровнями содержит $2^n - 1$ узлов и занимает $O(2^n)$ пространства:
|
||||
|
||||
=== "Python"
|
||||
|
||||
@@ -2212,7 +2230,22 @@ $$
|
||||
=== "Ruby"
|
||||
|
||||
```ruby title="space_complexity.rb"
|
||||
### Экспоненциальная сложность (построение полного двоичного дерева) ###
|
||||
### Квадратичная сложность ###
|
||||
def quadratic(n)
|
||||
# Двумерный список занимает O(n^2) памяти
|
||||
Array.new(n) { Array.new(n, 0) }
|
||||
end
|
||||
|
||||
# ## Квадратичная сложность (рекурсивная реализация) ###
|
||||
def quadratic_recur(n)
|
||||
return 0 unless n > 0
|
||||
|
||||
# Длина массива nums равна n, n-1, ..., 2, 1
|
||||
nums = Array.new(n, 0)
|
||||
quadratic_recur(n - 1)
|
||||
end
|
||||
|
||||
# ## Экспоненциальная сложность (построение полного двоичного дерева) ###
|
||||
def build_tree(n)
|
||||
return if n == 0
|
||||
|
||||
@@ -2234,14 +2267,14 @@ $$
|
||||
|
||||
### 5. Логарифмическая сложность $O(\log n)$ {data-toc-label="5. Логарифмическая сложность"}
|
||||
|
||||
Логарифмическая сложность часто встречается в алгоритмах "разделяй и властвуй". Например, при сортировке слиянием входной массив длины $n$ на каждом шаге рекурсии делится пополам по середине, образуя рекурсивное дерево высоты $\log n$ и используя $O(\log n)$ пространства кадров стека.
|
||||
Логарифмическая сложность часто встречается в алгоритмах "разделяй и властвуй". Например, при сортировке слиянием входной массив длины $n$ на каждом шаге рекурсии делится пополам, образуя рекурсивное дерево высоты $\log n$ и используя $O(\log n)$ пространства кадров стека.
|
||||
|
||||
Еще один пример - преобразование числа в строку. Если задано положительное целое число $n$ , то количество его цифр равно $\lfloor \log_{10} n \rfloor + 1$ , то есть длина соответствующей строки тоже равна $\lfloor \log_{10} n \rfloor + 1$ , следовательно, пространственная сложность составляет $O(\log_{10} n + 1) = O(\log n)$ .
|
||||
|
||||
## 2.4.4 Компромисс между временем и пространством
|
||||
|
||||
В идеале нам хотелось бы, чтобы и временная, и пространственная сложность алгоритма были оптимальными. Однако на практике одновременно оптимизировать и время, и память обычно очень трудно.
|
||||
В идеальных условиях хотелось бы, чтобы и временная, и пространственная сложность алгоритма были оптимальными. Однако на практике одновременно оптимизировать и время, и память обычно очень трудно.
|
||||
|
||||
**Снижение временной сложности обычно достигается ценой увеличения пространственной сложности, и наоборот**. Подход, при котором мы жертвуем памятью ради ускорения работы алгоритма, называется "обмен пространства на время"; обратный подход называется "обмен времени на пространство".
|
||||
**Снижение временной сложности обычно достигается ценой увеличения пространственной сложности, и наоборот**. Подход, при котором жертвуют памятью ради ускорения работы алгоритма, называется обменом пространства на время; обратный подход называется обменом времени на пространство.
|
||||
|
||||
Выбор между этими двумя идеями зависит от того, что для нас важнее. В большинстве случаев время ценнее памяти, поэтому стратегия "обмена пространства на время" используется чаще. Но при очень больших объемах данных контроль пространственной сложности тоже становится крайне важным.
|
||||
Выбор между этими двумя идеями зависит от того, что важнее в конкретной задаче. В большинстве случаев время ценнее памяти, поэтому стратегия обмена пространства на время используется чаще. Но при очень больших объемах данных контроль пространственной сложности тоже становится крайне важным.
|
||||
|
||||
Reference in New Issue
Block a user