|
27 / 6 / 0
Регистрация: 28.10.2010
Сообщений: 352
|
|
Объяснить рекурсию (на примере ханойской башни)09.12.2010, 20:59. Показов 15854. Ответов 27
Метки нет (Все метки)
0
|
|
| 09.12.2010, 20:59 | |
|
Ответы с готовыми решениями:
27
Визуализация решения Ханойской башни |
|
4903 / 2696 / 921
Регистрация: 29.11.2010
Сообщений: 5,783
|
|
| 09.12.2010, 21:10 | |
|
Чтобы понять рекурсию, нужно понять рекурсию.
GNU расшифровывается как "GNU is Not Unix".
1
|
|
|
27 / 6 / 0
Регистрация: 28.10.2010
Сообщений: 352
|
|
| 09.12.2010, 22:18 [ТС] | |
|
Так как её понять, если никто ее не объясняет?!
0
|
|
|
Шаровик затейник
696 / 445 / 78
Регистрация: 06.05.2010
Сообщений: 1,109
|
||||||
| 10.12.2010, 02:14 | ||||||
|
рекурсия это когда функция вызывает саму себя внутри своего тела функции, взять к примеру функцию вычисляющую факториал:
1
|
||||||
|
|
|||||||
| 10.12.2010, 12:34 | |||||||
|
Пример:
1
|
|||||||
|
64 / 64 / 12
Регистрация: 05.07.2010
Сообщений: 219
|
||||||
| 10.12.2010, 13:14 | ||||||
|
Рекурсивная функция-функция ,вызывающая сама себя. Каждое выполнение тела функции имеет свою область стека для параметров и локальных переменных.
Достоинство:создание компактного кода. Недостатки:затраты времени на вызов функции и передачу ей копий параметров, затраты памяти для организации каждого вложенного вызова. При этом следует избегать(по возможности) использования локальных переменных в рекурсивной функции. Например, факториал:
||стековый кадр для n=3 ||адрес возврата 1 ||3 стек при n=2 ||стековый кадр для n=2 ||адрес возврата 2 ||2 ||стековый кадр для n=3 ||адрес возврата 1 ||3 стек при n=1 ||стековый кадр для n=1 ||адрес возврата 3 ||1 ||стековый кадр для n=2 ||адрес возврата 2 ||2 ||стековый кадр для n=3 ||адрес возврата 1 ||3 Здесь каждый раз под х приходиться отводить память. В данном случае можно обойтись без локальной переменной. Пример в посте Crudelis
1
|
||||||
|
Модератор
12843 / 7592 / 1766
Регистрация: 25.07.2009
Сообщений: 13,973
|
|
| 10.12.2010, 13:28 | |
|
1
|
|
|
27 / 6 / 0
Регистрация: 28.10.2010
Сообщений: 352
|
||||||||||||
| 10.12.2010, 21:41 [ТС] | ||||||||||||
|
Спасибо! Можно вот на примере кода ханойской башни
Towers (number-1, from, free, to); cout << "Передвигаем " << number << "-й диск с "<< from << "-его стержня на " << to << "-ий \n"; Towers (number-1, free, to, from); Как здесь происходит обмен значений между переменными?
0
|
||||||||||||
|
27 / 6 / 0
Регистрация: 28.10.2010
Сообщений: 352
|
|
| 11.12.2010, 23:14 [ТС] | |
|
asics, прослушал спасибо, но там не объясняется!
0
|
|
|
|
|
| 12.12.2010, 08:40 | |
|
KOPC1886, а что может быть еще не понятного? Функция вызывает саму себя. Если на словах не понятно, то практика!!! Написали простеньку рекурсивную ф-цию (или возьмите одну из тех, что здесь предложили) и под отладчиком трассируем, при этом нужно следить за значением переменных. Особый инерес представляет когда ф-ция "скручивается", т.е. возвращается и в каждой ее копии будут разные значения переменных. Должно помочь ))
1
|
|
|
27 / 6 / 0
Регистрация: 28.10.2010
Сообщений: 352
|
|
| 15.12.2010, 20:46 [ТС] | |
|
Kastaneda, Я всё равно не понимаю, как происходит обмен значениями между переменными в коде ханойской башни.
0
|
|
|
|
||||||||||||
| 16.12.2010, 09:38 | ||||||||||||
Вот как это все выгдядет:
Добавлено через 12 часов 39 минут Есть мысль написать что-то типа FAQ про рекурсию, с очень подробным разъяснением (на низком уровне) и картинками. Если кто-нибудь напишет, что это нужно, вечером напишу.
1
|
||||||||||||
|
1 / 1 / 0
Регистрация: 13.08.2010
Сообщений: 88
|
|
| 16.12.2010, 10:37 | |
|
1
|
|
|
5058 / 3118 / 271
Регистрация: 11.11.2009
Сообщений: 7,044
|
|
| 16.12.2010, 12:08 | |
|
KOPC1886, мне кажется, вы не понимаете не рекурсию (хотя, возможно, и её вы тоже не понимаете), а именно рекурсивную реализацию Ханойских башен.
На самом деле в примере ханойских башен ничего никуда не передвигается (да и вообще, как там может что-то передвигаться, мы же в функцию всего лишь числа передаём), а создаётся видимость этого передвижения за счёт вывода на экран тех абстрактных передвижений, которые делает функция. Так что вы не понимаете Башни не за счёт того, что не понимаете рекурсию, а за счёт того, что вы не понимаете саму логику задачи о Башнях, которая к рекурсии никакого отношения не имеет.
1
|
|
|
|
|||||||||||
| 16.12.2010, 15:09 | |||||||||||
Сообщение было отмечено как решение
Решение
Итак, для начала разберем несколько моментов, которые необходимо знать, чтобы понять рекурсию.
Во первых – стек. Само понятие стека рассматривать не будем, т.к. подразумевается, что это знает каждый, если нет – в гугл. Следует отметить, что в каждой программе есть стек (на этапе компиляции под него выделяется память определенного размера, размер стека влияет на размер программы), который программа использует для своих целей. Цели разные, нас же интересует передача параметров для функции, и передача управления в вызываемую ф-цию (читай «возврат из ф-ции»). Во вторых – собственно передача параметров и возврат из ф-ции. Есть различные соглашения для вызовов ф-ций, в языке C используется соглашение cdecl (от «c-declaration»), однако для работы с WinAPI используется stdcall, но нас это сейчас не интересует. Соглашение cdecl звучит примерно так: «Аргументы передаются через стек, справа налево. За очистку стека отвечает вызывающая ф-ция.» Очистка стека нас не интересует, а вот передачу параметров, а так же вызов ф-ции и возврат из ф-ции разберем более подробно на примере такой программы.
Когда управление будет передано в функцию, то данные будут извлечены «сверху вниз» если смотреть по рисунку. Вроде здесь все понятно, более подробно объяснять думаю нет смысла. Теперь можно перейти непосредственно к самой рекурсии. Объяснение на словах, что такое рекурсия и с чем ее едят писалось на форуме выше, да в любой книге можно это найти, поэтому я не буду повторяться, сразу перейдем к примеру.
Итак программа доходит до строки int summa=func(a), первым делом будет вызвана ф-ция. Аргумент у нас один, поэтому он и будет положен в стек. Ф-ция принимает аргумент n, поэтому локальной переменной n будет присвоено значение, которое ф-ция вытащит из стека, т.е. число 3. Нужно уточнить, что «локальная» значит не только область видимости, но и то, что переменная создана в стеке (т.е. временная переменная, когда управление перейдет в вызывающую подпрограмму (т.е. в ф-цию main) эта переменная окажется в области стека «МУСОР») Итак мы находимся в программе, вот как выглядет стек: Далее выполняется строка if(n==1)return n, поскольку n сейчас не равно 1, то будет выполнена строка return n+ func (--n) (т.е. return 3+ func (2)) Значение переменной n у нас известно, а вот, что вернет func(2) пока нет, поэтому n будет запомнена, а для вызова ф-ции будет выделено дополнительное место в стеке. Вот стек после второго вызова ф-ции: Ф-ция начинает свою работу, if(n==1)return n, n опять не равно 1, поэтому вызываем дальше return n+ func (--n); (т.е. 2+ func(1)) И опять значение n запоминается, выделяется новое место в стеке для ф-ции, вот так: Ну вот, n=1, поэтому выполнется строка if(n==1)return n. Куда же вернется значение n? Туда, откуда была вызвана ф-ция, т.е. в предыдущую копию ф-ции, в которой n равно 2. В этой копии ф-ции мы остановились на строке return n+ func (--n), значение n у нас было известно ( n=2), вот теперь мы получили значение, которое вернула ф-ция (т.е. 1). Только теперь может выполниться эта строка return 2+1; Это значение опять возвращается в предыдущую копию ф-ции: Ну и наконец-то в первой копии ф-ции будет выполнена строка return n+ func (--n) (т.е. return 3+3). Таким образом в функцию main будет возвращено число 6, которое и присвоится переменной summa. Уфф, вроде все) P.S. когда сам понимаешь материал, кажется, что рассказал все понятно), если что-то не понятно - спрашивайте. P.P.S. давно я в paint'е не рисовал)))
20
|
|||||||||||
| 16.12.2010, 15:17 | |
|
Не по теме: Kastaneda, вам прямо учебники нужно писать :)
1
|
|
|
27 / 6 / 0
Регистрация: 28.10.2010
Сообщений: 352
|
||||||
| 16.12.2010, 20:49 [ТС] | ||||||
|
Kastaneda, спасибо большое, я понял в принципе как работает рекурсия. Но в ханойской башне там то функция ничего не возвращает.
opel ( n-1, s1, s3, s2); происходит обмен значениями между переменными s2 и s3(s1 остается неизменной). А в строке opel( n-1, s2, s1, s3); происходит обмен значениями между переменными s1 и s2. Почему происходит обмен значениями между переменными?
0
|
||||||
|
5058 / 3118 / 271
Регистрация: 11.11.2009
Сообщений: 7,044
|
|
| 16.12.2010, 21:11 | |
|
Итак, объясняю, какова логика решения задачи про ханойские башни.
Суть в том, что у нас имеется пирамида из n дисков, которая надета на один из штырей, и ещё два штыря - временный и целевой. Суть решения заключается в том, чтобы разбить нашу задачу на подзадачи, а именно - делим нашу пирамиду на две части - на пирамиду из n - 1 дисков (верхний набор) и на один единственный диск. Так вот, мы (абстрактно) берём нашу пирамиду из n - 1 дисков, переносим её на временный штырь, затем переносим наш единственный диск на целевой штырь, а затем переносим всю пирамиду на целевой штырь поверх уже перенесённого последнего диска. Поскольку мы не можем перенести всю пирамиду целиком, то мы меньшую пирамиду снова делим на две - пирамиду из уже n - 2 дисков и на отдельный диск и к полученной задаче применяем тот же алгоритм. В итоге мы добираемся до элементарной пирамиды из единственного диска, которую просто переносим на целевой штырь. Тут мы начинаем разворачивать рекурсию - вспоминать, что помимо перенесённой части изначальной пирамиды у нас есть ещё один диск - основание пирамиды. Теперь мы переносим это основание на целевой штырь, и всю нашу пирамиду (которая сейчас находится на временном штыре) переносим поверх её основания - диска, теперь находящегося на целевом штыре. Для этого снова применяем тот же алгоритм разбивания пирамиды на части. Перенеся часть пирамиды на основание, вспоминаем, что у полученной пирамиды так же отдельно есть основание, и переносим её на это основание. Так происходит до тех пор, пока вся изначальная пирамида из n дисков не окажется на целевом штыре. Вся фишка нашей рекурсивной функции не в том, что она физически что-то куда-то переносит, а в том, что она просто-напросто меняет функции штырей, ведь для каждой нашей подзадачи как целевой, как временный, так и исходный штыри могут меняться местами, т.е. перенесли мы пирамиду из, скажем, 4 дисков на временный (для неё) штырь, перенесли последний, пятый диск на целевой штырь, а для перенесённой части пирамиды из 4 дисков изначально временный штырь становится исходным, а в качестве временного будет служить изначально исходный. При этом глобальные функции штырей сохраняются (ведь для нас-то важны не функции штырей в пределах какой-либо из подзадач, а их функции для нашей изначальной задачи - для пирамиды из n дисков, вот эти глобальные назначения штырей функция и выводит на экран). Так вот функция всё то, что я описал, и делает. Сначала проверяется, больше ли n нуля (есть ли ещё диски для переноса), затем вызывает сама себя для n - 1 штыря, и меняет функции штырей (в качестве временного теперь выступает целевой, а в качестве целевого - временный). Затем, внутри этой вызванной функции снова проверяется количество дисков и вызывается ещё одна копия функции уже для ещё меньшей пирамиды из (n - 1) - 1, и снова меняется функция штырей. Так происходит, пока функция не вызовется для пирамиды из одного диска. Тогда будет напечатан мнимый перенос диска с одного штыря на другой и вызвана функция уже для переноса части пирамиды поверх этого перенесённого последнего штыря. Затем снова будет напечатан перенос, и снова какая-то часть пирамиды будет перенесена поверх перенесённого диска. Таким образом наши разрозненные пирамидки будут собираться в одну - изначальную, состоящую из n дисков. Вообще, главное здесь - понять, что функция и понятия не имеет о том, что нам нельзя класть больший диск поверх меньшего, это заложено в алгоритме, а функция лишь следует ему. Она на каждом шаге всего лишь печатает номера штырей - исходного и целевого, но если мы вспомним, что для каждого экземпляра функции назначения штырей меняются (целевой может стать временным, временный - исходным и т.д.), то становится очевидно, как происходит это мнимый обмен дисков.
3
|
|
|
27 / 6 / 0
Регистрация: 28.10.2010
Сообщений: 352
|
|
| 17.12.2010, 20:28 [ТС] | |
|
silent_1991, эм.... а можно не много по подробней о считывании значений в памяти, как все же это происходит?
1
|
|
| 17.12.2010, 20:28 | |
|
Помогаю со студенческими работами здесь
20
итерация Ханойской башни
Визуализация Ханойской башни Мой вариант реализации "Ханойской башни" Искать еще темы с ответами Или воспользуйтесь поиском по форуму: |
|
Новые блоги и статьи
|
|||
|
Модульный подход на примере F#
DevAlt 06.03.2026
В блоге дяди Боба наткнулся на такое определение:
В этой книге («Подход, основанный на вариантах использования») Ивар утверждает,
что архитектура программного обеспечения — это
структуры,. . .
|
Управление камерой с помощью скрипта OrbitControls.js на Three.js: Вращение, зум и панорамирование
8Observer8 05.03.2026
Содержание блога
Финальная демка в браузере работает на Desktop и мобильных браузерах. Итоговый код: orbit-controls-threejs-js. zip. Сканируйте QR-код на мобильном. Вращайте камеру одним пальцем,. . .
|
SDL3 для Web (WebAssembly): Синхронизация спрайтов SDL3 и тел Box2D
8Observer8 04.03.2026
Содержание блога
Финальная демка в браузере. Итоговый код: finish-sync-physics-sprites-sdl3-c. zip
На первой гифке отладочные линии отключены, а на второй включены:. . .
|
SDL3 для Web (WebAssembly): Идентификация объектов на Box2D v3 - использование userData и событий коллизий
8Observer8 02.03.2026
Содержание блога
Финальная демка в браузере. Итоговый код: finish-collision-events-sdl3-c. zip Сканируйте QR-код на мобильном и вы увидите, что появится джойстик для управления главным героем.
. . .
|
|
Реалии
Hrethgir 01.03.2026
Нет, я не закончил до сих пор симулятор. Эта задача сложнее. Не получилось уйти в плавсостав, но оно и к лучшему, возможно. Точнее получалось - но сварщиком в палубную команду, а это значит, в моём. . .
|
Ритм жизни
kumehtar 27.02.2026
Иногда приходится жить в ритме, где дел становится всё больше, а вовлечения в происходящее — всё меньше. Плотный график не даёт вниманию закрепиться ни на одном событии. Утро начинается с быстрых,. . .
|
SDL3 для Web (WebAssembly): Сборка библиотек: SDL3, Box2D, FreeType, SDL3_ttf, SDL3_mixer и SDL3_image из исходников с помощью CMake и Emscripten
8Observer8 27.02.2026
Недавно вышла версия 3. 4. 2 библиотеки SDL3. На странице официальной релиза доступны исходники, готовые DLL (для x86, x64, arm64), а также библиотеки для разработки под Android, MinGW и Visual Studio. . . .
|
SDL3 для Web (WebAssembly): Реализация движения на Box2D v3 - трение и коллизии с повёрнутыми стенами
8Observer8 20.02.2026
Содержание блога
Box2D позволяет легко создать главного героя, который не проходит сквозь стены и перемещается с заданным трением о препятствия, которые можно располагать под углом, как верхнее. . .
|