mirror of
https://github.com/krahets/hello-algo.git
synced 2026-07-12 07:26:07 +00:00
deploy
This commit is contained in:
@@ -4706,7 +4706,7 @@
|
||||
<p><div style="height: 423px; width: 100%;"><iframe class="pythontutor-iframe" src="https://pythontutor.com/iframe-embed.html#code=def%20for_loop%28n%3A%20int%29%20-%3E%20int%3A%0A%20%20%20%20%22%22%22%D0%A6%D0%B8%D0%BA%D0%BB%20for%22%22%22%0A%20%20%20%20res%20%3D%200%0A%20%20%20%20%23%20%D0%A6%D0%B8%D0%BA%D0%BB%D0%B8%D1%87%D0%B5%D1%81%D0%BA%D0%BE%D0%B5%20%D1%81%D1%83%D0%BC%D0%BC%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5%201%2C%202%2C%20...%2C%20n-1%2C%20n%0A%20%20%20%20for%20i%20in%20range%281%2C%20n%20%2B%201%29%3A%0A%20%20%20%20%20%20%20%20res%20%2B%3D%20i%0A%20%20%20%20return%20res%0A%0A%22%22%22Driver%20Code%22%22%22%0Aif%20__name__%20%3D%3D%20%22__main__%22%3A%0A%20%20%20%20n%20%3D%205%0A%20%20%20%20res%20%3D%20for_loop%28n%29%0A%20%20%20%20print%28f%22%5Cn%D0%A0%D0%B5%D0%B7%D1%83%D0%BB%D1%8C%D1%82%D0%B0%D1%82%20%D1%81%D1%83%D0%BC%D0%BC%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D1%8F%20%D0%B2%20%D1%86%D0%B8%D0%BA%D0%BB%D0%B5%20for%20res%20%3D%20%7Bres%7D%22%29&codeDivHeight=472&codeDivWidth=350&cumulative=false&curInstr=3&heapPrimitives=nevernest&origin=opt-frontend.js&py=311&rawInputLstJSON=%5B%5D&"> </iframe></div>
|
||||
<div style="margin-top: 5px;"><a href="https://pythontutor.com/iframe-embed.html#code=def%20for_loop%28n%3A%20int%29%20-%3E%20int%3A%0A%20%20%20%20%22%22%22%D0%A6%D0%B8%D0%BA%D0%BB%20for%22%22%22%0A%20%20%20%20res%20%3D%200%0A%20%20%20%20%23%20%D0%A6%D0%B8%D0%BA%D0%BB%D0%B8%D1%87%D0%B5%D1%81%D0%BA%D0%BE%D0%B5%20%D1%81%D1%83%D0%BC%D0%BC%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5%201%2C%202%2C%20...%2C%20n-1%2C%20n%0A%20%20%20%20for%20i%20in%20range%281%2C%20n%20%2B%201%29%3A%0A%20%20%20%20%20%20%20%20res%20%2B%3D%20i%0A%20%20%20%20return%20res%0A%0A%22%22%22Driver%20Code%22%22%22%0Aif%20__name__%20%3D%3D%20%22__main__%22%3A%0A%20%20%20%20n%20%3D%205%0A%20%20%20%20res%20%3D%20for_loop%28n%29%0A%20%20%20%20print%28f%22%5Cn%D0%A0%D0%B5%D0%B7%D1%83%D0%BB%D1%8C%D1%82%D0%B0%D1%82%20%D1%81%D1%83%D0%BC%D0%BC%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D1%8F%20%D0%B2%20%D1%86%D0%B8%D0%BA%D0%BB%D0%B5%20for%20res%20%3D%20%7Bres%7D%22%29&codeDivHeight=800&codeDivWidth=600&cumulative=false&curInstr=3&heapPrimitives=nevernest&origin=opt-frontend.js&py=311&rawInputLstJSON=%5B%5D&" target="_blank" rel="noopener noreferrer">Во весь экран ></a></div></p>
|
||||
</details>
|
||||
<p>Ниже представлена блок-схема этой функции суммирования.</p>
|
||||
<p>На рисунке 2-1 представлена блок-схема этой функции суммирования.</p>
|
||||
<p><img alt="Блок-схема функции суммирования" class="animation-figure" src="../iteration_and_recursion.assets/iteration.png" /></p>
|
||||
<p align="center"> Рисунок 2-1 Блок-схема функции суммирования </p>
|
||||
|
||||
@@ -5353,7 +5353,7 @@
|
||||
<p><div style="height: 459px; width: 100%;"><iframe class="pythontutor-iframe" src="https://pythontutor.com/iframe-embed.html#code=def%20nested_for_loop%28n%3A%20int%29%20-%3E%20str%3A%0A%20%20%20%20%22%22%22%D0%94%D0%B2%D0%BE%D0%B9%D0%BD%D0%BE%D0%B9%20%D1%86%D0%B8%D0%BA%D0%BB%20for%22%22%22%0A%20%20%20%20res%20%3D%20%22%22%0A%20%20%20%20%23%20%D0%A6%D0%B8%D0%BA%D0%BB%20%D0%BF%D0%BE%20i%20%3D%201%2C%202%2C%20...%2C%20n-1%2C%20n%0A%20%20%20%20for%20i%20in%20range%281%2C%20n%20%2B%201%29%3A%0A%20%20%20%20%20%20%20%20%23%20%D0%A6%D0%B8%D0%BA%D0%BB%20%D0%BF%D0%BE%20j%20%3D%201%2C%202%2C%20...%2C%20n-1%2C%20n%0A%20%20%20%20%20%20%20%20for%20j%20in%20range%281%2C%20n%20%2B%201%29%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20res%20%2B%3D%20f%22%28%7Bi%7D%2C%20%7Bj%7D%29%2C%20%22%0A%20%20%20%20return%20res%0A%0A%22%22%22Driver%20Code%22%22%22%0Aif%20__name__%20%3D%3D%20%22__main__%22%3A%0A%20%20%20%20n%20%3D%205%0A%20%20%20%20res%20%3D%20nested_for_loop%28n%29%0A%20%20%20%20print%28f%22%5Cn%D0%A0%D0%B5%D0%B7%D1%83%D0%BB%D1%8C%D1%82%D0%B0%D1%82%20%D0%BE%D0%B1%D1%85%D0%BE%D0%B4%D0%B0%20%D0%B2%20%D0%B4%D0%B2%D0%BE%D0%B9%D0%BD%D0%BE%D0%BC%20%D1%86%D0%B8%D0%BA%D0%BB%D0%B5%20for%20%7Bres%7D%22%29&codeDivHeight=472&codeDivWidth=350&cumulative=false&curInstr=3&heapPrimitives=nevernest&origin=opt-frontend.js&py=311&rawInputLstJSON=%5B%5D&textReferences=false"> </iframe></div>
|
||||
<div style="margin-top: 5px;"><a href="https://pythontutor.com/iframe-embed.html#code=def%20nested_for_loop%28n%3A%20int%29%20-%3E%20str%3A%0A%20%20%20%20%22%22%22%D0%94%D0%B2%D0%BE%D0%B9%D0%BD%D0%BE%D0%B9%20%D1%86%D0%B8%D0%BA%D0%BB%20for%22%22%22%0A%20%20%20%20res%20%3D%20%22%22%0A%20%20%20%20%23%20%D0%A6%D0%B8%D0%BA%D0%BB%20%D0%BF%D0%BE%20i%20%3D%201%2C%202%2C%20...%2C%20n-1%2C%20n%0A%20%20%20%20for%20i%20in%20range%281%2C%20n%20%2B%201%29%3A%0A%20%20%20%20%20%20%20%20%23%20%D0%A6%D0%B8%D0%BA%D0%BB%20%D0%BF%D0%BE%20j%20%3D%201%2C%202%2C%20...%2C%20n-1%2C%20n%0A%20%20%20%20%20%20%20%20for%20j%20in%20range%281%2C%20n%20%2B%201%29%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20res%20%2B%3D%20f%22%28%7Bi%7D%2C%20%7Bj%7D%29%2C%20%22%0A%20%20%20%20return%20res%0A%0A%22%22%22Driver%20Code%22%22%22%0Aif%20__name__%20%3D%3D%20%22__main__%22%3A%0A%20%20%20%20n%20%3D%205%0A%20%20%20%20res%20%3D%20nested_for_loop%28n%29%0A%20%20%20%20print%28f%22%5Cn%D0%A0%D0%B5%D0%B7%D1%83%D0%BB%D1%8C%D1%82%D0%B0%D1%82%20%D0%BE%D0%B1%D1%85%D0%BE%D0%B4%D0%B0%20%D0%B2%20%D0%B4%D0%B2%D0%BE%D0%B9%D0%BD%D0%BE%D0%BC%20%D1%86%D0%B8%D0%BA%D0%BB%D0%B5%20for%20%7Bres%7D%22%29&codeDivHeight=800&codeDivWidth=600&cumulative=false&curInstr=3&heapPrimitives=nevernest&origin=opt-frontend.js&py=311&rawInputLstJSON=%5B%5D&textReferences=false" target="_blank" rel="noopener noreferrer">Во весь экран ></a></div></p>
|
||||
</details>
|
||||
<p>Ниже приведена блок-схема такого вложенного цикла.</p>
|
||||
<p>На рисунке 2-2 приведена блок-схема такого вложенного цикла.</p>
|
||||
<p><img alt="Блок-схема вложенного цикла" class="animation-figure" src="../iteration_and_recursion.assets/nested_iteration.png" /></p>
|
||||
<p align="center"> Рисунок 2-2 Блок-схема вложенного цикла </p>
|
||||
|
||||
@@ -5548,7 +5548,7 @@
|
||||
<p><div style="height: 459px; width: 100%;"><iframe class="pythontutor-iframe" src="https://pythontutor.com/iframe-embed.html#code=def%20recur%28n%3A%20int%29%20-%3E%20int%3A%0A%20%20%20%20%22%22%22%D0%A0%D0%B5%D0%BA%D1%83%D1%80%D1%81%D0%B8%D1%8F%22%22%22%0A%20%20%20%20%23%20%D0%A3%D1%81%D0%BB%D0%BE%D0%B2%D0%B8%D0%B5%20%D0%B7%D0%B0%D0%B2%D0%B5%D1%80%D1%88%D0%B5%D0%BD%D0%B8%D1%8F%0A%20%20%20%20if%20n%20%3D%3D%201%3A%0A%20%20%20%20%20%20%20%20return%201%0A%20%20%20%20%23%20%D0%A0%D0%B5%D0%BA%D1%83%D1%80%D1%81%D0%B8%D1%8F%3A%20%D1%80%D0%B5%D0%BA%D1%83%D1%80%D1%81%D0%B8%D0%B2%D0%BD%D1%8B%D0%B9%20%D0%B2%D1%8B%D0%B7%D0%BE%D0%B2%0A%20%20%20%20res%20%3D%20recur%28n%20-%201%29%0A%20%20%20%20%23%20%D0%92%D0%BE%D0%B7%D0%B2%D1%80%D0%B0%D1%82%3A%20%D0%B2%D0%B5%D1%80%D0%BD%D1%83%D1%82%D1%8C%20%D1%80%D0%B5%D0%B7%D1%83%D0%BB%D1%8C%D1%82%D0%B0%D1%82%0A%20%20%20%20return%20n%20%2B%20res%0A%0A%22%22%22Driver%20Code%22%22%22%0Aif%20__name__%20%3D%3D%20%22__main__%22%3A%0A%20%20%20%20n%20%3D%205%0A%20%20%20%20res%20%3D%20recur%28n%29%0A%20%20%20%20print%28f%22%5Cn%D0%A0%D0%B5%D0%B7%D1%83%D0%BB%D1%8C%D1%82%D0%B0%D1%82%20%D1%81%D1%83%D0%BC%D0%BC%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D1%8F%20%D0%B2%20%D1%80%D0%B5%D0%BA%D1%83%D1%80%D1%81%D0%B8%D0%B2%D0%BD%D0%BE%D0%B9%20%D1%84%D1%83%D0%BD%D0%BA%D1%86%D0%B8%D0%B8%20res%20%3D%20%7Bres%7D%22%29&codeDivHeight=472&codeDivWidth=350&cumulative=false&curInstr=3&heapPrimitives=nevernest&origin=opt-frontend.js&py=311&rawInputLstJSON=%5B%5D&textReferences=false"> </iframe></div>
|
||||
<div style="margin-top: 5px;"><a href="https://pythontutor.com/iframe-embed.html#code=def%20recur%28n%3A%20int%29%20-%3E%20int%3A%0A%20%20%20%20%22%22%22%D0%A0%D0%B5%D0%BA%D1%83%D1%80%D1%81%D0%B8%D1%8F%22%22%22%0A%20%20%20%20%23%20%D0%A3%D1%81%D0%BB%D0%BE%D0%B2%D0%B8%D0%B5%20%D0%B7%D0%B0%D0%B2%D0%B5%D1%80%D1%88%D0%B5%D0%BD%D0%B8%D1%8F%0A%20%20%20%20if%20n%20%3D%3D%201%3A%0A%20%20%20%20%20%20%20%20return%201%0A%20%20%20%20%23%20%D0%A0%D0%B5%D0%BA%D1%83%D1%80%D1%81%D0%B8%D1%8F%3A%20%D1%80%D0%B5%D0%BA%D1%83%D1%80%D1%81%D0%B8%D0%B2%D0%BD%D1%8B%D0%B9%20%D0%B2%D1%8B%D0%B7%D0%BE%D0%B2%0A%20%20%20%20res%20%3D%20recur%28n%20-%201%29%0A%20%20%20%20%23%20%D0%92%D0%BE%D0%B7%D0%B2%D1%80%D0%B0%D1%82%3A%20%D0%B2%D0%B5%D1%80%D0%BD%D1%83%D1%82%D1%8C%20%D1%80%D0%B5%D0%B7%D1%83%D0%BB%D1%8C%D1%82%D0%B0%D1%82%0A%20%20%20%20return%20n%20%2B%20res%0A%0A%22%22%22Driver%20Code%22%22%22%0Aif%20__name__%20%3D%3D%20%22__main__%22%3A%0A%20%20%20%20n%20%3D%205%0A%20%20%20%20res%20%3D%20recur%28n%29%0A%20%20%20%20print%28f%22%5Cn%D0%A0%D0%B5%D0%B7%D1%83%D0%BB%D1%8C%D1%82%D0%B0%D1%82%20%D1%81%D1%83%D0%BC%D0%BC%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D1%8F%20%D0%B2%20%D1%80%D0%B5%D0%BA%D1%83%D1%80%D1%81%D0%B8%D0%B2%D0%BD%D0%BE%D0%B9%20%D1%84%D1%83%D0%BD%D0%BA%D1%86%D0%B8%D0%B8%20res%20%3D%20%7Bres%7D%22%29&codeDivHeight=800&codeDivWidth=600&cumulative=false&curInstr=3&heapPrimitives=nevernest&origin=opt-frontend.js&py=311&rawInputLstJSON=%5B%5D&textReferences=false" target="_blank" rel="noopener noreferrer">Во весь экран ></a></div></p>
|
||||
</details>
|
||||
<p>Ниже представлен рекурсивный процесс этой функции.</p>
|
||||
<p>На рисунке 2-3 представлен рекурсивный процесс этой функции.</p>
|
||||
<p><img alt="Рекурсивный процесс функции суммирования" class="animation-figure" src="../iteration_and_recursion.assets/recursion_sum.png" /></p>
|
||||
<p align="center"> Рисунок 2-3 Рекурсивный процесс функции суммирования </p>
|
||||
|
||||
@@ -5744,10 +5744,10 @@
|
||||
<p>Обратите внимание: многие компиляторы и интерпретаторы не поддерживают оптимизацию хвостовой рекурсии. Например, Python по умолчанию такую оптимизацию не выполняет, поэтому даже функция в хвостово-рекурсивной форме все равно может привести к переполнению стека.</p>
|
||||
</div>
|
||||
<h3 id="3_1">3. Дерево рекурсии<a class="headerlink" href="#3_1" title="Permanent link">¶</a></h3>
|
||||
<p>При решении задач, связанных с алгоритмами типа "разделяй и властвуй", рекурсия часто оказывается более интуитивной и читабельной, чем итерация. Рассмотрим в качестве примера последовательность Фибоначчи.</p>
|
||||
<p>При решении задач, связанных с алгоритмами типа «разделяй и властвуй», рекурсия часто оказывается более интуитивной и читабельной, чем итерация. Рассмотрим в качестве примера последовательность Фибоначчи.</p>
|
||||
<div class="admonition question">
|
||||
<p class="admonition-title">Question</p>
|
||||
<p>Дана последовательность Фибоначчи <span class="arithmatex">\(0, 1, 1, 2, 3, 5, 8, 13, \dots\)</span> ; найди <span class="arithmatex">\(n\)</span>-й элемент этой последовательности.</p>
|
||||
<p>Дана последовательность Фибоначчи <span class="arithmatex">\(0, 1, 1, 2, 3, 5, 8, 13, \dots\)</span>. Найди <span class="arithmatex">\(n\)</span>-й элемент этой последовательности.</p>
|
||||
</div>
|
||||
<p>Обозначив <span class="arithmatex">\(n\)</span>-й член последовательности Фибоначчи как <span class="arithmatex">\(f(n)\)</span> , можно сформулировать два утверждения.</p>
|
||||
<ul>
|
||||
@@ -5935,10 +5935,10 @@
|
||||
<p><img alt="Дерево рекурсии последовательности Фибоначчи" class="animation-figure" src="../iteration_and_recursion.assets/recursion_tree.png" /></p>
|
||||
<p align="center"> Рисунок 2-6 Дерево рекурсии последовательности Фибоначчи </p>
|
||||
|
||||
<p>По своей сути рекурсия отражает парадигму мышления "разбиение задачи на более мелкие подзадачи", что делает стратегию "разделяй и властвуй" крайне важной.</p>
|
||||
<p>По своей сути рекурсия отражает парадигму мышления «разбиение задачи на более мелкие подзадачи», что делает стратегию «разделяй и властвуй» крайне важной.</p>
|
||||
<ul>
|
||||
<li>С точки зрения <strong>алгоритмов</strong> многие важные алгоритмические стратегии, такие как поиск, сортировка, возврат, "разделяй и властвуй" и динамическое программирование, прямо или косвенно используют этот подход.</li>
|
||||
<li>С точки зрения <strong>структур данных</strong> рекурсия естественно подходит для решения задач, связанных со списками, деревьями и графами, поскольку они очень хорошо поддаются анализу с использованием идеи "разделяй и властвуй".</li>
|
||||
<li>С точки зрения <strong>алгоритмов</strong> многие важные алгоритмические стратегии, такие как поиск, сортировка, возврат, «разделяй и властвуй» и динамическое программирование, прямо или косвенно используют этот подход.</li>
|
||||
<li>С точки зрения <strong>структур данных</strong> рекурсия естественно подходит для решения задач, связанных со списками, деревьями и графами, поскольку они очень хорошо поддаются анализу с использованием идеи «разделяй и властвуй».</li>
|
||||
</ul>
|
||||
<h2 id="223">2.2.3 Сравнение<a class="headerlink" href="#223" title="Permanent link">¶</a></h2>
|
||||
<p>Подводя итог, можно сказать, что итерация и рекурсия различаются по реализации, производительности и применимости, как показано в таблице 2-1.</p>
|
||||
@@ -5972,20 +5972,20 @@
|
||||
<tr>
|
||||
<td>Сфера использования</td>
|
||||
<td>Подходит для простых циклических задач, код интуитивно понятен и хорошо читаем</td>
|
||||
<td>Подходит для разбиения на подзадачи, для структур деревья и графы, алгоритмов "разделяй и властвуй", возврата и т. д.; структура кода проста и ясна</td>
|
||||
<td>Подходит для разбиения на подзадачи, для структур деревья и графы, алгоритмов «разделяй и властвуй», возврата и т. д.. Структура кода проста и ясна</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div class="admonition tip">
|
||||
<p class="admonition-title">Tip</p>
|
||||
<p>Если дальнейшее содержание кажется сложным, можно вернуться к нему после чтения главы о "стеке".</p>
|
||||
<p>Если дальнейшее содержание кажется сложным, можно вернуться к нему после чтения главы о «стеке».</p>
|
||||
</div>
|
||||
<p>Какова же внутренняя связь между итерацией и рекурсией? В рассмотренном примере рекурсивной функции операция сложения выполняется на этапе возврата рекурсии. Это означает, что функция, вызванная первой, фактически завершает операцию сложения последней, <strong>что соответствует принципу стека "первым пришел - последним вышел"</strong>.</p>
|
||||
<p>На самом деле такие термины рекурсии, как "стек вызовов" и "пространство стекового кадра", уже намекают на тесную связь между рекурсией и стеком.</p>
|
||||
<p>Какова же внутренняя связь между итерацией и рекурсией? В рассмотренном примере рекурсивной функции операция сложения выполняется на этапе возврата рекурсии. Это означает, что функция, вызванная первой, фактически завершает операцию сложения последней, <strong>что соответствует принципу стека «первым пришел - последним вышел»</strong>.</p>
|
||||
<p>На самом деле такие термины рекурсии, как «стек вызовов» и «пространство стекового кадра», уже намекают на тесную связь между рекурсией и стеком.</p>
|
||||
<ol>
|
||||
<li><strong>Вызов</strong>: когда вызывается функция, система выделяет для нее новый стековый кадр в "стеке вызовов" для хранения локальных переменных функции, параметров, адреса возврата и других данных.</li>
|
||||
<li><strong>Возврат</strong>: когда функция завершает выполнение и возвращает результат, соответствующий стековый кадр удаляется из "стека вызовов", восстанавливая среду выполнения предыдущей функции.</li>
|
||||
<li><strong>Вызов</strong>: когда вызывается функция, система выделяет для нее новый стековый кадр в «стеке вызовов» для хранения локальных переменных функции, параметров, адреса возврата и других данных.</li>
|
||||
<li><strong>Возврат</strong>: когда функция завершает выполнение и возвращает результат, соответствующий стековый кадр удаляется из «стека вызовов», восстанавливая среду выполнения предыдущей функции.</li>
|
||||
</ol>
|
||||
<p>Таким образом, <strong>можно использовать явный стек для моделирования поведения стека вызовов</strong>, чтобы преобразовать рекурсию в итеративную форму:</p>
|
||||
<div class="tabbed-set tabbed-alternate" data-tabs="8:13"><input checked="checked" id="__tabbed_8_1" name="__tabbed_8" type="radio" /><input id="__tabbed_8_2" name="__tabbed_8" type="radio" /><input id="__tabbed_8_3" name="__tabbed_8" type="radio" /><input id="__tabbed_8_4" name="__tabbed_8" type="radio" /><input id="__tabbed_8_5" name="__tabbed_8" type="radio" /><input id="__tabbed_8_6" name="__tabbed_8" type="radio" /><input id="__tabbed_8_7" name="__tabbed_8" type="radio" /><input id="__tabbed_8_8" name="__tabbed_8" type="radio" /><input id="__tabbed_8_9" name="__tabbed_8" type="radio" /><input id="__tabbed_8_10" name="__tabbed_8" type="radio" /><input id="__tabbed_8_11" name="__tabbed_8" type="radio" /><input id="__tabbed_8_12" name="__tabbed_8" type="radio" /><input id="__tabbed_8_13" name="__tabbed_8" type="radio" /><div class="tabbed-labels"><label for="__tabbed_8_1">Python</label><label for="__tabbed_8_2">C++</label><label for="__tabbed_8_3">Java</label><label for="__tabbed_8_4">C#</label><label for="__tabbed_8_5">Go</label><label for="__tabbed_8_6">Swift</label><label for="__tabbed_8_7">JS</label><label for="__tabbed_8_8">TS</label><label for="__tabbed_8_9">Dart</label><label for="__tabbed_8_10">Rust</label><label for="__tabbed_8_11">C</label><label for="__tabbed_8_12">Kotlin</label><label for="__tabbed_8_13">Ruby</label></div>
|
||||
|
||||
@@ -4371,15 +4371,15 @@
|
||||
<p>Методы оценки эффективности делятся на два типа: практическое тестирование и теоретическую оценку.</p>
|
||||
<h2 id="211">2.1.1 Практическое тестирование<a class="headerlink" href="#211" title="Permanent link">¶</a></h2>
|
||||
<p>Предположим, у нас есть алгоритмы <code>A</code> и <code>B</code>, которые решают одну и ту же задачу, и необходимо сравнить их эффективность. Самый прямой метод - это запустить оба алгоритма на компьютере и зафиксировать время их выполнения и объем используемой памяти. Этот метод отражает реальную ситуацию, но имеет значительные ограничения.</p>
|
||||
<p>С одной стороны, <strong>сложно исключить влияние факторов тестовой среды</strong>. Аппаратная конфигурация влияет на производительность алгоритма. Например, если алгоритм обладает высокой степенью параллелизма, он будет лучше работать на многоядерных CPU; если алгоритм интенсивно использует память, его производительность будет выше на высокопроизводительной памяти. Это означает, что результаты тестирования на разных машинах могут значительно отличаться, а для получения средней эффективности пришлось бы тестировать на различных платформах, что крайне затруднительно.</p>
|
||||
<p>С одной стороны, <strong>сложно исключить влияние факторов тестовой среды</strong>. Аппаратная конфигурация влияет на производительность алгоритма. Например, если алгоритм обладает высокой степенью параллелизма, он будет лучше работать на многоядерных CPU. Если алгоритм интенсивно использует память, его производительность будет выше на высокопроизводительной памяти. Это означает, что результаты тестирования на разных машинах могут значительно отличаться, а для получения средней эффективности пришлось бы тестировать на различных платформах, что крайне затруднительно.</p>
|
||||
<p>С другой стороны, <strong>проведение полного тестирования требует значительных ресурсов</strong>. С изменением объема входных данных алгоритмы демонстрируют разную эффективность. Например, при небольшом объеме данных алгоритм <code>A</code> может работать быстрее, чем алгоритм <code>B</code>, но при большом объеме данных результат может быть противоположным. Следовательно, для получения убедительных выводов необходимо тестировать различные масштабы входных данных, что требует значительных вычислительных ресурсов.</p>
|
||||
<h2 id="212">2.1.2 Теоретическая оценка<a class="headerlink" href="#212" title="Permanent link">¶</a></h2>
|
||||
<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>
|
||||
<ul>
|
||||
|
||||
@@ -4535,7 +4535,7 @@
|
||||
<p>Временное пространство можно дополнительно разделить на три части.</p>
|
||||
<ul>
|
||||
<li><strong>Временные данные</strong>: используются для хранения различных констант, переменных, объектов и т.д., возникающих во время выполнения алгоритма.</li>
|
||||
<li><strong>Пространство кадров стека</strong>: используется для хранения контекстных данных вызываемых функций. При каждом вызове функции система создает на вершине стека новый кадр; после возврата функции пространство этого кадра освобождается.</li>
|
||||
<li><strong>Пространство кадров стека</strong>: используется для хранения контекстных данных вызываемых функций. При каждом вызове функции система создает на вершине стека новый кадр. После возврата функции пространство этого кадра освобождается.</li>
|
||||
<li><strong>Пространство инструкций</strong>: используется для хранения скомпилированных инструкций программы и в реальном подсчете обычно не учитывается.</li>
|
||||
</ul>
|
||||
<p>При анализе пространственной сложности программы <strong>обычно учитываются временные данные, пространство стека и выходные данные</strong>, как показано на рисунке 2-15.</p>
|
||||
@@ -4864,7 +4864,7 @@
|
||||
<p>Рассмотрим следующий код. Понятие худшей пространственной сложности здесь имеет два значения.</p>
|
||||
<ol>
|
||||
<li><strong>Ориентир на худшие входные данные</strong>: когда <span class="arithmatex">\(n < 10\)</span> , пространственная сложность равна <span class="arithmatex">\(O(1)\)</span> ; но когда <span class="arithmatex">\(n > 10\)</span> , инициализированный массив <code>nums</code> занимает <span class="arithmatex">\(O(n)\)</span> пространства, поэтому худшая пространственная сложность равна <span class="arithmatex">\(O(n)\)</span> .</li>
|
||||
<li><strong>Ориентир на пиковое использование памяти во время выполнения</strong>: например, до выполнения последней строки программа занимает <span class="arithmatex">\(O(1)\)</span> пространства; при инициализации массива <code>nums</code> она занимает <span class="arithmatex">\(O(n)\)</span> пространства, поэтому худшая пространственная сложность также равна <span class="arithmatex">\(O(n)\)</span> .</li>
|
||||
<li><strong>Ориентир на пиковое использование памяти во время выполнения</strong>: например, до выполнения последней строки программа занимает <span class="arithmatex">\(O(1)\)</span> пространства. При инициализации массива <code>nums</code> она занимает <span class="arithmatex">\(O(n)\)</span> пространства, поэтому худшая пространственная сложность также равна <span class="arithmatex">\(O(n)\)</span> .</li>
|
||||
</ol>
|
||||
<div class="tabbed-set tabbed-alternate" data-tabs="2:13"><input checked="checked" id="__tabbed_2_1" name="__tabbed_2" type="radio" /><input id="__tabbed_2_2" name="__tabbed_2" type="radio" /><input id="__tabbed_2_3" name="__tabbed_2" type="radio" /><input id="__tabbed_2_4" name="__tabbed_2" type="radio" /><input id="__tabbed_2_5" name="__tabbed_2" type="radio" /><input id="__tabbed_2_6" name="__tabbed_2" type="radio" /><input id="__tabbed_2_7" name="__tabbed_2" type="radio" /><input id="__tabbed_2_8" name="__tabbed_2" type="radio" /><input id="__tabbed_2_9" name="__tabbed_2" type="radio" /><input id="__tabbed_2_10" name="__tabbed_2" type="radio" /><input id="__tabbed_2_11" name="__tabbed_2" type="radio" /><input id="__tabbed_2_12" name="__tabbed_2" type="radio" /><input id="__tabbed_2_13" name="__tabbed_2" type="radio" /><div class="tabbed-labels"><label for="__tabbed_2_1">Python</label><label for="__tabbed_2_2">C++</label><label for="__tabbed_2_3">Java</label><label for="__tabbed_2_4">C#</label><label for="__tabbed_2_5">Go</label><label for="__tabbed_2_6">Swift</label><label for="__tabbed_2_7">JS</label><label for="__tabbed_2_8">TS</label><label for="__tabbed_2_9">Dart</label><label for="__tabbed_2_10">Rust</label><label for="__tabbed_2_11">C</label><label for="__tabbed_2_12">Kotlin</label><label for="__tabbed_2_13">Ruby</label></div>
|
||||
<div class="tabbed-content">
|
||||
@@ -5246,7 +5246,7 @@
|
||||
</div>
|
||||
<p>Функции <code>loop()</code> и <code>recur()</code> имеют временную сложность <span class="arithmatex">\(O(n)\)</span> , но их пространственная сложность различается.</p>
|
||||
<ul>
|
||||
<li>Функция <code>loop()</code> вызывает <code>function()</code> в цикле <span class="arithmatex">\(n\)</span> раз; на каждой итерации <code>function()</code> возвращается и освобождает пространство своего кадра стека, поэтому пространственная сложность по-прежнему равна <span class="arithmatex">\(O(1)\)</span> .</li>
|
||||
<li>Функция <code>loop()</code> вызывает <code>function()</code> в цикле <span class="arithmatex">\(n\)</span> раз. На каждой итерации <code>function()</code> возвращается и освобождает пространство своего кадра стека, поэтому пространственная сложность по-прежнему равна <span class="arithmatex">\(O(1)\)</span> .</li>
|
||||
<li>Рекурсивная функция <code>recur()</code> во время выполнения одновременно содержит <span class="arithmatex">\(n\)</span> еще не завершившихся экземпляров <code>recur()</code> , поэтому занимает <span class="arithmatex">\(O(n)\)</span> пространства кадров стека.</li>
|
||||
</ul>
|
||||
<h2 id="243">2.4.3 Распространенные типы<a class="headerlink" href="#243" title="Permanent link">¶</a></h2>
|
||||
@@ -6207,7 +6207,7 @@
|
||||
<p><div style="height: 405px; width: 100%;"><iframe class="pythontutor-iframe" src="https://pythontutor.com/iframe-embed.html#code=def%20quadratic%28n%3A%20int%29%3A%0A%20%20%20%20%22%22%22%D0%9A%D0%B2%D0%B0%D0%B4%D1%80%D0%B0%D1%82%D0%B8%D1%87%D0%BD%D0%B0%D1%8F%20%D1%81%D0%BB%D0%BE%D0%B6%D0%BD%D0%BE%D1%81%D1%82%D1%8C%22%22%22%0A%20%20%20%20%23%20%D0%94%D0%B2%D1%83%D0%BC%D0%B5%D1%80%D0%BD%D1%8B%D0%B9%20%D1%81%D0%BF%D0%B8%D1%81%D0%BE%D0%BA%20%D0%B7%D0%B0%D0%BD%D0%B8%D0%BC%D0%B0%D0%B5%D1%82%20O%28n%5E2%29%20%D0%BF%D0%B0%D0%BC%D1%8F%D1%82%D0%B8%0A%20%20%20%20num_matrix%20%3D%20%5B%5B0%5D%20%2A%20n%20for%20_%20in%20range%28n%29%5D%0A%0A%22%22%22Driver%20Code%22%22%22%0Aif%20__name__%20%3D%3D%20%22__main__%22%3A%0A%20%20%20%20n%20%3D%205%0A%20%20%20%20print%28%22%D0%A0%D0%B0%D0%B7%D0%BC%D0%B5%D1%80%20%D0%B2%D1%85%D0%BE%D0%B4%D0%BD%D1%8B%D1%85%20%D0%B4%D0%B0%D0%BD%D0%BD%D1%8B%D1%85%20n%20%3D%22%2C%20n%29%0A%0A%20%20%20%20%23%20%D0%9A%D0%B2%D0%B0%D0%B4%D1%80%D0%B0%D1%82%D0%B8%D1%87%D0%BD%D0%B0%D1%8F%20%D1%81%D0%BB%D0%BE%D0%B6%D0%BD%D0%BE%D1%81%D1%82%D1%8C%0A%20%20%20%20quadratic%28n%29&codeDivHeight=472&codeDivWidth=350&cumulative=false&curInstr=16&heapPrimitives=nevernest&origin=opt-frontend.js&py=311&rawInputLstJSON=%5B%5D&textReferences=false"> </iframe></div>
|
||||
<div style="margin-top: 5px;"><a href="https://pythontutor.com/iframe-embed.html#code=def%20quadratic%28n%3A%20int%29%3A%0A%20%20%20%20%22%22%22%D0%9A%D0%B2%D0%B0%D0%B4%D1%80%D0%B0%D1%82%D0%B8%D1%87%D0%BD%D0%B0%D1%8F%20%D1%81%D0%BB%D0%BE%D0%B6%D0%BD%D0%BE%D1%81%D1%82%D1%8C%22%22%22%0A%20%20%20%20%23%20%D0%94%D0%B2%D1%83%D0%BC%D0%B5%D1%80%D0%BD%D1%8B%D0%B9%20%D1%81%D0%BF%D0%B8%D1%81%D0%BE%D0%BA%20%D0%B7%D0%B0%D0%BD%D0%B8%D0%BC%D0%B0%D0%B5%D1%82%20O%28n%5E2%29%20%D0%BF%D0%B0%D0%BC%D1%8F%D1%82%D0%B8%0A%20%20%20%20num_matrix%20%3D%20%5B%5B0%5D%20%2A%20n%20for%20_%20in%20range%28n%29%5D%0A%0A%22%22%22Driver%20Code%22%22%22%0Aif%20__name__%20%3D%3D%20%22__main__%22%3A%0A%20%20%20%20n%20%3D%205%0A%20%20%20%20print%28%22%D0%A0%D0%B0%D0%B7%D0%BC%D0%B5%D1%80%20%D0%B2%D1%85%D0%BE%D0%B4%D0%BD%D1%8B%D1%85%20%D0%B4%D0%B0%D0%BD%D0%BD%D1%8B%D1%85%20n%20%3D%22%2C%20n%29%0A%0A%20%20%20%20%23%20%D0%9A%D0%B2%D0%B0%D0%B4%D1%80%D0%B0%D1%82%D0%B8%D1%87%D0%BD%D0%B0%D1%8F%20%D1%81%D0%BB%D0%BE%D0%B6%D0%BD%D0%BE%D1%81%D1%82%D1%8C%0A%20%20%20%20quadratic%28n%29&codeDivHeight=800&codeDivWidth=600&cumulative=false&curInstr=16&heapPrimitives=nevernest&origin=opt-frontend.js&py=311&rawInputLstJSON=%5B%5D&textReferences=false" target="_blank" rel="noopener noreferrer">Во весь экран ></a></div></p>
|
||||
</details>
|
||||
<p>Как показано на рисунке 2-18, глубина рекурсии этой функции равна <span class="arithmatex">\(n\)</span> , и в каждой рекурсивной функции инициализируется массив длины <span class="arithmatex">\(n\)</span> , <span class="arithmatex">\(n-1\)</span> , <span class="arithmatex">\(\dots\)</span> , <span class="arithmatex">\(2\)</span> , <span class="arithmatex">\(1\)</span> ; его средняя длина равна <span class="arithmatex">\(n / 2\)</span> , поэтому в сумме используется <span class="arithmatex">\(O(n^2)\)</span> пространства:</p>
|
||||
<p>Как показано на рисунке 2-18, глубина рекурсии этой функции равна <span class="arithmatex">\(n\)</span> , и в каждой рекурсивной функции инициализируется массив длины <span class="arithmatex">\(n\)</span> , <span class="arithmatex">\(n-1\)</span> , <span class="arithmatex">\(\dots\)</span> , <span class="arithmatex">\(2\)</span> , <span class="arithmatex">\(1\)</span>. Его средняя длина равна <span class="arithmatex">\(n / 2\)</span> , поэтому в сумме используется <span class="arithmatex">\(O(n^2)\)</span> пространства:</p>
|
||||
<div class="tabbed-set tabbed-alternate" data-tabs="8:13"><input checked="checked" id="__tabbed_8_1" name="__tabbed_8" type="radio" /><input id="__tabbed_8_2" name="__tabbed_8" type="radio" /><input id="__tabbed_8_3" name="__tabbed_8" type="radio" /><input id="__tabbed_8_4" name="__tabbed_8" type="radio" /><input id="__tabbed_8_5" name="__tabbed_8" type="radio" /><input id="__tabbed_8_6" name="__tabbed_8" type="radio" /><input id="__tabbed_8_7" name="__tabbed_8" type="radio" /><input id="__tabbed_8_8" name="__tabbed_8" type="radio" /><input id="__tabbed_8_9" name="__tabbed_8" type="radio" /><input id="__tabbed_8_10" name="__tabbed_8" type="radio" /><input id="__tabbed_8_11" name="__tabbed_8" type="radio" /><input id="__tabbed_8_12" name="__tabbed_8" type="radio" /><input id="__tabbed_8_13" name="__tabbed_8" type="radio" /><div class="tabbed-labels"><label for="__tabbed_8_1">Python</label><label for="__tabbed_8_2">C++</label><label for="__tabbed_8_3">Java</label><label for="__tabbed_8_4">C#</label><label for="__tabbed_8_5">Go</label><label for="__tabbed_8_6">Swift</label><label for="__tabbed_8_7">JS</label><label for="__tabbed_8_8">TS</label><label for="__tabbed_8_9">Dart</label><label for="__tabbed_8_10">Rust</label><label for="__tabbed_8_11">C</label><label for="__tabbed_8_12">Kotlin</label><label for="__tabbed_8_13">Ruby</label></div>
|
||||
<div class="tabbed-content">
|
||||
<div class="tabbed-block">
|
||||
@@ -6375,7 +6375,7 @@
|
||||
<p align="center"> Рисунок 2-18 Квадратичная пространственная сложность, порождаемая рекурсивной функцией </p>
|
||||
|
||||
<h3 id="4-o2n">4. Экспоненциальная сложность <span class="arithmatex">\(O(2^n)\)</span><a class="headerlink" href="#4-o2n" title="Permanent link">¶</a></h3>
|
||||
<p>Экспоненциальная сложность часто встречается у бинарных деревьев. Полное бинарное дерево с <span class="arithmatex">\(n\)</span> уровнями содержит <span class="arithmatex">\(2^n - 1\)</span> узлов и занимает <span class="arithmatex">\(O(2^n)\)</span> пространства:</p>
|
||||
<p>Экспоненциальная сложность часто встречается у бинарных деревьев. Как видно на рисунке 2-19, полное бинарное дерево с <span class="arithmatex">\(n\)</span> уровнями содержит <span class="arithmatex">\(2^n - 1\)</span> узлов и занимает <span class="arithmatex">\(O(2^n)\)</span> пространства:</p>
|
||||
<div class="tabbed-set tabbed-alternate" data-tabs="9:13"><input checked="checked" id="__tabbed_9_1" name="__tabbed_9" type="radio" /><input id="__tabbed_9_2" name="__tabbed_9" type="radio" /><input id="__tabbed_9_3" name="__tabbed_9" type="radio" /><input id="__tabbed_9_4" name="__tabbed_9" type="radio" /><input id="__tabbed_9_5" name="__tabbed_9" type="radio" /><input id="__tabbed_9_6" name="__tabbed_9" type="radio" /><input id="__tabbed_9_7" name="__tabbed_9" type="radio" /><input id="__tabbed_9_8" name="__tabbed_9" type="radio" /><input id="__tabbed_9_9" name="__tabbed_9" type="radio" /><input id="__tabbed_9_10" name="__tabbed_9" type="radio" /><input id="__tabbed_9_11" name="__tabbed_9" type="radio" /><input id="__tabbed_9_12" name="__tabbed_9" type="radio" /><input id="__tabbed_9_13" name="__tabbed_9" type="radio" /><div class="tabbed-labels"><label for="__tabbed_9_1">Python</label><label for="__tabbed_9_2">C++</label><label for="__tabbed_9_3">Java</label><label for="__tabbed_9_4">C#</label><label for="__tabbed_9_5">Go</label><label for="__tabbed_9_6">Swift</label><label for="__tabbed_9_7">JS</label><label for="__tabbed_9_8">TS</label><label for="__tabbed_9_9">Dart</label><label for="__tabbed_9_10">Rust</label><label for="__tabbed_9_11">C</label><label for="__tabbed_9_12">Kotlin</label><label for="__tabbed_9_13">Ruby</label></div>
|
||||
<div class="tabbed-content">
|
||||
<div class="tabbed-block">
|
||||
@@ -6559,11 +6559,11 @@
|
||||
<p align="center"> Рисунок 2-19 Экспоненциальная пространственная сложность, порождаемая полным бинарным деревом </p>
|
||||
|
||||
<h3 id="5-olog-n">5. Логарифмическая сложность <span class="arithmatex">\(O(\log n)\)</span><a class="headerlink" href="#5-olog-n" title="Permanent link">¶</a></h3>
|
||||
<p>Логарифмическая сложность часто встречается в алгоритмах "разделяй и властвуй". Например, при сортировке слиянием входной массив длины <span class="arithmatex">\(n\)</span> на каждом шаге рекурсии делится пополам, образуя рекурсивное дерево высоты <span class="arithmatex">\(\log n\)</span> и используя <span class="arithmatex">\(O(\log n)\)</span> пространства кадров стека.</p>
|
||||
<p>Логарифмическая сложность часто встречается в алгоритмах «разделяй и властвуй». Например, при сортировке слиянием входной массив длины <span class="arithmatex">\(n\)</span> на каждом шаге рекурсии делится пополам, образуя рекурсивное дерево высоты <span class="arithmatex">\(\log n\)</span> и используя <span class="arithmatex">\(O(\log n)\)</span> пространства кадров стека.</p>
|
||||
<p>Еще один пример - преобразование числа в строку. Если задано положительное целое число <span class="arithmatex">\(n\)</span> , то количество его цифр равно <span class="arithmatex">\(\lfloor \log_{10} n \rfloor + 1\)</span> , то есть длина соответствующей строки тоже равна <span class="arithmatex">\(\lfloor \log_{10} n \rfloor + 1\)</span> , следовательно, пространственная сложность составляет <span class="arithmatex">\(O(\log_{10} n + 1) = O(\log n)\)</span> .</p>
|
||||
<h2 id="244">2.4.4 Компромисс между временем и пространством<a class="headerlink" href="#244" title="Permanent link">¶</a></h2>
|
||||
<p>В идеальных условиях хотелось бы, чтобы и временная, и пространственная сложность алгоритма были оптимальными. Однако на практике одновременно оптимизировать и время, и память обычно очень трудно.</p>
|
||||
<p><strong>Снижение временной сложности обычно достигается ценой увеличения пространственной сложности, и наоборот</strong>. Подход, при котором жертвуют памятью ради ускорения работы алгоритма, называется обменом пространства на время; обратный подход называется обменом времени на пространство.</p>
|
||||
<p><strong>Снижение временной сложности обычно достигается ценой увеличения пространственной сложности, и наоборот</strong>. Подход, при котором жертвуют памятью ради ускорения работы алгоритма, называется обменом пространства на время. Обратный подход называется обменом времени на пространство.</p>
|
||||
<p>Выбор между этими двумя идеями зависит от того, что важнее в конкретной задаче. В большинстве случаев время ценнее памяти, поэтому стратегия обмена пространства на время используется чаще. Но при очень больших объемах данных контроль пространственной сложности тоже становится крайне важным.</p>
|
||||
|
||||
<!-- Source file information -->
|
||||
|
||||
@@ -4391,10 +4391,10 @@
|
||||
<li>Java и C# - объектно-ориентированные языки программирования, в которых блоки кода (методы) обычно являются частью класса. Статические методы по поведению похожи на функции, потому что они привязаны к классу и не могут обращаться к конкретным переменным экземпляра.</li>
|
||||
<li>C++ и Python поддерживают как процедурное программирование (функции), так и объектно-ориентированное программирование (методы).</li>
|
||||
</ul>
|
||||
<p><strong>Q</strong>: Отражает ли диаграмма "распространенных типов пространственной сложности" абсолютный размер занятой памяти?</p>
|
||||
<p><strong>Q</strong>: Отражает ли диаграмма «распространенных типов пространственной сложности» абсолютный размер занятой памяти?</p>
|
||||
<p>Нет, эта диаграмма показывает пространственную сложность, а значит отражает именно тенденцию роста, а не абсолютный объем занятого пространства.</p>
|
||||
<p>Если взять <span class="arithmatex">\(n = 8\)</span> , можно заметить, что значения на кривых не совпадают напрямую с соответствующими функциями. Это связано с тем, что каждая кривая содержит константный член, который сжимает диапазон значений до визуально удобного масштаба.</p>
|
||||
<p>На практике, поскольку мы обычно не знаем, какова "константная" сложность каждого метода, только по сложности мы, как правило, не можем выбрать оптимальное решение для случая <span class="arithmatex">\(n = 8\)</span> . Но для <span class="arithmatex">\(n = 8^5\)</span> выбор уже очевиден: в этой области доминирует именно тенденция роста.</p>
|
||||
<p>На практике, поскольку мы обычно не знаем, какова «константная» сложность каждого метода, только по сложности мы, как правило, не можем выбрать оптимальное решение для случая <span class="arithmatex">\(n = 8\)</span> . Но для <span class="arithmatex">\(n = 8^5\)</span> выбор уже очевиден: в этой области доминирует именно тенденция роста.</p>
|
||||
<p><strong>Q</strong>: Бывают ли случаи, когда в реальных сценариях алгоритм специально проектируют так, чтобы жертвовать временем ради пространства или пространством ради времени?</p>
|
||||
<p>На практике в большинстве случаев выбирают обмен пространства на время. Например, для индексов в базах данных обычно строят B+ деревья или хеш-индексы, расходуя значительный объем памяти ради эффективных запросов уровня <span class="arithmatex">\(O(\log n)\)</span> или даже <span class="arithmatex">\(O(1)\)</span>.</p>
|
||||
<p>В сценариях, где память особенно дорога, наоборот, могут жертвовать временем ради пространства. Например, в embedded-разработке память устройства очень ограничена, поэтому инженеры могут отказаться от хеш-таблиц и выбрать последовательный поиск по массиву, экономя память ценой более медленного поиска.</p>
|
||||
|
||||
@@ -4830,7 +4830,7 @@
|
||||
<p>Но на практике <strong>подсчитывать реальное время выполнения алгоритма и неразумно, и нереалистично</strong>. Во-первых, мы не хотим привязывать оценку времени к конкретной платформе, потому что алгоритм должен запускаться на самых разных платформах. Во-вторых, нам трудно определить время выполнения каждого типа операций, а это делает точную оценку крайне затруднительной.</p>
|
||||
<h2 id="231">2.3.1 Подсчет тенденции роста времени<a class="headerlink" href="#231" title="Permanent link">¶</a></h2>
|
||||
<p>Анализ временной сложности оценивает не само время выполнения алгоритма, <strong>а тенденцию роста этого времени по мере увеличения объема данных</strong>.</p>
|
||||
<p>Понятие "тенденции роста времени" выглядит довольно абстрактным, поэтому разберем его на примере. Предположим, размер входных данных равен <span class="arithmatex">\(n\)</span> , и даны три алгоритма <code>A</code> , <code>B</code> и <code>C</code> :</p>
|
||||
<p>Понятие «тенденции роста времени» выглядит довольно абстрактным, поэтому разберем его на примере. Предположим, размер входных данных равен <span class="arithmatex">\(n\)</span> , и даны три алгоритма <code>A</code> , <code>B</code> и <code>C</code> :</p>
|
||||
<div class="tabbed-set tabbed-alternate" data-tabs="2:13"><input checked="checked" id="__tabbed_2_1" name="__tabbed_2" type="radio" /><input id="__tabbed_2_2" name="__tabbed_2" type="radio" /><input id="__tabbed_2_3" name="__tabbed_2" type="radio" /><input id="__tabbed_2_4" name="__tabbed_2" type="radio" /><input id="__tabbed_2_5" name="__tabbed_2" type="radio" /><input id="__tabbed_2_6" name="__tabbed_2" type="radio" /><input id="__tabbed_2_7" name="__tabbed_2" type="radio" /><input id="__tabbed_2_8" name="__tabbed_2" type="radio" /><input id="__tabbed_2_9" name="__tabbed_2" type="radio" /><input id="__tabbed_2_10" name="__tabbed_2" type="radio" /><input id="__tabbed_2_11" name="__tabbed_2" type="radio" /><input id="__tabbed_2_12" name="__tabbed_2" type="radio" /><input id="__tabbed_2_13" name="__tabbed_2" type="radio" /><div class="tabbed-labels"><label for="__tabbed_2_1">Python</label><label for="__tabbed_2_2">C++</label><label for="__tabbed_2_3">Java</label><label for="__tabbed_2_4">C#</label><label for="__tabbed_2_5">Go</label><label for="__tabbed_2_6">Swift</label><label for="__tabbed_2_7">JS</label><label for="__tabbed_2_8">TS</label><label for="__tabbed_2_9">Dart</label><label for="__tabbed_2_10">Rust</label><label for="__tabbed_2_11">C</label><label for="__tabbed_2_12">Kotlin</label><label for="__tabbed_2_13">Ruby</label></div>
|
||||
<div class="tabbed-content">
|
||||
<div class="tabbed-block">
|
||||
@@ -5077,11 +5077,11 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<p>Ниже показаны временные сложности трех приведенных выше функций.</p>
|
||||
<p>На рисунке 2-7 показаны временные сложности трех приведенных выше функций.</p>
|
||||
<ul>
|
||||
<li>У алгоритма <code>A</code> есть только одна операция вывода, и время его работы не растет с увеличением <span class="arithmatex">\(n\)</span> . Такую временную сложность называют постоянной.</li>
|
||||
<li>В алгоритме <code>B</code> операция вывода выполняется в цикле <span class="arithmatex">\(n\)</span> раз, поэтому время работы растет линейно по мере увеличения <span class="arithmatex">\(n\)</span> . Такая временная сложность называется линейной.</li>
|
||||
<li>В алгоритме <code>C</code> операция вывода выполняется <span class="arithmatex">\(1000000\)</span> раз; хотя время работы велико, оно не зависит от размера входных данных <span class="arithmatex">\(n\)</span> . Поэтому временная сложность <code>C</code> такая же, как у <code>A</code> , и тоже является постоянной.</li>
|
||||
<li>В алгоритме <code>C</code> операция вывода выполняется <span class="arithmatex">\(1000000\)</span> раз. Хотя время работы велико, оно не зависит от размера входных данных <span class="arithmatex">\(n\)</span> . Поэтому временная сложность <code>C</code> такая же, как у <code>A</code> , и тоже является постоянной.</li>
|
||||
</ul>
|
||||
<p><img alt="Тенденции роста времени для алгоритмов A, B и C" class="animation-figure" src="../time_complexity.assets/time_complexity_simple_example.png" /></p>
|
||||
<p align="center"> Рисунок 2-7 Тенденции роста времени для алгоритмов A, B и C </p>
|
||||
@@ -5253,16 +5253,16 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<p>Пусть количество операций алгоритма является функцией от размера входных данных <span class="arithmatex">\(n\)</span> и обозначается как <span class="arithmatex">\(T(n)\)</span> ; тогда для приведенной выше функции число операций равно:</p>
|
||||
<p>Пусть количество операций алгоритма является функцией от размера входных данных <span class="arithmatex">\(n\)</span> и обозначается как <span class="arithmatex">\(T(n)\)</span>. Тогда для приведенной выше функции число операций равно:</p>
|
||||
<div class="arithmatex">\[
|
||||
T(n) = 3 + 2n
|
||||
\]</div>
|
||||
<p><span class="arithmatex">\(T(n)\)</span> - линейная функция, а это означает, что тенденция роста времени работы линейна, следовательно, временная сложность здесь тоже линейна.</p>
|
||||
<p>Линейную временную сложность записывают как <span class="arithmatex">\(O(n)\)</span> ; этот математический символ называется <u>нотацией Big <span class="arithmatex">\(O\)</span> (big-<span class="arithmatex">\(O\)</span> notation)</u> и обозначает <u>асимптотическую верхнюю границу (asymptotic upper bound)</u> функции <span class="arithmatex">\(T(n)\)</span> .</p>
|
||||
<p>Линейную временную сложность записывают как <span class="arithmatex">\(O(n)\)</span>. Этот математический символ называется <u>нотацией Big <span class="arithmatex">\(O\)</span> (big-<span class="arithmatex">\(O\)</span> notation)</u> и обозначает <u>асимптотическую верхнюю границу (asymptotic upper bound)</u> функции <span class="arithmatex">\(T(n)\)</span> .</p>
|
||||
<p>Иными словами, анализ временной сложности сводится к определению асимптотической верхней границы числа операций <span class="arithmatex">\(T(n)\)</span>, и у этого понятия есть строгое математическое определение.</p>
|
||||
<div class="admonition note">
|
||||
<p class="admonition-title">Асимптотическая верхняя граница функции</p>
|
||||
<p>Если существуют положительное действительное число <span class="arithmatex">\(c\)</span> и действительное число <span class="arithmatex">\(n_0\)</span> , такие что для всех <span class="arithmatex">\(n > n_0\)</span> выполняется <span class="arithmatex">\(T(n) \leq c \cdot f(n)\)</span> , то можно считать, что <span class="arithmatex">\(f(n)\)</span> задает асимптотическую верхнюю границу для <span class="arithmatex">\(T(n)\)</span> ; это записывается как <span class="arithmatex">\(T(n) = O(f(n))\)</span> .</p>
|
||||
<p>Если существуют положительное действительное число <span class="arithmatex">\(c\)</span> и действительное число <span class="arithmatex">\(n_0\)</span> , такие что для всех <span class="arithmatex">\(n > n_0\)</span> выполняется <span class="arithmatex">\(T(n) \leq c \cdot f(n)\)</span> , то можно считать, что <span class="arithmatex">\(f(n)\)</span> задает асимптотическую верхнюю границу для <span class="arithmatex">\(T(n)\)</span>. Это записывается как <span class="arithmatex">\(T(n) = O(f(n))\)</span> .</p>
|
||||
</div>
|
||||
<p>Как показано на рисунке 2-8, вычислить асимптотическую верхнюю границу - значит найти такую функцию <span class="arithmatex">\(f(n)\)</span> , что при стремлении <span class="arithmatex">\(n\)</span> к бесконечности функции <span class="arithmatex">\(T(n)\)</span> и <span class="arithmatex">\(f(n)\)</span> имеют один и тот же порядок роста и отличаются только постоянным коэффициентом <span class="arithmatex">\(c\)</span>.</p>
|
||||
<p><img alt="Асимптотическая верхняя граница функции" class="animation-figure" src="../time_complexity.assets/asymptotic_upper_bound.png" /></p>
|
||||
@@ -5276,7 +5276,7 @@ T(n) = 3 + 2n
|
||||
<ol>
|
||||
<li><strong>Игнорировать константы в <span class="arithmatex">\(T(n)\)</span></strong>. Они не зависят от <span class="arithmatex">\(n\)</span> , а значит не влияют на временную сложность.</li>
|
||||
<li><strong>Опускать все коэффициенты</strong>. Например, циклы на <span class="arithmatex">\(2n\)</span> раз или <span class="arithmatex">\(5n + 1\)</span> раз можно упростить до <span class="arithmatex">\(n\)</span> раз, потому что коэффициент перед <span class="arithmatex">\(n\)</span> не влияет на временную сложность.</li>
|
||||
<li><strong>При вложенных циклах использовать умножение</strong>. Общее число операций равно произведению числа операций внешнего и внутреннего циклов; при этом для каждого уровня цикла по-прежнему можно применять приемы из пунктов <code>1.</code> и <code>2.</code> .</li>
|
||||
<li><strong>При вложенных циклах использовать умножение</strong>. Общее число операций равно произведению числа операций внешнего и внутреннего циклов. При этом для каждого уровня цикла по-прежнему можно применять приемы из пунктов <code>1.</code> и <code>2.</code> .</li>
|
||||
</ol>
|
||||
<p>Для заданной функции мы можем использовать перечисленные выше приемы и подсчитать число операций:</p>
|
||||
<div class="tabbed-set tabbed-alternate" data-tabs="4:13"><input checked="checked" id="__tabbed_4_1" name="__tabbed_4" type="radio" /><input id="__tabbed_4_2" name="__tabbed_4" type="radio" /><input id="__tabbed_4_3" name="__tabbed_4" type="radio" /><input id="__tabbed_4_4" name="__tabbed_4" type="radio" /><input id="__tabbed_4_5" name="__tabbed_4" type="radio" /><input id="__tabbed_4_6" name="__tabbed_4" type="radio" /><input id="__tabbed_4_7" name="__tabbed_4" type="radio" /><input id="__tabbed_4_8" name="__tabbed_4" type="radio" /><input id="__tabbed_4_9" name="__tabbed_4" type="radio" /><input id="__tabbed_4_10" name="__tabbed_4" type="radio" /><input id="__tabbed_4_11" name="__tabbed_4" type="radio" /><input id="__tabbed_4_12" name="__tabbed_4" type="radio" /><input id="__tabbed_4_13" name="__tabbed_4" type="radio" /><div class="tabbed-labels"><label for="__tabbed_4_1">Python</label><label for="__tabbed_4_2">C++</label><label for="__tabbed_4_3">Java</label><label for="__tabbed_4_4">C#</label><label for="__tabbed_4_5">Go</label><label for="__tabbed_4_6">Swift</label><label for="__tabbed_4_7">JS</label><label for="__tabbed_4_8">TS</label><label for="__tabbed_4_9">Dart</label><label for="__tabbed_4_10">Rust</label><label for="__tabbed_4_11">C</label><label for="__tabbed_4_12">Kotlin</label><label for="__tabbed_4_13">Ruby</label></div>
|
||||
@@ -5498,7 +5498,7 @@ T(n) = 3 + 2n
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<p>Следующая формула показывает результаты подсчета до и после использования перечисленных выше приемов; в обоих случаях выводимая временная сложность равна <span class="arithmatex">\(O(n^2)\)</span> .</p>
|
||||
<p>Следующая формула показывает результаты подсчета до и после использования перечисленных выше приемов. В обоих случаях выводимая временная сложность равна <span class="arithmatex">\(O(n^2)\)</span> .</p>
|
||||
<div class="arithmatex">\[
|
||||
\begin{aligned}
|
||||
T(n) & = 2n(n + 1) + (5n + 1) + 2 & \text{полный подсчет (-.-|||)} \newline
|
||||
@@ -5544,7 +5544,7 @@ T(n) & = n^2 + n & \text{ленивый подсчет (o.O)}
|
||||
</table>
|
||||
</div>
|
||||
<h2 id="234">2.3.4 Распространенные типы<a class="headerlink" href="#234" title="Permanent link">¶</a></h2>
|
||||
<p>Пусть размер входных данных равен <span class="arithmatex">\(n\)</span> ; распространенные типы временной сложности показаны на рисунке 2-9 в порядке от меньшей к большей.</p>
|
||||
<p>Пусть размер входных данных равен <span class="arithmatex">\(n\)</span>. Распространенные типы временной сложности показаны на рисунке 2-9 в порядке от меньшей к большей.</p>
|
||||
<div class="arithmatex">\[
|
||||
\begin{aligned}
|
||||
& O(1) < O(\log n) < O(n) < O(n \log n) < O(n^2) < O(2^n) < O(n!) \newline
|
||||
@@ -6029,7 +6029,7 @@ T(n) & = n^2 + n & \text{ленивый подсчет (o.O)}
|
||||
<p><div style="height: 459px; width: 100%;"><iframe class="pythontutor-iframe" src="https://pythontutor.com/iframe-embed.html#code=def%20array_traversal%28nums%3A%20list%5Bint%5D%29%20-%3E%20int%3A%0A%20%20%20%20%22%22%22%D0%9B%D0%B8%D0%BD%D0%B5%D0%B9%D0%BD%D0%B0%D1%8F%20%D1%81%D0%BB%D0%BE%D0%B6%D0%BD%D0%BE%D1%81%D1%82%D1%8C%20%28%D0%BE%D0%B1%D1%85%D0%BE%D0%B4%20%D0%BC%D0%B0%D1%81%D1%81%D0%B8%D0%B2%D0%B0%29%22%22%22%0A%20%20%20%20count%20%3D%200%0A%20%20%20%20%23%20%D0%A7%D0%B8%D1%81%D0%BB%D0%BE%20%D0%B8%D1%82%D0%B5%D1%80%D0%B0%D1%86%D0%B8%D0%B9%20%D0%BF%D1%80%D0%BE%D0%BF%D0%BE%D1%80%D1%86%D0%B8%D0%BE%D0%BD%D0%B0%D0%BB%D1%8C%D0%BD%D0%BE%20%D0%B4%D0%BB%D0%B8%D0%BD%D0%B5%20%D0%BC%D0%B0%D1%81%D1%81%D0%B8%D0%B2%D0%B0%0A%20%20%20%20for%20num%20in%20nums%3A%0A%20%20%20%20%20%20%20%20count%20%2B%3D%201%0A%20%20%20%20return%20count%0A%0A%22%22%22Driver%20Code%22%22%22%0Aif%20__name__%20%3D%3D%20%22__main__%22%3A%0A%20%20%20%20n%20%3D%208%0A%20%20%20%20print%28%22%D0%A0%D0%B0%D0%B7%D0%BC%D0%B5%D1%80%20%D0%B2%D1%85%D0%BE%D0%B4%D0%BD%D1%8B%D1%85%20%D0%B4%D0%B0%D0%BD%D0%BD%D1%8B%D1%85%20n%20%3D%22%2C%20n%29%0A%0A%20%20%20%20count%20%3D%20array_traversal%28%5B0%5D%20%2A%20n%29%0A%20%20%20%20print%28%22%D0%A7%D0%B8%D1%81%D0%BB%D0%BE%20%D0%BE%D0%BF%D0%B5%D1%80%D0%B0%D1%86%D0%B8%D0%B9%20%D0%BB%D0%B8%D0%BD%D0%B5%D0%B9%D0%BD%D0%BE%D0%B9%20%D1%81%D0%BB%D0%BE%D0%B6%D0%BD%D0%BE%D1%81%D1%82%D0%B8%20%28%D0%BE%D0%B1%D1%85%D0%BE%D0%B4%20%D0%BC%D0%B0%D1%81%D1%81%D0%B8%D0%B2%D0%B0%29%20%3D%22%2C%20count%29&codeDivHeight=472&codeDivWidth=350&cumulative=false&curInstr=3&heapPrimitives=nevernest&origin=opt-frontend.js&py=311&rawInputLstJSON=%5B%5D&textReferences=false"> </iframe></div>
|
||||
<div style="margin-top: 5px;"><a href="https://pythontutor.com/iframe-embed.html#code=def%20array_traversal%28nums%3A%20list%5Bint%5D%29%20-%3E%20int%3A%0A%20%20%20%20%22%22%22%D0%9B%D0%B8%D0%BD%D0%B5%D0%B9%D0%BD%D0%B0%D1%8F%20%D1%81%D0%BB%D0%BE%D0%B6%D0%BD%D0%BE%D1%81%D1%82%D1%8C%20%28%D0%BE%D0%B1%D1%85%D0%BE%D0%B4%20%D0%BC%D0%B0%D1%81%D1%81%D0%B8%D0%B2%D0%B0%29%22%22%22%0A%20%20%20%20count%20%3D%200%0A%20%20%20%20%23%20%D0%A7%D0%B8%D1%81%D0%BB%D0%BE%20%D0%B8%D1%82%D0%B5%D1%80%D0%B0%D1%86%D0%B8%D0%B9%20%D0%BF%D1%80%D0%BE%D0%BF%D0%BE%D1%80%D1%86%D0%B8%D0%BE%D0%BD%D0%B0%D0%BB%D1%8C%D0%BD%D0%BE%20%D0%B4%D0%BB%D0%B8%D0%BD%D0%B5%20%D0%BC%D0%B0%D1%81%D1%81%D0%B8%D0%B2%D0%B0%0A%20%20%20%20for%20num%20in%20nums%3A%0A%20%20%20%20%20%20%20%20count%20%2B%3D%201%0A%20%20%20%20return%20count%0A%0A%22%22%22Driver%20Code%22%22%22%0Aif%20__name__%20%3D%3D%20%22__main__%22%3A%0A%20%20%20%20n%20%3D%208%0A%20%20%20%20print%28%22%D0%A0%D0%B0%D0%B7%D0%BC%D0%B5%D1%80%20%D0%B2%D1%85%D0%BE%D0%B4%D0%BD%D1%8B%D1%85%20%D0%B4%D0%B0%D0%BD%D0%BD%D1%8B%D1%85%20n%20%3D%22%2C%20n%29%0A%0A%20%20%20%20count%20%3D%20array_traversal%28%5B0%5D%20%2A%20n%29%0A%20%20%20%20print%28%22%D0%A7%D0%B8%D1%81%D0%BB%D0%BE%20%D0%BE%D0%BF%D0%B5%D1%80%D0%B0%D1%86%D0%B8%D0%B9%20%D0%BB%D0%B8%D0%BD%D0%B5%D0%B9%D0%BD%D0%BE%D0%B9%20%D1%81%D0%BB%D0%BE%D0%B6%D0%BD%D0%BE%D1%81%D1%82%D0%B8%20%28%D0%BE%D0%B1%D1%85%D0%BE%D0%B4%20%D0%BC%D0%B0%D1%81%D1%81%D0%B8%D0%B2%D0%B0%29%20%3D%22%2C%20count%29&codeDivHeight=800&codeDivWidth=600&cumulative=false&curInstr=3&heapPrimitives=nevernest&origin=opt-frontend.js&py=311&rawInputLstJSON=%5B%5D&textReferences=false" target="_blank" rel="noopener noreferrer">Во весь экран ></a></div></p>
|
||||
</details>
|
||||
<p>Стоит отметить, что <strong>размер входных данных <span class="arithmatex">\(n\)</span> нужно определять конкретно в зависимости от типа входа</strong>. Например, в первом примере переменная <span class="arithmatex">\(n\)</span> сама является размером входных данных; во втором примере размером данных служит длина массива.</p>
|
||||
<p>Стоит отметить, что <strong>размер входных данных <span class="arithmatex">\(n\)</span> нужно определять конкретно в зависимости от типа входа</strong>. Например, в первом примере переменная <span class="arithmatex">\(n\)</span> сама является размером входных данных. Во втором примере размером данных служит длина массива.</p>
|
||||
<h3 id="3-on2">3. Квадратичная сложность <span class="arithmatex">\(O(n^2)\)</span><a class="headerlink" href="#3-on2" title="Permanent link">¶</a></h3>
|
||||
<p>Квадратичная сложность характеризуется тем, что число операций растет квадратично относительно размера входных данных <span class="arithmatex">\(n\)</span> . Квадратичная сложность обычно встречается во вложенных циклах: временная сложность внешнего и внутреннего циклов равна <span class="arithmatex">\(O(n)\)</span> , поэтому общая временная сложность составляет <span class="arithmatex">\(O(n^2)\)</span> :</p>
|
||||
<div class="tabbed-set tabbed-alternate" data-tabs="8:13"><input checked="checked" id="__tabbed_8_1" name="__tabbed_8" type="radio" /><input id="__tabbed_8_2" name="__tabbed_8" type="radio" /><input id="__tabbed_8_3" name="__tabbed_8" type="radio" /><input id="__tabbed_8_4" name="__tabbed_8" type="radio" /><input id="__tabbed_8_5" name="__tabbed_8" type="radio" /><input id="__tabbed_8_6" name="__tabbed_8" type="radio" /><input id="__tabbed_8_7" name="__tabbed_8" type="radio" /><input id="__tabbed_8_8" name="__tabbed_8" type="radio" /><input id="__tabbed_8_9" name="__tabbed_8" type="radio" /><input id="__tabbed_8_10" name="__tabbed_8" type="radio" /><input id="__tabbed_8_11" name="__tabbed_8" type="radio" /><input id="__tabbed_8_12" name="__tabbed_8" type="radio" /><input id="__tabbed_8_13" name="__tabbed_8" type="radio" /><div class="tabbed-labels"><label for="__tabbed_8_1">Python</label><label for="__tabbed_8_2">C++</label><label for="__tabbed_8_3">Java</label><label for="__tabbed_8_4">C#</label><label for="__tabbed_8_5">Go</label><label for="__tabbed_8_6">Swift</label><label for="__tabbed_8_7">JS</label><label for="__tabbed_8_8">TS</label><label for="__tabbed_8_9">Dart</label><label for="__tabbed_8_10">Rust</label><label for="__tabbed_8_11">C</label><label for="__tabbed_8_12">Kotlin</label><label for="__tabbed_8_13">Ruby</label></div>
|
||||
@@ -6521,8 +6521,8 @@ T(n) & = n^2 + n & \text{ленивый подсчет (o.O)}
|
||||
<div style="margin-top: 5px;"><a href="https://pythontutor.com/iframe-embed.html#code=def%20bubble_sort%28nums%3A%20list%5Bint%5D%29%20-%3E%20int%3A%0A%20%20%20%20%22%22%22%D0%9A%D0%B2%D0%B0%D0%B4%D1%80%D0%B0%D1%82%D0%B8%D1%87%D0%BD%D0%B0%D1%8F%20%D1%81%D0%BB%D0%BE%D0%B6%D0%BD%D0%BE%D1%81%D1%82%D1%8C%20%28%D0%BF%D1%83%D0%B7%D1%8B%D1%80%D1%8C%D0%BA%D0%BE%D0%B2%D0%B0%D1%8F%20%D1%81%D0%BE%D1%80%D1%82%D0%B8%D1%80%D0%BE%D0%B2%D0%BA%D0%B0%29%22%22%22%0A%20%20%20%20count%20%3D%200%20%20%23%20%D0%A1%D1%87%D0%B5%D1%82%D1%87%D0%B8%D0%BA%0A%20%20%20%20%23%20%D0%92%D0%BD%D0%B5%D1%88%D0%BD%D0%B8%D0%B9%20%D1%86%D0%B8%D0%BA%D0%BB%3A%20%D0%BD%D0%B5%D0%BE%D1%82%D1%81%D0%BE%D1%80%D1%82%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%BD%D1%8B%D0%B9%20%D0%B4%D0%B8%D0%B0%D0%BF%D0%B0%D0%B7%D0%BE%D0%BD%20%5B0%2C%20i%5D%0A%20%20%20%20for%20i%20in%20range%28len%28nums%29%20-%201%2C%200%2C%20-1%29%3A%0A%20%20%20%20%20%20%20%20%23%20%D0%92%D0%BD%D1%83%D1%82%D1%80%D0%B5%D0%BD%D0%BD%D0%B8%D0%B9%20%D1%86%D0%B8%D0%BA%D0%BB%3A%20%D0%BF%D0%B5%D1%80%D0%B5%D0%BC%D0%B5%D1%81%D1%82%D0%B8%D1%82%D1%8C%20%D0%BC%D0%B0%D0%BA%D1%81%D0%B8%D0%BC%D0%B0%D0%BB%D1%8C%D0%BD%D1%8B%D0%B9%20%D1%8D%D0%BB%D0%B5%D0%BC%D0%B5%D0%BD%D1%82%20%D0%BD%D0%B5%D0%BE%D1%82%D1%81%D0%BE%D1%80%D1%82%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%BD%D0%BE%D0%B3%D0%BE%20%D0%B4%D0%B8%D0%B0%D0%BF%D0%B0%D0%B7%D0%BE%D0%BD%D0%B0%20%5B0%2C%20i%5D%20%D0%B2%20%D0%B5%D0%B3%D0%BE%20%D0%BF%D1%80%D0%B0%D0%B2%D1%8B%D0%B9%20%D0%BA%D0%BE%D0%BD%D0%B5%D1%86%0A%20%20%20%20%20%20%20%20for%20j%20in%20range%28i%29%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20if%20nums%5Bj%5D%20%3E%20nums%5Bj%20%2B%201%5D%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%23%20%D0%9F%D0%BE%D0%BC%D0%B5%D0%BD%D1%8F%D1%82%D1%8C%20%D0%BC%D0%B5%D1%81%D1%82%D0%B0%D0%BC%D0%B8%20nums%5Bj%5D%20%D0%B8%20nums%5Bj%20%2B%201%5D%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20tmp%20%3D%20nums%5Bj%5D%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20nums%5Bj%5D%20%3D%20nums%5Bj%20%2B%201%5D%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20nums%5Bj%20%2B%201%5D%20%3D%20tmp%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20count%20%2B%3D%203%20%20%23%20%D0%9E%D0%B1%D0%BC%D0%B5%D0%BD%20%D1%8D%D0%BB%D0%B5%D0%BC%D0%B5%D0%BD%D1%82%D0%BE%D0%B2%20%D0%B2%D0%BA%D0%BB%D1%8E%D1%87%D0%B0%D0%B5%D1%82%203%20%D1%8D%D0%BB%D0%B5%D0%BC%D0%B5%D0%BD%D1%82%D0%B0%D1%80%D0%BD%D1%8B%D0%B5%20%D0%BE%D0%BF%D0%B5%D1%80%D0%B0%D1%86%D0%B8%D0%B8%0A%20%20%20%20return%20count%0A%0A%22%22%22Driver%20Code%22%22%22%0Aif%20__name__%20%3D%3D%20%22__main__%22%3A%0A%20%20%20%20n%20%3D%208%0A%20%20%20%20print%28%22%D0%A0%D0%B0%D0%B7%D0%BC%D0%B5%D1%80%20%D0%B2%D1%85%D0%BE%D0%B4%D0%BD%D1%8B%D1%85%20%D0%B4%D0%B0%D0%BD%D0%BD%D1%8B%D1%85%20n%20%3D%22%2C%20n%29%0A%0A%20%20%20%20nums%20%3D%20%5Bi%20for%20i%20in%20range%28n%2C%200%2C%20-1%29%5D%20%20%23%20%5Bn%2C%20n-1%2C%20...%2C%202%2C%201%5D%0A%20%20%20%20count%20%3D%20bubble_sort%28nums%29%0A%20%20%20%20print%28%22%D0%A7%D0%B8%D1%81%D0%BB%D0%BE%20%D0%BE%D0%BF%D0%B5%D1%80%D0%B0%D1%86%D0%B8%D0%B9%20%D0%BA%D0%B2%D0%B0%D0%B4%D1%80%D0%B0%D1%82%D0%B8%D1%87%D0%BD%D0%BE%D0%B9%20%D1%81%D0%BB%D0%BE%D0%B6%D0%BD%D0%BE%D1%81%D1%82%D0%B8%20%28%D0%BF%D1%83%D0%B7%D1%8B%D1%80%D1%8C%D0%BA%D0%BE%D0%B2%D0%B0%D1%8F%20%D1%81%D0%BE%D1%80%D1%82%D0%B8%D1%80%D0%BE%D0%B2%D0%BA%D0%B0%29%20%3D%22%2C%20count%29&codeDivHeight=800&codeDivWidth=600&cumulative=false&curInstr=3&heapPrimitives=nevernest&origin=opt-frontend.js&py=311&rawInputLstJSON=%5B%5D&textReferences=false" target="_blank" rel="noopener noreferrer">Во весь экран ></a></div></p>
|
||||
</details>
|
||||
<h3 id="4-o2n">4. Экспоненциальная сложность <span class="arithmatex">\(O(2^n)\)</span><a class="headerlink" href="#4-o2n" title="Permanent link">¶</a></h3>
|
||||
<p>Типичный пример экспоненциального роста в биологии - деление клеток: в начальном состоянии есть одна клетка, после одного деления их становится 2, после двух делений - 4 и так далее; после <span class="arithmatex">\(n\)</span> раундов деления клеток становится <span class="arithmatex">\(2^n\)</span> .</p>
|
||||
<p>На рисунке 2-11 и в следующем коде моделируется процесс деления клеток; временная сложность равна <span class="arithmatex">\(O(2^n)\)</span> . Здесь входное значение <span class="arithmatex">\(n\)</span> обозначает число раундов деления, а возвращаемое значение <code>count</code> обозначает общее число делений.</p>
|
||||
<p>Типичный пример экспоненциального роста в биологии - деление клеток: в начальном состоянии есть одна клетка, после одного деления их становится 2, после двух делений - 4 и так далее. После <span class="arithmatex">\(n\)</span> раундов деления клеток становится <span class="arithmatex">\(2^n\)</span> .</p>
|
||||
<p>На рисунке 2-11 и в следующем коде моделируется процесс деления клеток. Временная сложность равна <span class="arithmatex">\(O(2^n)\)</span> . Здесь входное значение <span class="arithmatex">\(n\)</span> обозначает число раундов деления, а возвращаемое значение <code>count</code> обозначает общее число делений.</p>
|
||||
<div class="tabbed-set tabbed-alternate" data-tabs="10:13"><input checked="checked" id="__tabbed_10_1" name="__tabbed_10" type="radio" /><input id="__tabbed_10_2" name="__tabbed_10" type="radio" /><input id="__tabbed_10_3" name="__tabbed_10" type="radio" /><input id="__tabbed_10_4" name="__tabbed_10" type="radio" /><input id="__tabbed_10_5" name="__tabbed_10" type="radio" /><input id="__tabbed_10_6" name="__tabbed_10" type="radio" /><input id="__tabbed_10_7" name="__tabbed_10" type="radio" /><input id="__tabbed_10_8" name="__tabbed_10" type="radio" /><input id="__tabbed_10_9" name="__tabbed_10" type="radio" /><input id="__tabbed_10_10" name="__tabbed_10" type="radio" /><input id="__tabbed_10_11" name="__tabbed_10" type="radio" /><input id="__tabbed_10_12" name="__tabbed_10" type="radio" /><input id="__tabbed_10_13" name="__tabbed_10" type="radio" /><div class="tabbed-labels"><label for="__tabbed_10_1">Python</label><label for="__tabbed_10_2">C++</label><label for="__tabbed_10_3">Java</label><label for="__tabbed_10_4">C#</label><label for="__tabbed_10_5">Go</label><label for="__tabbed_10_6">Swift</label><label for="__tabbed_10_7">JS</label><label for="__tabbed_10_8">TS</label><label for="__tabbed_10_9">Dart</label><label for="__tabbed_10_10">Rust</label><label for="__tabbed_10_11">C</label><label for="__tabbed_10_12">Kotlin</label><label for="__tabbed_10_13">Ruby</label></div>
|
||||
<div class="tabbed-content">
|
||||
<div class="tabbed-block">
|
||||
@@ -6958,8 +6958,8 @@ T(n) & = n^2 + n & \text{ленивый подсчет (o.O)}
|
||||
</details>
|
||||
<p>Экспоненциальный рост происходит очень быстро и часто встречается в переборных методах, грубой силе, поиске с возвратом и тому подобных подходах. Для задач большого масштаба экспоненциальная сложность неприемлема, и обычно приходится применять динамическое программирование, жадные алгоритмы и другие стратегии.</p>
|
||||
<h3 id="5-olog-n">5. Логарифмическая сложность <span class="arithmatex">\(O(\log n)\)</span><a class="headerlink" href="#5-olog-n" title="Permanent link">¶</a></h3>
|
||||
<p>В противоположность экспоненциальной, логарифмическая сложность описывает ситуацию, когда <strong>в каждом раунде размер задачи уменьшается вдвое</strong>. Пусть размер входных данных равен <span class="arithmatex">\(n\)</span> ; так как на каждом шаге размер уменьшается вдвое, число итераций равно <span class="arithmatex">\(\log_2 n\)</span> , то есть является обратной функцией к <span class="arithmatex">\(2^n\)</span> .</p>
|
||||
<p>На рисунке 2-12 и в следующем коде моделируется процесс, в котором <strong>в каждом раунде размер задачи уменьшается вдвое</strong>; временная сложность равна <span class="arithmatex">\(O(\log_2 n)\)</span> и кратко записывается как <span class="arithmatex">\(O(\log n)\)</span> :</p>
|
||||
<p>В противоположность экспоненциальной, логарифмическая сложность описывает ситуацию, когда <strong>в каждом раунде размер задачи уменьшается вдвое</strong>. Пусть размер входных данных равен <span class="arithmatex">\(n\)</span>. Так как на каждом шаге размер уменьшается вдвое, число итераций равно <span class="arithmatex">\(\log_2 n\)</span> , то есть является обратной функцией к <span class="arithmatex">\(2^n\)</span> .</p>
|
||||
<p>На рисунке 2-12 и в следующем коде моделируется процесс, в котором <strong>в каждом раунде размер задачи уменьшается вдвое</strong>. Временная сложность равна <span class="arithmatex">\(O(\log_2 n)\)</span> и кратко записывается как <span class="arithmatex">\(O(\log n)\)</span> :</p>
|
||||
<div class="tabbed-set tabbed-alternate" data-tabs="12:13"><input checked="checked" id="__tabbed_12_1" name="__tabbed_12" type="radio" /><input id="__tabbed_12_2" name="__tabbed_12" type="radio" /><input id="__tabbed_12_3" name="__tabbed_12" type="radio" /><input id="__tabbed_12_4" name="__tabbed_12" type="radio" /><input id="__tabbed_12_5" name="__tabbed_12" type="radio" /><input id="__tabbed_12_6" name="__tabbed_12" type="radio" /><input id="__tabbed_12_7" name="__tabbed_12" type="radio" /><input id="__tabbed_12_8" name="__tabbed_12" type="radio" /><input id="__tabbed_12_9" name="__tabbed_12" type="radio" /><input id="__tabbed_12_10" name="__tabbed_12" type="radio" /><input id="__tabbed_12_11" name="__tabbed_12" type="radio" /><input id="__tabbed_12_12" name="__tabbed_12" type="radio" /><input id="__tabbed_12_13" name="__tabbed_12" type="radio" /><div class="tabbed-labels"><label for="__tabbed_12_1">Python</label><label for="__tabbed_12_2">C++</label><label for="__tabbed_12_3">Java</label><label for="__tabbed_12_4">C#</label><label for="__tabbed_12_5">Go</label><label for="__tabbed_12_6">Swift</label><label for="__tabbed_12_7">JS</label><label for="__tabbed_12_8">TS</label><label for="__tabbed_12_9">Dart</label><label for="__tabbed_12_10">Rust</label><label for="__tabbed_12_11">C</label><label for="__tabbed_12_12">Kotlin</label><label for="__tabbed_12_13">Ruby</label></div>
|
||||
<div class="tabbed-content">
|
||||
<div class="tabbed-block">
|
||||
@@ -7376,10 +7376,10 @@ T(n) & = n^2 + n & \text{ленивый подсчет (o.O)}
|
||||
<p><div style="height: 423px; width: 100%;"><iframe class="pythontutor-iframe" src="https://pythontutor.com/iframe-embed.html#code=def%20log_recur%28n%3A%20int%29%20-%3E%20int%3A%0A%20%20%20%20%22%22%22%D0%9B%D0%BE%D0%B3%D0%B0%D1%80%D0%B8%D1%84%D0%BC%D0%B8%D1%87%D0%B5%D1%81%D0%BA%D0%B0%D1%8F%20%D1%81%D0%BB%D0%BE%D0%B6%D0%BD%D0%BE%D1%81%D1%82%D1%8C%20%28%D1%80%D0%B5%D0%BA%D1%83%D1%80%D1%81%D0%B8%D0%B2%D0%BD%D0%B0%D1%8F%20%D1%80%D0%B5%D0%B0%D0%BB%D0%B8%D0%B7%D0%B0%D1%86%D0%B8%D1%8F%29%22%22%22%0A%20%20%20%20if%20n%20%3C%3D%201%3A%0A%20%20%20%20%20%20%20%20return%200%0A%20%20%20%20return%20log_recur%28n%20%2F%202%29%20%2B%201%0A%0A%22%22%22Driver%20Code%22%22%22%0Aif%20__name__%20%3D%3D%20%22__main__%22%3A%0A%20%20%20%20n%20%3D%208%0A%20%20%20%20print%28%22%D0%A0%D0%B0%D0%B7%D0%BC%D0%B5%D1%80%20%D0%B2%D1%85%D0%BE%D0%B4%D0%BD%D1%8B%D1%85%20%D0%B4%D0%B0%D0%BD%D0%BD%D1%8B%D1%85%20n%20%3D%22%2C%20n%29%0A%0A%20%20%20%20count%20%3D%20log_recur%28n%29%0A%20%20%20%20print%28%22%D0%A7%D0%B8%D1%81%D0%BB%D0%BE%20%D0%BE%D0%BF%D0%B5%D1%80%D0%B0%D1%86%D0%B8%D0%B9%20%D0%BB%D0%BE%D0%B3%D0%B0%D1%80%D0%B8%D1%84%D0%BC%D0%B8%D1%87%D0%B5%D1%81%D0%BA%D0%BE%D0%B9%20%D1%81%D0%BB%D0%BE%D0%B6%D0%BD%D0%BE%D1%81%D1%82%D0%B8%20%28%D1%80%D0%B5%D0%BA%D1%83%D1%80%D1%81%D0%B8%D0%B2%D0%BD%D0%B0%D1%8F%20%D1%80%D0%B5%D0%B0%D0%BB%D0%B8%D0%B7%D0%B0%D1%86%D0%B8%D1%8F%29%20%3D%22%2C%20count%29&codeDivHeight=472&codeDivWidth=350&cumulative=false&curInstr=4&heapPrimitives=nevernest&origin=opt-frontend.js&py=311&rawInputLstJSON=%5B%5D&textReferences=false"> </iframe></div>
|
||||
<div style="margin-top: 5px;"><a href="https://pythontutor.com/iframe-embed.html#code=def%20log_recur%28n%3A%20int%29%20-%3E%20int%3A%0A%20%20%20%20%22%22%22%D0%9B%D0%BE%D0%B3%D0%B0%D1%80%D0%B8%D1%84%D0%BC%D0%B8%D1%87%D0%B5%D1%81%D0%BA%D0%B0%D1%8F%20%D1%81%D0%BB%D0%BE%D0%B6%D0%BD%D0%BE%D1%81%D1%82%D1%8C%20%28%D1%80%D0%B5%D0%BA%D1%83%D1%80%D1%81%D0%B8%D0%B2%D0%BD%D0%B0%D1%8F%20%D1%80%D0%B5%D0%B0%D0%BB%D0%B8%D0%B7%D0%B0%D1%86%D0%B8%D1%8F%29%22%22%22%0A%20%20%20%20if%20n%20%3C%3D%201%3A%0A%20%20%20%20%20%20%20%20return%200%0A%20%20%20%20return%20log_recur%28n%20%2F%202%29%20%2B%201%0A%0A%22%22%22Driver%20Code%22%22%22%0Aif%20__name__%20%3D%3D%20%22__main__%22%3A%0A%20%20%20%20n%20%3D%208%0A%20%20%20%20print%28%22%D0%A0%D0%B0%D0%B7%D0%BC%D0%B5%D1%80%20%D0%B2%D1%85%D0%BE%D0%B4%D0%BD%D1%8B%D1%85%20%D0%B4%D0%B0%D0%BD%D0%BD%D1%8B%D1%85%20n%20%3D%22%2C%20n%29%0A%0A%20%20%20%20count%20%3D%20log_recur%28n%29%0A%20%20%20%20print%28%22%D0%A7%D0%B8%D1%81%D0%BB%D0%BE%20%D0%BE%D0%BF%D0%B5%D1%80%D0%B0%D1%86%D0%B8%D0%B9%20%D0%BB%D0%BE%D0%B3%D0%B0%D1%80%D0%B8%D1%84%D0%BC%D0%B8%D1%87%D0%B5%D1%81%D0%BA%D0%BE%D0%B9%20%D1%81%D0%BB%D0%BE%D0%B6%D0%BD%D0%BE%D1%81%D1%82%D0%B8%20%28%D1%80%D0%B5%D0%BA%D1%83%D1%80%D1%81%D0%B8%D0%B2%D0%BD%D0%B0%D1%8F%20%D1%80%D0%B5%D0%B0%D0%BB%D0%B8%D0%B7%D0%B0%D1%86%D0%B8%D1%8F%29%20%3D%22%2C%20count%29&codeDivHeight=800&codeDivWidth=600&cumulative=false&curInstr=4&heapPrimitives=nevernest&origin=opt-frontend.js&py=311&rawInputLstJSON=%5B%5D&textReferences=false" target="_blank" rel="noopener noreferrer">Во весь экран ></a></div></p>
|
||||
</details>
|
||||
<p>Логарифмическая сложность часто встречается в алгоритмах, основанных на стратегии "разделяй и властвуй", и отражает идеи разбиения на части и упрощения сложной задачи. Она растет медленно и считается одной из самых желательных временных сложностей после константной.</p>
|
||||
<p>Логарифмическая сложность часто встречается в алгоритмах, основанных на стратегии «разделяй и властвуй», и отражает идеи разбиения на части и упрощения сложной задачи. Она растет медленно и считается одной из самых желательных временных сложностей после константной.</p>
|
||||
<div class="admonition tip">
|
||||
<p class="admonition-title">Каково основание у <span class="arithmatex">\(O(\log n)\)</span> ?</p>
|
||||
<p>Точнее говоря, "разделение на <span class="arithmatex">\(m\)</span> частей" соответствует временной сложности <span class="arithmatex">\(O(\log_m n)\)</span> . А по формуле перехода к другому основанию логарифма мы получаем равные по сложности выражения с разными основаниями:</p>
|
||||
<p>Точнее говоря, «разделение на <span class="arithmatex">\(m\)</span> частей» соответствует временной сложности <span class="arithmatex">\(O(\log_m n)\)</span> . А по формуле перехода к другому основанию логарифма мы получаем равные по сложности выражения с разными основаниями:</p>
|
||||
<div class="arithmatex">\[
|
||||
O(\log_m n) = O(\log_k n / \log_k m) = O(\log_k n)
|
||||
\]</div>
|
||||
@@ -7775,7 +7775,7 @@ n! = n \times (n - 1) \times (n - 2) \times \dots \times 2 \times 1
|
||||
|
||||
<p>Следует отметить, что поскольку при <span class="arithmatex">\(n \geq 4\)</span> всегда выполняется <span class="arithmatex">\(n! > 2^n\)</span> , факториальная сложность растет еще быстрее, чем экспоненциальная, и при больших <span class="arithmatex">\(n\)</span> становится неприемлемой.</p>
|
||||
<h2 id="235">2.3.5 Худшая, лучшая и средняя временная сложность<a class="headerlink" href="#235" title="Permanent link">¶</a></h2>
|
||||
<p><strong>Временная эффективность алгоритма часто не фиксирована, а зависит от распределения входных данных</strong>. Предположим, на вход подается массив <code>nums</code> длины <span class="arithmatex">\(n\)</span> , состоящий из чисел от <span class="arithmatex">\(1\)</span> до <span class="arithmatex">\(n\)</span> , каждое из которых встречается ровно один раз; при этом порядок элементов случайно перемешан. Задача состоит в том, чтобы вернуть индекс элемента <span class="arithmatex">\(1\)</span> . Тогда можно сделать следующие выводы.</p>
|
||||
<p><strong>Временная эффективность алгоритма часто не фиксирована, а зависит от распределения входных данных</strong>. Предположим, на вход подается массив <code>nums</code> длины <span class="arithmatex">\(n\)</span> , состоящий из чисел от <span class="arithmatex">\(1\)</span> до <span class="arithmatex">\(n\)</span> , каждое из которых встречается ровно один раз. При этом порядок элементов случайно перемешан. Задача состоит в том, чтобы вернуть индекс элемента <span class="arithmatex">\(1\)</span> . Тогда можно сделать следующие выводы.</p>
|
||||
<ul>
|
||||
<li>Когда <code>nums = [?, ?, ..., 1]</code> , то есть когда последний элемент равен <span class="arithmatex">\(1\)</span> , нужно полностью пройти по массиву, <strong>что дает худшую временную сложность <span class="arithmatex">\(O(n)\)</span></strong> .</li>
|
||||
<li>Когда <code>nums = [1, ?, ?, ...]</code> , то есть когда первый элемент равен <span class="arithmatex">\(1\)</span> , независимо от длины массива продолжать обход не нужно, <strong>что дает лучшую временную сложность <span class="arithmatex">\(\Omega(1)\)</span></strong> .</li>
|
||||
@@ -8139,12 +8139,12 @@ n! = n \times (n - 1) \times (n - 2) \times \dots \times 2 \times 1
|
||||
<div style="margin-top: 5px;"><a href="https://pythontutor.com/iframe-embed.html#code=import%20random%0A%0Adef%20random_numbers%28n%3A%20int%29%20-%3E%20list%5Bint%5D%3A%0A%20%20%20%20%22%22%22%D0%A1%D0%B3%D0%B5%D0%BD%D0%B5%D1%80%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D1%82%D1%8C%20%D0%BC%D0%B0%D1%81%D1%81%D0%B8%D0%B2%20%D1%81%20%D1%8D%D0%BB%D0%B5%D0%BC%D0%B5%D0%BD%D1%82%D0%B0%D0%BC%D0%B8%201%2C%202%2C%20...%2C%20n%20%D0%B2%20%D1%81%D0%BB%D1%83%D1%87%D0%B0%D0%B9%D0%BD%D0%BE%D0%BC%20%D0%BF%D0%BE%D1%80%D1%8F%D0%B4%D0%BA%D0%B5%22%22%22%0A%20%20%20%20%23%20%D0%A1%D0%BE%D0%B7%D0%B4%D0%B0%D1%82%D1%8C%20%D0%BC%D0%B0%D1%81%D1%81%D0%B8%D0%B2%20nums%20%3D%3A%201%2C%202%2C%203%2C%20...%2C%20n%0A%20%20%20%20nums%20%3D%20%5Bi%20for%20i%20in%20range%281%2C%20n%20%2B%201%29%5D%0A%20%20%20%20%23%20%D0%A1%D0%BB%D1%83%D1%87%D0%B0%D0%B9%D0%BD%D0%BE%20%D0%BF%D0%B5%D1%80%D0%B5%D0%BC%D0%B5%D1%88%D0%B0%D1%82%D1%8C%20%D1%8D%D0%BB%D0%B5%D0%BC%D0%B5%D0%BD%D1%82%D1%8B%20%D0%BC%D0%B0%D1%81%D1%81%D0%B8%D0%B2%D0%B0%0A%20%20%20%20random.shuffle%28nums%29%0A%20%20%20%20return%20nums%0A%0Adef%20find_one%28nums%3A%20list%5Bint%5D%29%20-%3E%20int%3A%0A%20%20%20%20%22%22%22%D0%9D%D0%B0%D0%B9%D1%82%D0%B8%20%D0%B8%D0%BD%D0%B4%D0%B5%D0%BA%D1%81%20%D1%87%D0%B8%D1%81%D0%BB%D0%B0%201%20%D0%B2%20%D0%BC%D0%B0%D1%81%D1%81%D0%B8%D0%B2%D0%B5%20nums%22%22%22%0A%20%20%20%20for%20i%20in%20range%28len%28nums%29%29%3A%0A%20%20%20%20%20%20%20%20%23%20%D0%9A%D0%BE%D0%B3%D0%B4%D0%B0%20%D1%8D%D0%BB%D0%B5%D0%BC%D0%B5%D0%BD%D1%82%201%20%D0%BD%D0%B0%D1%85%D0%BE%D0%B4%D0%B8%D1%82%D1%81%D1%8F%20%D0%B2%20%D0%BD%D0%B0%D1%87%D0%B0%D0%BB%D0%B5%20%D0%BC%D0%B0%D1%81%D1%81%D0%B8%D0%B2%D0%B0%2C%20%D0%B4%D0%BE%D1%81%D1%82%D0%B8%D0%B3%D0%B0%D0%B5%D1%82%D1%81%D1%8F%20%D0%BB%D1%83%D1%87%D1%88%D0%B0%D1%8F%20%D0%B2%D1%80%D0%B5%D0%BC%D0%B5%D0%BD%D0%BD%D0%B0%D1%8F%20%D1%81%D0%BB%D0%BE%D0%B6%D0%BD%D0%BE%D1%81%D1%82%D1%8C%20O%281%29%0A%20%20%20%20%20%20%20%20%23%20%D0%9A%D0%BE%D0%B3%D0%B4%D0%B0%20%D1%8D%D0%BB%D0%B5%D0%BC%D0%B5%D0%BD%D1%82%201%20%D0%BD%D0%B0%D1%85%D0%BE%D0%B4%D0%B8%D1%82%D1%81%D1%8F%20%D0%B2%20%D0%BA%D0%BE%D0%BD%D1%86%D0%B5%20%D0%BC%D0%B0%D1%81%D1%81%D0%B8%D0%B2%D0%B0%2C%20%D0%B4%D0%BE%D1%81%D1%82%D0%B8%D0%B3%D0%B0%D0%B5%D1%82%D1%81%D1%8F%20%D1%85%D1%83%D0%B4%D1%88%D0%B0%D1%8F%20%D0%B2%D1%80%D0%B5%D0%BC%D0%B5%D0%BD%D0%BD%D0%B0%D1%8F%20%D1%81%D0%BB%D0%BE%D0%B6%D0%BD%D0%BE%D1%81%D1%82%D1%8C%20O%28n%29%0A%20%20%20%20%20%20%20%20if%20nums%5Bi%5D%20%3D%3D%201%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20return%20i%0A%20%20%20%20return%20-1%0A%0A%22%22%22Driver%20Code%22%22%22%0Aif%20__name__%20%3D%3D%20%22__main__%22%3A%0A%20%20%20%20n%20%3D%2010%0A%20%20%20%20nums%20%3D%20random_numbers%28n%29%0A%20%20%20%20index%20%3D%20find_one%28nums%29%0A%20%20%20%20print%28%22%5Cn%D0%9C%D0%B0%D1%81%D1%81%D0%B8%D0%B2%20%5B1%2C%202%2C%20...%2C%20n%5D%20%D0%BF%D0%BE%D1%81%D0%BB%D0%B5%20%D0%BF%D0%B5%D1%80%D0%B5%D0%BC%D0%B5%D1%88%D0%B8%D0%B2%D0%B0%D0%BD%D0%B8%D1%8F%20%3D%22%2C%20nums%29%0A%20%20%20%20print%28%22%D0%98%D0%BD%D0%B4%D0%B5%D0%BA%D1%81%20%D1%87%D0%B8%D1%81%D0%BB%D0%B0%201%20%3D%22%2C%20index%29&codeDivHeight=800&codeDivWidth=600&cumulative=false&curInstr=25&heapPrimitives=nevernest&origin=opt-frontend.js&py=311&rawInputLstJSON=%5B%5D&textReferences=false" target="_blank" rel="noopener noreferrer">Во весь экран ></a></div></p>
|
||||
</details>
|
||||
<p>Стоит отметить, что на практике лучшая временная сложность используется редко, поскольку обычно она достигается лишь с очень малой вероятностью и может вводить в заблуждение. <strong>Худшая временная сложность гораздо практичнее, потому что задает безопасную оценку эффективности</strong> и позволяет уверенно использовать алгоритм.</p>
|
||||
<p>Из приведенного выше примера видно, что худшая и лучшая временные сложности возникают только при особых распределениях данных; вероятность таких случаев может быть низкой, и они не всегда реально отражают эффективность алгоритма. Напротив, <strong>средняя временная сложность способна показать эффективность алгоритма на случайных входных данных</strong> и обозначается символом <span class="arithmatex">\(\Theta\)</span> .</p>
|
||||
<p>Для некоторых алгоритмов можно относительно просто вывести средний случай при случайном распределении данных. Например, в приведенном выше примере входной массив перемешан, а вероятность появления элемента <span class="arithmatex">\(1\)</span> на любом индексе одинакова; следовательно, среднее число итераций алгоритма равно половине длины массива, то есть <span class="arithmatex">\(n / 2\)</span> , а средняя временная сложность равна <span class="arithmatex">\(\Theta(n / 2) = \Theta(n)\)</span> .</p>
|
||||
<p>Из приведенного выше примера видно, что худшая и лучшая временные сложности возникают только при особых распределениях данных. Вероятность таких случаев может быть низкой, и они не всегда реально отражают эффективность алгоритма. Напротив, <strong>средняя временная сложность способна показать эффективность алгоритма на случайных входных данных</strong> и обозначается символом <span class="arithmatex">\(\Theta\)</span> .</p>
|
||||
<p>Для некоторых алгоритмов можно относительно просто вывести средний случай при случайном распределении данных. Например, в приведенном выше примере входной массив перемешан, а вероятность появления элемента <span class="arithmatex">\(1\)</span> на любом индексе одинакова. Следовательно, среднее число итераций алгоритма равно половине длины массива, то есть <span class="arithmatex">\(n / 2\)</span> , а средняя временная сложность равна <span class="arithmatex">\(\Theta(n / 2) = \Theta(n)\)</span> .</p>
|
||||
<p>Однако для более сложных алгоритмов вычислить среднюю временную сложность часто непросто, потому что трудно проанализировать полное математическое ожидание на заданном распределении данных. В таких случаях обычно используют худшую временную сложность как критерий оценки эффективности алгоритма.</p>
|
||||
<div class="admonition question">
|
||||
<p class="admonition-title">Почему символ <span class="arithmatex">\(\Theta\)</span> встречается так редко?</p>
|
||||
<p>Возможно, потому что символ <span class="arithmatex">\(O\)</span> звучит слишком привычно, и мы часто используем его для обозначения средней временной сложности. Но строго говоря, это некорректно. В этой книге и в других материалах, если встретится выражение вроде "средняя временная сложность <span class="arithmatex">\(O(n)\)</span>", просто понимай его как <span class="arithmatex">\(\Theta(n)\)</span> .</p>
|
||||
<p>Возможно, потому что символ <span class="arithmatex">\(O\)</span> звучит слишком привычно, и мы часто используем его для обозначения средней временной сложности. Но строго говоря, это некорректно. В этой книге и в других материалах, если встретится выражение вроде «средняя временная сложность <span class="arithmatex">\(O(n)\)</span>», просто понимай его как <span class="arithmatex">\(\Theta(n)\)</span> .</p>
|
||||
</div>
|
||||
|
||||
<!-- Source file information -->
|
||||
|
||||
Reference in New Issue
Block a user