Инструменты пользователя

Инструменты сайта


basics_of_algorithms:recursion

Различия

Здесь показаны различия между двумя версиями данной страницы.

Ссылка на это сравнение

Предыдущая версия справа и слева Предыдущая версия
Следующая версия
Предыдущая версия
basics_of_algorithms:recursion [2023/10/04 21:08]
werwolf [Что такое рекурсия]
basics_of_algorithms:recursion [2023/10/04 21:22] (текущий)
werwolf [Достоинства и недостатки рекурсии]
Строка 188: Строка 188:
 Один из распространенных алгоритмов в программировании — это **бинарный поиск**,​ который мы уже изучали в этом курсе. Мы уже рассмотрели его реализацию через циклы: Один из распространенных алгоритмов в программировании — это **бинарный поиск**,​ который мы уже изучали в этом курсе. Мы уже рассмотрели его реализацию через циклы:
  
-<​code>​+<​code ​javascript>
 const binarySearch = (items, value) => { const binarySearch = (items, value) => {
   let left = 0;   let left = 0;
Строка 216: Строка 216:
 </​code>​ </​code>​
  
-Python 
  
-<​code>​+<​details>​ 
 +<​summary>​Python</​summary>​ 
 + 
 +<​code ​python>
 def binary_search(items,​ value): def binary_search(items,​ value):
     left = 0     left = 0
Строка 242: Строка 244:
 print(binary_search(items,​ 7))  # => 5 print(binary_search(items,​ 7))  # => 5
 </​code>​ </​code>​
 +</​details>​
  
-PHP+<​details>​ 
 +<​summary>​PHP</​summary>​
  
-<​code>​+<​code ​php>
 <?php <?php
  
Строка 275: Строка 279:
 print_r(binarySearch($items,​ 7)); // => 5 print_r(binarySearch($items,​ 7)); // => 5
 </​code>​ </​code>​
 +</​details>​
  
-Java+<​details>​ 
 +<​summary>​Java</​summary>​
  
-<​code>​+<​code ​java>
 class App { class App {
     public static int binarySearch(List<​Integer>​ items, int value) {     public static int binarySearch(List<​Integer>​ items, int value) {
Строка 307: Строка 313:
 App.binarySearch(items,​ 7); // 5 App.binarySearch(items,​ 7); // 5
 </​code>​ </​code>​
 +</​details>​
  
 Тогда мы описывали алгоритм через циклы, поэтому объяснение было подробным и сложным. Алгоритм бинарного поиска можно описать намного проще, если опираться на знания о рекурсии:​ Тогда мы описывали алгоритм через циклы, поэтому объяснение было подробным и сложным. Алгоритм бинарного поиска можно описать намного проще, если опираться на знания о рекурсии:​
Строка 317: Строка 324:
 И это все: мы свели задачу поиска к самой себе. Сначала зона поиска — это весь массив,​ но на каждом шаге она сужается в два раза. Если нужного элемента в массиве не окажется,​ зона поиска сузится до пустого массива,​ и поиск завершится неудачно:​ И это все: мы свели задачу поиска к самой себе. Сначала зона поиска — это весь массив,​ но на каждом шаге она сужается в два раза. Если нужного элемента в массиве не окажется,​ зона поиска сузится до пустого массива,​ и поиск завершится неудачно:​
  
-<​code>​+<​code ​javascript>
 const binarySearch = (items, value, left, right) => { const binarySearch = (items, value, left, right) => {
   if (left > right) {   if (left > right) {
Строка 341: Строка 348:
 </​code>​ </​code>​
  
-Python+<​details>​ 
 +<​summary>​Python</​summary>​
  
-<​code>​+<​code ​python>
 def binary_search(items,​ value, left, right): def binary_search(items,​ value, left, right):
     if left > right:     if left > right:
Строка 364: Строка 372:
 print(binary_search(items,​ 7, 0, len(items) - 1))  # => 5 print(binary_search(items,​ 7, 0, len(items) - 1))  # => 5
 </​code>​ </​code>​
 +</​details>​
  
-PHP+<​details>​ 
 +<​summary>​PHP</​summary>​
  
-<​code>​+<​code ​php>
 <?php <?php
  
Строка 393: Строка 403:
 print_r(binarySearch(items,​ 7, 0, items.length - 1)); // => 5 print_r(binarySearch(items,​ 7, 0, items.length - 1)); // => 5
 </​code>​ </​code>​
 +</​details>​
  
-Java+<​details>​ 
 +<​summary>​Java</​summary>​
  
-<​code>​+<​code ​java>
 class App { class App {
     public static int binarySearch(List<​Integer>​ items, int value, int left, int right) {     public static int binarySearch(List<​Integer>​ items, int value, int left, int right) {
Строка 422: Строка 434:
 App.binarySearch(items,​ 7, 0, items.size() - 1); // 5 App.binarySearch(items,​ 7, 0, items.size() - 1); // 5
 </​code>​ </​code>​
 +</​details>​
  
 https://​replit.com/​@hexlet/​algorithms-recursion https://​replit.com/​@hexlet/​algorithms-recursion
Строка 431: Строка 444:
 ===== Рекурсивный алгоритм для Ханойской башни ===== ===== Рекурсивный алгоритм для Ханойской башни =====
  
-{{https://​cdn2.hexlet.io/​derivations/​image/​original/​eyJpZCI6IjY1NDQ2Mzg5NDE3MzE5NjQ4OTFmMWM5ZGRjMTViZjdhLmpwZyIsInN0b3JhZ2UiOiJjYWNoZSJ9?​signature=5cd19f7555be298f6279b9f25069feea31d7d5b535e44c77358cf4730ecca7b6|eyJpZCI6IjY1NDQ2Mzg5NDE3MzE5NjQ4OTFmMWM5ZGRjMTViZjdhLmpwZyIsInN0b3JhZ2UiOiJjYWNoZSJ9?​signature=5cd19f7555be298f6279b9f25069feea31d7d5b535e44c77358cf4730ecca7b6}}Ханойская башня — это знаменитая древняя головоломка. Она состоит из трех стержней,​ на один из которых надеты кольца разного размера.+{{ :basics_of_algorithms:​image_processing20231003-23-yrsg1a.jpg |}} 
 + 
 +Ханойская башня — это знаменитая древняя головоломка. Она состоит из трех стержней,​ на один из которых надеты кольца разного размера.
  
 Надо перенести все кольца на другой стержень,​ при этом кольца можно переносить только по одному. Главное правило:​ никогда нельзя класть кольцо большего размера на кольцо меньшего. Надо перенести все кольца на другой стержень,​ при этом кольца можно переносить только по одному. Главное правило:​ никогда нельзя класть кольцо большего размера на кольцо меньшего.
Строка 459: Строка 474:
 Только мы знаем, что рекурсия должна где-то останавливаться. В нашем случае минимальная задача,​ которую мы можем решить — перенос башни из одного кольца. Она решается тривиально:​ одно кольцо мы можем перенести безо всяких условий,​ поэтому нам достаточно просто вывести номера стержней «откуда» и «куда»:​ Только мы знаем, что рекурсия должна где-то останавливаться. В нашем случае минимальная задача,​ которую мы можем решить — перенос башни из одного кольца. Она решается тривиально:​ одно кольцо мы можем перенести безо всяких условий,​ поэтому нам достаточно просто вывести номера стержней «откуда» и «куда»:​
  
-<​code>​+<​code ​javascript>
 const hanoi = (height, from, to) => { const hanoi = (height, from, to) => {
   if (height === 1) {   if (height === 1) {
Строка 490: Строка 505:
 </​code>​ </​code>​
  
-Python 
  
-<​code>​+<​details>​ 
 +<​summary>​Python</​summary>​ 
 + 
 +<​code ​python>
 def hanoi(height,​ start, to): def hanoi(height,​ start, to):
     if height == 1:     if height == 1:
Строка 520: Строка 537:
 # => с 3 на 2 # => с 3 на 2
 </​code>​ </​code>​
 +</​details>​
  
-PHP+<​details>​ 
 +<​summary>​PHP</​summary>​
  
-<​code>​+<​code ​php>
 <?php <?php
  
Строка 556: Строка 575:
 // => с 3 на 2 // => с 3 на 2
 </​code>​ </​code>​
 +</​details>​
  
-Java+<​details>​ 
 +<​summary>​Java</​summary>​
  
-<​code>​+<​code ​java>
 class App { class App {
     public static void hanoi(int height, int from, int to) {     public static void hanoi(int height, int from, int to) {
Строка 591: Строка 612:
 // => с 3 на 2 // => с 3 на 2
 </​code>​ </​code>​
 +</​details>​
  
 https://​replit.com/​@hexlet/​hanoi https://​replit.com/​@hexlet/​hanoi
Строка 600: Строка 622:
 Чтобы определить номер вспомогательного стержня,​ надо написать сложное условие:​ Чтобы определить номер вспомогательного стержня,​ надо написать сложное условие:​
  
-<​code>​+<​code ​javascript>
 if (from === 1 && to === 2 || from === 2 && to === 1) { if (from === 1 && to === 2 || from === 2 && to === 1) {
   temporary = 3;   temporary = 3;
Строка 610: Строка 632:
 </​code>​ </​code>​
  
-Python+<​details>​ 
 +<​summary>​Python</​summary>​
  
-<​code>​+<​code ​python>
 if (start == 1 and to == 2) or (start == 2 and to == 1): if (start == 1 and to == 2) or (start == 2 and to == 1):
     temporary = 3     temporary = 3
Строка 621: Строка 644:
 </​code>​ </​code>​
  
-PHP+</​details>​ 
 + 
 +<​details>​ 
 +<​summary>​PHP</​summary>​
  
-<​code>​+<​code ​php>
 <?php <?php
  
Строка 634: Строка 660:
 } }
 </​code>​ </​code>​
 +</​details>​
  
-Java+<​details>​ 
 +<​summary>​Java</​summary>​
  
-<​code>​+<​code ​java>
 if (from == 1 && to == 2 || from == 2 && to == 1) { if (from == 1 && to == 2 || from == 2 && to == 1) {
     temporary = 3;     temporary = 3;
Строка 646: Строка 674:
 } }
 </​code>​ </​code>​
 +</​details>​
  
 Несмотря на то, что логика этого кода понятна,​ само условие выглядит громоздким. Разберем по шагам, как оно работает. В коде используют три константы:​ Несмотря на то, что логика этого кода понятна,​ само условие выглядит громоздким. Разберем по шагам, как оно работает. В коде используют три константы:​
Строка 657: Строка 686:
 Обратите внимание,​ что сумма ''​from'',​ ''​to''​ и ''​temporary''​ всегда равна 6. Можно взять число 6 и вычесть из него номера стержней ''​from''​ и ''​to'',​ и тогда мы узнаем номер вспомогательного стержня:​ Обратите внимание,​ что сумма ''​from'',​ ''​to''​ и ''​temporary''​ всегда равна 6. Можно взять число 6 и вычесть из него номера стержней ''​from''​ и ''​to'',​ и тогда мы узнаем номер вспомогательного стержня:​
  
-<​code>​+<​code ​javascript>
 const temporary = 6 - from - to; const temporary = 6 - from - to;
 </​code>​ </​code>​
  
-Python+<​details>​ 
 +<​summary>​Python</​summary>​
  
-<​code>​+<​code ​python>
 temporary = 6 - start - to temporary = 6 - start - to
 </​code>​ </​code>​
 +</​details>​
  
-PHP+<​details>​ 
 +<​summary>​PHP</​summary>​
  
-<​code>​+<​code ​php>
 <?php <?php
  
 $temporary = 6 - $start - $to; $temporary = 6 - $start - $to;
 </​code>​ </​code>​
 +</​details>​
  
-Java+<​details>​ 
 +<​summary>​Java</​summary>​
  
-<​code>​+<​code ​java>
 var temporary = 6 - from - to; var temporary = 6 - from - to;
 </​code>​ </​code>​
 +</​details>​
  
 Этот код гораздо проще, но требует разъяснений. В начале каждой рекурсивной функции мы должны проверить **граничное условие** — оно помогает не войти в бесконечный вызов. В задаче о ханойской башне граничное условие — это башня из одного кольца. Этот код гораздо проще, но требует разъяснений. В начале каждой рекурсивной функции мы должны проверить **граничное условие** — оно помогает не войти в бесконечный вызов. В задаче о ханойской башне граничное условие — это башня из одного кольца.
Строка 685: Строка 720:
 Если встретилась такая башня, надо перенести кольцо и завершить работу:​ Если встретилась такая башня, надо перенести кольцо и завершить работу:​
  
-<​code>​+<​code ​javascript>
 if (height === 1) { if (height === 1) {
   console.log("​с %d на %d", from, to);   console.log("​с %d на %d", from, to);
Строка 692: Строка 727:
 </​code>​ </​code>​
  
-Python+<​details>​ 
 +<​summary>​Python</​summary>​
  
-<​code>​+<​code ​python>
 if height == 1: if height == 1:
     print(f'​с {start} на {to}')     print(f'​с {start} на {to}')
     return     return
 </​code>​ </​code>​
 +</​details>​
  
-PHP+<​details>​ 
 +<​summary>​PHP</​summary>​
  
-<​code>​+<​code ​php>
 <?php <?php
  
Строка 710: Строка 748:
 } }
 </​code>​ </​code>​
 +</​details>​
  
-Java+<​details>​ 
 +<​summary>​Java</​summary>​
  
-<​code>​+<​code ​java>
 if (height == 1) { if (height == 1) {
     System.out.println(String.format("​с %d на %d", from, to));     System.out.println(String.format("​с %d на %d", from, to));
Строка 719: Строка 759:
 } }
 </​code>​ </​code>​
 +</​details>​
  
 Если высота башни ''​height''​ больше единицы,​ мы упрощаем задачу и сводим ее к самой себе. В случае ханойской башни такая задача — перенести башни высотой ''​height - 1''​ на вспомогательный стержень. После этого можно перенести самое большое кольцо на стержень «куда»,​ а потом — переместить на него маленькую башню со вспомогательного стержня:​ Если высота башни ''​height''​ больше единицы,​ мы упрощаем задачу и сводим ее к самой себе. В случае ханойской башни такая задача — перенести башни высотой ''​height - 1''​ на вспомогательный стержень. После этого можно перенести самое большое кольцо на стержень «куда»,​ а потом — переместить на него маленькую башню со вспомогательного стержня:​
  
-<​code>​+<​code ​javascript>
 hanoi(height - 1, from, temporary); hanoi(height - 1, from, temporary);
 console.log("​с %d на %d", from, to); console.log("​с %d на %d", from, to);
Строка 728: Строка 769:
 </​code>​ </​code>​
  
-Python+<​details>​ 
 +<​summary>​Python</​summary>​
  
-<​code>​+<​code ​python
 hanoi(height - 1, start, temporary) hanoi(height - 1, start, temporary)
 print(f'​с {start} на {to}'​);​ print(f'​с {start} на {to}'​);​
 hanoi(height - 1, temporary, to) hanoi(height - 1, temporary, to)
 </​code>​ </​code>​
 +</​details>​
  
-PHP+<​details>​ 
 +<​summary>​PHP</​summary>​
  
-<​code>​+<​code ​php>
 <?php <?php
  
Строка 745: Строка 789:
 hanoi($height - 1, $temporary, $to); hanoi($height - 1, $temporary, $to);
 </​code>​ </​code>​
 +</​details>​
  
-Java+<​details>​ 
 +<​summary>​Java</​summary>​
  
-<​code>​+<​code ​java>
 hanoi(height - 1, from, temporary); hanoi(height - 1, from, temporary);
 System.out.println(String.format("​с %d на %d", from, to)); System.out.println(String.format("​с %d на %d", from, to));
 hanoi(height - 1, temporary, to); hanoi(height - 1, temporary, to);
 </​code>​ </​code>​
 +</​details>​
  
 ===== Достоинства и недостатки рекурсии ===== ===== Достоинства и недостатки рекурсии =====
Строка 760: Строка 807:
 Еще один плюс — знания о рекурсии открывают программисту новые возможности. Многие задачи можно решить и с помощью циклов,​ но есть и те, в которых не обойтись без рекурсии. Например,​ синтаксический разбор арифметических выражений можно сделать только рекурсивно. Вот так выглядит сложное арифметическое выражение,​ если решать его через рекурсивный алгоритм:​ Еще один плюс — знания о рекурсии открывают программисту новые возможности. Многие задачи можно решить и с помощью циклов,​ но есть и те, в которых не обойтись без рекурсии. Например,​ синтаксический разбор арифметических выражений можно сделать только рекурсивно. Вот так выглядит сложное арифметическое выражение,​ если решать его через рекурсивный алгоритм:​
  
-<​code>​+<​code ​javascript>
 sin(a) + (3 + 2 * b ** 7 - cos (a / b)) sin(a) + (3 + 2 * b ** 7 - cos (a / b))
 </​code>​ </​code>​
basics_of_algorithms/recursion.1696442883.txt.gz · Последние изменения: 2023/10/04 21:08 — werwolf