fix Ru translation (#1894)

* docs(ru): replace prose quotes with guillemets

* docs(ru): replace prose semicolons with periods

* docs(ru): align animation title forms

* docs(ru): align figure and table references
This commit is contained in:
Yudong Jin
2026-04-14 18:10:12 +08:00
committed by GitHub
parent 1d12e2def5
commit 22b3b568ef
94 changed files with 565 additions and 570 deletions
@@ -213,7 +213,7 @@ $$
Анализ временной сложности оценивает не само время выполнения алгоритма, **а тенденцию роста этого времени по мере увеличения объема данных**.
Понятие "тенденции роста времени" выглядит довольно абстрактным, поэтому разберем его на примере. Предположим, размер входных данных равен $n$ , и даны три алгоритма `A` , `B` и `C` :
Понятие «тенденции роста времени» выглядит довольно абстрактным, поэтому разберем его на примере. Предположим, размер входных данных равен $n$ , и даны три алгоритма `A` , `B` и `C` :
=== "Python"
@@ -484,11 +484,11 @@ $$
end
```
Ниже показаны временные сложности трех приведенных выше функций.
На рисунке ниже показаны временные сложности трех приведенных выше функций.
- У алгоритма `A` есть только одна операция вывода, и время его работы не растет с увеличением $n$ . Такую временную сложность называют постоянной.
- В алгоритме `B` операция вывода выполняется в цикле $n$ раз, поэтому время работы растет линейно по мере увеличения $n$ . Такая временная сложность называется линейной.
- В алгоритме `C` операция вывода выполняется $1000000$ раз; хотя время работы велико, оно не зависит от размера входных данных $n$ . Поэтому временная сложность `C` такая же, как у `A` , и тоже является постоянной.
- В алгоритме `C` операция вывода выполняется $1000000$ раз. Хотя время работы велико, оно не зависит от размера входных данных $n$ . Поэтому временная сложность `C` такая же, как у `A` , и тоже является постоянной.
![Тенденции роста времени для алгоритмов A, B и C](time_complexity.assets/time_complexity_simple_example.png)
@@ -683,7 +683,7 @@ $$
end
```
Пусть количество операций алгоритма является функцией от размера входных данных $n$ и обозначается как $T(n)$ ; тогда для приведенной выше функции число операций равно:
Пусть количество операций алгоритма является функцией от размера входных данных $n$ и обозначается как $T(n)$. Тогда для приведенной выше функции число операций равно:
$$
T(n) = 3 + 2n
@@ -691,13 +691,13 @@ $$
$T(n)$ - линейная функция, а это означает, что тенденция роста времени работы линейна, следовательно, временная сложность здесь тоже линейна.
Линейную временную сложность записывают как $O(n)$ ; этот математический символ называется <u>нотацией Big $O$ (big-$O$ notation)</u> и обозначает <u>асимптотическую верхнюю границу (asymptotic upper bound)</u> функции $T(n)$ .
Линейную временную сложность записывают как $O(n)$. Этот математический символ называется <u>нотацией Big $O$ (big-$O$ notation)</u> и обозначает <u>асимптотическую верхнюю границу (asymptotic upper bound)</u> функции $T(n)$ .
Иными словами, анализ временной сложности сводится к определению асимптотической верхней границы числа операций $T(n)$, и у этого понятия есть строгое математическое определение.
!!! note "Асимптотическая верхняя граница функции"
Если существуют положительное действительное число $c$ и действительное число $n_0$ , такие что для всех $n > n_0$ выполняется $T(n) \leq c \cdot f(n)$ , то можно считать, что $f(n)$ задает асимптотическую верхнюю границу для $T(n)$ ; это записывается как $T(n) = O(f(n))$ .
Если существуют положительное действительное число $c$ и действительное число $n_0$ , такие что для всех $n > n_0$ выполняется $T(n) \leq c \cdot f(n)$ , то можно считать, что $f(n)$ задает асимптотическую верхнюю границу для $T(n)$. Это записывается как $T(n) = O(f(n))$ .
Как показано на рисунке ниже, вычислить асимптотическую верхнюю границу - значит найти такую функцию $f(n)$ , что при стремлении $n$ к бесконечности функции $T(n)$ и $f(n)$ имеют один и тот же порядок роста и отличаются только постоянным коэффициентом $c$.
@@ -715,7 +715,7 @@ $T(n)$ - линейная функция, а это означает, что т
1. **Игнорировать константы в $T(n)$**. Они не зависят от $n$ , а значит не влияют на временную сложность.
2. **Опускать все коэффициенты**. Например, циклы на $2n$ раз или $5n + 1$ раз можно упростить до $n$ раз, потому что коэффициент перед $n$ не влияет на временную сложность.
3. **При вложенных циклах использовать умножение**. Общее число операций равно произведению числа операций внешнего и внутреннего циклов; при этом для каждого уровня цикла по-прежнему можно применять приемы из пунктов `1.` и `2.` .
3. **При вложенных циклах использовать умножение**. Общее число операций равно произведению числа операций внешнего и внутреннего циклов. При этом для каждого уровня цикла по-прежнему можно применять приемы из пунктов `1.` и `2.` .
Для заданной функции мы можем использовать перечисленные выше приемы и подсчитать число операций:
@@ -960,7 +960,7 @@ $T(n)$ - линейная функция, а это означает, что т
end
```
Следующая формула показывает результаты подсчета до и после использования перечисленных выше приемов; в обоих случаях выводимая временная сложность равна $O(n^2)$ .
Следующая формула показывает результаты подсчета до и после использования перечисленных выше приемов. В обоих случаях выводимая временная сложность равна $O(n^2)$ .
$$
\begin{aligned}
@@ -988,7 +988,7 @@ $$
## Распространенные типы
Пусть размер входных данных равен $n$ ; распространенные типы временной сложности показаны на рисунке ниже в порядке от меньшей к большей.
Пусть размер входных данных равен $n$. Распространенные типы временной сложности показаны на рисунке ниже в порядке от меньшей к большей.
$$
\begin{aligned}
@@ -1023,7 +1023,7 @@ $$
[file]{time_complexity}-[class]{}-[func]{array_traversal}
```
Стоит отметить, что **размер входных данных $n$ нужно определять конкретно в зависимости от типа входа**. Например, в первом примере переменная $n$ сама является размером входных данных; во втором примере размером данных служит длина массива.
Стоит отметить, что **размер входных данных $n$ нужно определять конкретно в зависимости от типа входа**. Например, в первом примере переменная $n$ сама является размером входных данных. Во втором примере размером данных служит длина массива.
### Квадратичная сложность $O(n^2)$
@@ -1045,9 +1045,9 @@ $$
### Экспоненциальная сложность $O(2^n)$
Типичный пример экспоненциального роста в биологии - деление клеток: в начальном состоянии есть одна клетка, после одного деления их становится 2, после двух делений - 4 и так далее; после $n$ раундов деления клеток становится $2^n$ .
Типичный пример экспоненциального роста в биологии - деление клеток: в начальном состоянии есть одна клетка, после одного деления их становится 2, после двух делений - 4 и так далее. После $n$ раундов деления клеток становится $2^n$ .
На рисунке ниже и в следующем коде моделируется процесс деления клеток; временная сложность равна $O(2^n)$ . Здесь входное значение $n$ обозначает число раундов деления, а возвращаемое значение `count` обозначает общее число делений.
На рисунке ниже и в следующем коде моделируется процесс деления клеток. Временная сложность равна $O(2^n)$ . Здесь входное значение $n$ обозначает число раундов деления, а возвращаемое значение `count` обозначает общее число делений.
```src
[file]{time_complexity}-[class]{}-[func]{exponential}
@@ -1065,9 +1065,9 @@ $$
### Логарифмическая сложность $O(\log n)$
В противоположность экспоненциальной, логарифмическая сложность описывает ситуацию, когда **в каждом раунде размер задачи уменьшается вдвое**. Пусть размер входных данных равен $n$ ; так как на каждом шаге размер уменьшается вдвое, число итераций равно $\log_2 n$ , то есть является обратной функцией к $2^n$ .
В противоположность экспоненциальной, логарифмическая сложность описывает ситуацию, когда **в каждом раунде размер задачи уменьшается вдвое**. Пусть размер входных данных равен $n$. Так как на каждом шаге размер уменьшается вдвое, число итераций равно $\log_2 n$ , то есть является обратной функцией к $2^n$ .
На рисунке ниже и в следующем коде моделируется процесс, в котором **в каждом раунде размер задачи уменьшается вдвое**; временная сложность равна $O(\log_2 n)$ и кратко записывается как $O(\log n)$ :
На рисунке ниже и в следующем коде моделируется процесс, в котором **в каждом раунде размер задачи уменьшается вдвое**. Временная сложность равна $O(\log_2 n)$ и кратко записывается как $O(\log n)$ :
```src
[file]{time_complexity}-[class]{}-[func]{logarithmic}
@@ -1081,11 +1081,11 @@ $$
[file]{time_complexity}-[class]{}-[func]{log_recur}
```
Логарифмическая сложность часто встречается в алгоритмах, основанных на стратегии "разделяй и властвуй", и отражает идеи разбиения на части и упрощения сложной задачи. Она растет медленно и считается одной из самых желательных временных сложностей после константной.
Логарифмическая сложность часто встречается в алгоритмах, основанных на стратегии «разделяй и властвуй», и отражает идеи разбиения на части и упрощения сложной задачи. Она растет медленно и считается одной из самых желательных временных сложностей после константной.
!!! tip "Каково основание у $O(\log n)$ ?"
Точнее говоря, "разделение на $m$ частей" соответствует временной сложности $O(\log_m n)$ . А по формуле перехода к другому основанию логарифма мы получаем равные по сложности выражения с разными основаниями:
Точнее говоря, «разделение на $m$ частей» соответствует временной сложности $O(\log_m n)$ . А по формуле перехода к другому основанию логарифма мы получаем равные по сложности выражения с разными основаниями:
$$
O(\log_m n) = O(\log_k n / \log_k m) = O(\log_k n)
@@ -1127,7 +1127,7 @@ $$
## Худшая, лучшая и средняя временная сложность
**Временная эффективность алгоритма часто не фиксирована, а зависит от распределения входных данных**. Предположим, на вход подается массив `nums` длины $n$ , состоящий из чисел от $1$ до $n$ , каждое из которых встречается ровно один раз; при этом порядок элементов случайно перемешан. Задача состоит в том, чтобы вернуть индекс элемента $1$ . Тогда можно сделать следующие выводы.
**Временная эффективность алгоритма часто не фиксирована, а зависит от распределения входных данных**. Предположим, на вход подается массив `nums` длины $n$ , состоящий из чисел от $1$ до $n$ , каждое из которых встречается ровно один раз. При этом порядок элементов случайно перемешан. Задача состоит в том, чтобы вернуть индекс элемента $1$ . Тогда можно сделать следующие выводы.
- Когда `nums = [?, ?, ..., 1]` , то есть когда последний элемент равен $1$ , нужно полностью пройти по массиву, **что дает худшую временную сложность $O(n)$** .
- Когда `nums = [1, ?, ?, ...]` , то есть когда первый элемент равен $1$ , независимо от длины массива продолжать обход не нужно, **что дает лучшую временную сложность $\Omega(1)$** .
@@ -1140,12 +1140,12 @@ $$
Стоит отметить, что на практике лучшая временная сложность используется редко, поскольку обычно она достигается лишь с очень малой вероятностью и может вводить в заблуждение. **Худшая временная сложность гораздо практичнее, потому что задает безопасную оценку эффективности** и позволяет уверенно использовать алгоритм.
Из приведенного выше примера видно, что худшая и лучшая временные сложности возникают только при особых распределениях данных; вероятность таких случаев может быть низкой, и они не всегда реально отражают эффективность алгоритма. Напротив, **средняя временная сложность способна показать эффективность алгоритма на случайных входных данных** и обозначается символом $\Theta$ .
Из приведенного выше примера видно, что худшая и лучшая временные сложности возникают только при особых распределениях данных. Вероятность таких случаев может быть низкой, и они не всегда реально отражают эффективность алгоритма. Напротив, **средняя временная сложность способна показать эффективность алгоритма на случайных входных данных** и обозначается символом $\Theta$ .
Для некоторых алгоритмов можно относительно просто вывести средний случай при случайном распределении данных. Например, в приведенном выше примере входной массив перемешан, а вероятность появления элемента $1$ на любом индексе одинакова; следовательно, среднее число итераций алгоритма равно половине длины массива, то есть $n / 2$ , а средняя временная сложность равна $\Theta(n / 2) = \Theta(n)$ .
Для некоторых алгоритмов можно относительно просто вывести средний случай при случайном распределении данных. Например, в приведенном выше примере входной массив перемешан, а вероятность появления элемента $1$ на любом индексе одинакова. Следовательно, среднее число итераций алгоритма равно половине длины массива, то есть $n / 2$ , а средняя временная сложность равна $\Theta(n / 2) = \Theta(n)$ .
Однако для более сложных алгоритмов вычислить среднюю временную сложность часто непросто, потому что трудно проанализировать полное математическое ожидание на заданном распределении данных. В таких случаях обычно используют худшую временную сложность как критерий оценки эффективности алгоритма.
!!! question "Почему символ $\Theta$ встречается так редко?"
Возможно, потому что символ $O$ звучит слишком привычно, и мы часто используем его для обозначения средней временной сложности. Но строго говоря, это некорректно. В этой книге и в других материалах, если встретится выражение вроде "средняя временная сложность $O(n)$", просто понимай его как $\Theta(n)$ .
Возможно, потому что символ $O$ звучит слишком привычно, и мы часто используем его для обозначения средней временной сложности. Но строго говоря, это некорректно. В этой книге и в других материалах, если встретится выражение вроде «средняя временная сложность $O(n)$», просто понимай его как $\Theta(n)$ .