This commit is contained in:
krahets
2026-03-30 08:17:41 +08:00
parent 68cafe99dd
commit 46bccf0065
484 changed files with 60193 additions and 20315 deletions
@@ -65,8 +65,8 @@
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Noto+Sans:300,300i,400,400i,500,500i,700,700i%7CJetBrains+Mono:400,400i,700,700i&display=fallback">
<style>:root{--md-text-font:"Noto Sans";--md-code-font:"JetBrains Mono"}</style>
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=PT+Sans:300,300i,400,400i,500,500i,700,700i%7CJetBrains+Mono:400,400i,700,700i&display=fallback">
<style>:root{--md-text-font:"PT Sans";--md-code-font:"JetBrains Mono"}</style>
@@ -574,7 +574,7 @@
<span class="md-ellipsis">
Глава 1. Знакомство с алгоритмами
Глава 1. Введение в алгоритмы
@@ -596,7 +596,7 @@
<span class="md-nav__icon md-icon"></span>
Глава 1. Знакомство с алгоритмами
Глава 1. Введение в алгоритмы
</label>
@@ -646,7 +646,7 @@
<span class="md-ellipsis">
1.2 Что такое структуры данных и алгоритмы
1.2 Что такое алгоритм
@@ -1253,7 +1253,7 @@
<span class="md-ellipsis">
Глава 4. Массив и связный список
Глава 4. Массивы и списки
@@ -1275,7 +1275,7 @@
<span class="md-nav__icon md-icon"></span>
Глава 4. Массив и связный список
Глава 4. Массивы и списки
</label>
@@ -1381,7 +1381,7 @@
<span class="md-ellipsis">
4.4 Память и кеш *
4.4 Оперативная память и кэш *
@@ -1663,7 +1663,7 @@
<span class="md-ellipsis">
Глава 6. Хеширование
Глава 6. Хеш-таблицы
@@ -1685,7 +1685,7 @@
<span class="md-nav__icon md-icon"></span>
Глава 6. Хеширование
Глава 6. Хеш-таблицы
</label>
@@ -1763,7 +1763,7 @@
<span class="md-ellipsis">
6.3 Хеш-алгоритмы
6.3 Алгоритмы хеширования
@@ -1858,7 +1858,7 @@
<span class="md-ellipsis">
Глава 7. Дерево
Глава 7. Деревья
@@ -1880,7 +1880,7 @@
<span class="md-nav__icon md-icon"></span>
Глава 7. Дерево
Глава 7. Деревья
</label>
@@ -1958,7 +1958,7 @@
<span class="md-ellipsis">
7.3 Представление дерева массивом
7.3 Представление двоичного дерева массивом
@@ -2042,7 +2042,7 @@
<span class="md-ellipsis">
7.6 Резюме
7.6 Краткие итоги
@@ -2205,7 +2205,7 @@
<span class="md-ellipsis">
8.3 Задача Top-K
8.3 Задача Top-k
@@ -2296,7 +2296,7 @@
<span class="md-ellipsis">
Глава 9. Граф
Глава 9. Графы
@@ -2318,7 +2318,7 @@
<span class="md-nav__icon md-icon"></span>
Глава 9. Граф
Глава 9. Графы
</label>
@@ -2368,7 +2368,7 @@
<span class="md-ellipsis">
9.2 Базовые операции над графами
9.2 Базовые операции графа
@@ -2424,7 +2424,7 @@
<span class="md-ellipsis">
9.4 Резюме
9.4 Краткие итоги
@@ -2563,7 +2563,7 @@
<span class="md-ellipsis">
10.2 Точка вставки двоичного поиска
10.2 Двоичный поиск точки вставки
@@ -2591,7 +2591,7 @@
<span class="md-ellipsis">
10.3 Граничные случаи двоичного поиска
10.3 Двоичный поиск границ
@@ -2619,7 +2619,7 @@
<span class="md-ellipsis">
10.4 Стратегия оптимизации через хеширование
10.4 Стратегии оптимизации хеширования
@@ -2647,7 +2647,7 @@
<span class="md-ellipsis">
10.5 Алгоритмы поиска: новый взгляд
10.5 Переосмысление алгоритмов поиска
@@ -2852,7 +2852,7 @@
<span class="md-ellipsis">
11.3 Пузырьковая сортировка
11.3 Сортировка пузырьком
@@ -2880,7 +2880,7 @@
<span class="md-ellipsis">
11.4 Сортировка вставкой
11.4 Сортировка вставками
@@ -3185,7 +3185,7 @@
<span class="md-ellipsis">
12.1 Алгоритмы разделяй и властвуй
12.1 Стратегия разделяй и властвуй
@@ -3213,7 +3213,7 @@
<span class="md-ellipsis">
12.2 Стратегия поиска разделяй и властвуй
12.2 Поисковая стратегия разделяй и властвуй
@@ -3490,7 +3490,7 @@
<span class="md-ellipsis">
13.4 Задача о $n$ ферзях
13.4 Задача о n ферзях
@@ -3631,7 +3631,7 @@
<span class="md-ellipsis">
14.1 Введение в динамическое программирование
14.1 Первое знакомство с динамическим программированием
@@ -3743,7 +3743,7 @@
<span class="md-ellipsis">
14.5 Задача о неограниченном рюкзаке
14.5 Задача о полном рюкзаке
@@ -4355,43 +4355,43 @@
<!-- Page content -->
<h1 id="21">2.1 &nbsp; Оценка эффективности алгоритмов<a class="headerlink" href="#21" title="Permanent link">&para;</a></h1>
<p>При проектировании алгоритмов мы последовательно стремимся к двум уровням целей.</p>
<p>В процессе разработки алгоритмов мы стремимся к достижению следующих целей.</p>
<ol>
<li><strong>Найти решение задачи</strong>: алгоритм должен надежно получать правильный ответ в заданном диапазоне входных данных.</li>
<li><strong>Найти оптимальное решение</strong>: для одной и той же задачи может существовать несколько решений, и нам хочется выбрать максимально эффективный алгоритм.</li>
<li><strong>Найти решение задачи</strong>: алгоритм должен надежно находить правильное решение задачи в заданных пределах входных данных.</li>
<li><strong>Найти оптимальное решение</strong>: для одной и той же задачи может существовать несколько решений, и мы стремимся найти максимально эффективный алгоритм.</li>
</ol>
<p>Иными словами, если задача в принципе решается, эффективность алгоритма становится главным критерием оценки его качества. Она включает два следующих измерения.</p>
<p>Таким образом, при условии возможности решения задачи эффективность алгоритма становится основным критерием его оценки, который включает два аспекта.</p>
<ul>
<li><strong>Временная эффективность</strong>: сколько времени работает алгоритм.</li>
<li><strong>Пространственная эффективность</strong>: сколько памяти занимает алгоритм.</li>
<li><strong>Временная эффективность</strong>: продолжительность выполнения алгоритма.</li>
<li><strong>Пространственная эффективность</strong>: объем памяти, занимаемой алгоритмом.</li>
</ul>
<p>Короче говоря, <strong>наша цель - проектировать структуры данных и алгоритмы, которые "и быстры, и экономны по памяти"</strong>. Эффективная оценка алгоритмов крайне важна, потому что только так можно сравнивать разные алгоритмы и направлять процесс их проектирования и оптимизации.</p>
<p>Методы оценки эффективности в основном делятся на два типа: практическое тестирование и теоретическая оценка.</p>
<p>В двух словах, <strong>наша цель - разработка быстрых и экономных структур данных и алгоритмов</strong>. Эффективная оценка алгоритмов крайне важна, так как только так можно сравнивать различные алгоритмы и управлять процессом их разработки и оптимизации.</p>
<p>Методы оценки эффективности делятся на два типа: практическое тестирование и теоретическую оценку.</p>
<h2 id="211">2.1.1 &nbsp; Практическое тестирование<a class="headerlink" href="#211" title="Permanent link">&para;</a></h2>
<p>Предположим, у нас есть алгоритм <code>A</code> и алгоритм <code>B</code>, оба решают одну и ту же задачу, и нам нужно сравнить их эффективность. Самый прямой способ - взять компьютер, запустить оба алгоритма и зафиксировать время работы и объем используемой памяти. Такой способ оценки отражает реальную ситуацию, но имеет и серьезные ограничения.</p>
<p>С одной стороны, <strong>трудно исключить влияние факторов тестовой среды</strong>. Аппаратная конфигурация влияет на производительность алгоритма. Например, если алгоритм имеет высокий уровень параллелизма, он лучше подходит для многоядерных CPU; если алгоритм интенсивно работает с памятью, он покажет себя лучше на быстрой памяти. Иными словами, результаты тестирования одного и того же алгоритма на разных машинах могут различаться. Это означает, что пришлось бы тестировать на самых разных машинах и усреднять результаты, а на практике это нереалистично.</p>
<p>С другой стороны, <strong>полное тестирование требует больших ресурсов</strong>. По мере изменения объема входных данных алгоритм может вести себя по-разному. Например, при небольшом объеме входных данных время работы алгоритма <code>A</code> может быть меньше, чем у алгоритма <code>B</code>; но при большом объеме результаты могут оказаться прямо противоположными. Поэтому для убедительных выводов пришлось бы тестировать входные данные множества разных масштабов, а это требует значительных вычислительных ресурсов.</p>
<p>Предположим, у нас есть алгоритмы <code>A</code> и <code>B</code>, которые решают одну и ту же задачу, и необходимо сравнить их эффективность. Самый прямой метод - это запустить оба алгоритма на компьютере и зафиксировать время их выполнения и объем используемой памяти. Этот метод отражает реальную ситуацию, но имеет значительные ограничения.</p>
<p>С одной стороны, <strong>сложно исключить влияние факторов тестовой среды</strong>. Аппаратная конфигурация влияет на производительность алгоритма. Например, если алгоритм обладает высокой степенью параллелизма, он будет лучше работать на многоядерных CPU; если алгоритм интенсивно использует память, его производительность будет выше на высокопроизводительной памяти. Это означает, что результаты тестирования на разных машинах могут значительно отличаться, а для получения средней эффективности пришлось бы тестировать на различных платформах, что крайне затруднительно.</p>
<p>С другой стороны, <strong>проведение полного тестирования требует значительных ресурсов</strong>. С изменением объема входных данных алгоритмы демонстрируют разную эффективность. Например, при небольшом объеме данных алгоритм <code>A</code> может работать быстрее, чем алгоритм <code>B</code>, но при большом объеме данных результат может быть противоположным. Следовательно, для получения убедительных выводов необходимо тестировать различные масштабы входных данных, что требует значительных вычислительных ресурсов.</p>
<h2 id="212">2.1.2 &nbsp; Теоретическая оценка<a class="headerlink" href="#212" title="Permanent link">&para;</a></h2>
<p>Поскольку практическое тестирование имеет серьезные ограничения, можно попытаться оценить эффективность алгоритма только с помощью вычислений. Такой метод называется <u>асимптотическим анализом сложности (asymptotic complexity analysis)</u>, или сокращенно <u>анализом сложности</u>.</p>
<p>Анализ сложности показывает зависимость между временем и пространственными ресурсами, требуемыми алгоритму, и масштабом входных данных. <strong>Он описывает тенденцию роста времени и памяти, необходимых алгоритму, по мере увеличения размера входных данных</strong>. Это определение звучит немного тяжеловесно, поэтому полезно разложить его на три ключевые идеи.</p>
<p>Из-за значительных ограничений практического тестирования можно рассмотреть возможность оценки эффективности алгоритмов только с помощью вычислений. Такой метод называется <u>анализом асимптотической сложности (asymptotic complexity analysis)</u>, или сокращенно <u>анализом сложности</u>.</p>
<p>Анализ сложности позволяет отразить зависимость между ресурсами времени и пространства, необходимыми для выполнения алгоритма, и размером входных данных. <strong>Он описывает тенденцию роста времени и пространства, необходимых для выполнения алгоритма, по мере увеличения размера входных данных</strong>. Это определение может показаться сложным, но его можно разбить на три ключевых момента.</p>
<ul>
<li>"Временные и пространственные ресурсы" соответствуют <u>временной сложности (time complexity)</u> и <u>пространственной сложности (space complexity)</u> соответственно.</li>
<li>"По мере увеличения размера входных данных" означает, что сложность отражает связь между эффективностью алгоритма и масштабом входа.</li>
<li>"Тенденция роста времени и пространства" означает, что анализ сложности интересуется не конкретными значениями времени или памяти, а тем, насколько быстро они растут.</li>
<li>"Ресурсы времени и пространства" соответствуют <u>временной сложности (time complexity)</u> и <u>пространственной сложности (space complexity)</u>.</li>
<li>"По мере увеличения размера входных данных" означает, что сложность отражает зависимость эффективности алгоритма от объема входных данных.</li>
<li>"Тенденция роста времени и пространства" указывает, что анализ сложности фокусируется не на конкретных значениях времени выполнения или объема занимаемой памяти, а на скорости их роста.</li>
</ul>
<p><strong>Анализ сложности устраняет недостатки практического тестирования</strong>, что проявляется в следующих аспектах.</p>
<p><strong>Анализ сложности преодолевает недостатки метода практического тестирования</strong>, что выражается в следующих аспектах.</p>
<ul>
<li>Для него не нужно реально запускать код, а значит, он экологичнее и экономит ресурсы.</li>
<li>Он не зависит от тестовой среды, поэтому результаты анализа применимы ко всем платформам выполнения.</li>
<li>Он позволяет увидеть эффективность алгоритма при разных объемах данных, особенно на больших данных.</li>
<li>Он не требует фактического выполнения кода, что делает его более экологичным и энергосберегающим.</li>
<li>Он независим от тестовой среды, а результаты анализа применимы ко всем платформам выполнения.</li>
<li>Он может продемонстрировать эффективность алгоритма при различных объемах данных, особенно при больших объемах.</li>
</ul>
<div class="admonition tip">
<p class="admonition-title">Tip</p>
<p>Если понятие сложности пока все еще кажется тебе запутанным, не переживай: мы подробно разберем его в следующих разделах.</p>
<p>Если понятие сложности пока все еще кажется вам запутанным, не переживайте: мы подробно разберем его в следующих разделах.</p>
</div>
<p>Анализ сложности дает нам "линейку" для оценки эффективности алгоритмов, позволяя измерять, сколько времени и памяти требуется для выполнения конкретного алгоритма, и сравнивать эффективность разных алгоритмов между собой.</p>
<p>Сложность - это математическое понятие, поэтому для начинающих оно может показаться довольно абстрактным и сравнительно трудным. С этой точки зрения анализ сложности, возможно, не лучший самый первый материал для знакомства. Однако, когда мы обсуждаем особенности конкретной структуры данных или алгоритма, почти невозможно не затронуть скорость его работы и использование памяти.</p>
<p>В итоге рекомендуется еще до глубокого погружения в структуры данных и алгоритмы <strong>сформировать хотя бы первичное понимание анализа сложности, чтобы уметь выполнять анализ сложности простых алгоритмов</strong>.</p>
<p>Анализ сложности предоставляет нам мерило оценки эффективности алгоритмов, позволяя измерять время и ресурсы, необходимые для выполнения конкретного алгоритма, а также сравнивать эффективность различных алгоритмов.</p>
<p>Сложность - это математическое понятие, которое новичкам может показаться абстрактным и сложным для изучения. С этой точки зрения анализ сложности не то, с чего стоит начинать изучение алгоритмов. Однако, обсуждая особенности той или иной структуры данных или алгоритма, невозможно избежать анализа их скорости выполнения и использования памяти.</p>
<p>Таким образом, перед погружением в изучение структур данных и алгоритмов рекомендуется получить базовое представление об анализе сложности, чтобы иметь возможность выполнять хотя бы базовую оценку их эффективности.</p>
<!-- Source file information -->