Ханойская башня: рекурсивный алгоритм
хотя у меня нет никаких проблем с пониманием рекурсии, я не могу, кажется, обернуть голову вокруг рекурсивного решения проблемы Ханойской башни. Вот код Википедия:
procedure Hanoi(n: integer; source, dest, by: char);
Begin
if (n=1) then
writeln('Move the plate from ', source, ' to ', dest)
else begin
Hanoi(n-1, source, by, dest);
writeln('Move the plate from ', source, ' to ', dest);
Hanoi(n-1, by, dest, source);
end;
End;
Я понимаю базовый случай и концепцию разбиения проблемы на более мелкие части, пока вы не сможете переместить один диск. Однако я не могу понять, как два рекурсивных вызова в неосновном случае работают вместе. Может, кто-нибудь мне поможет? Спасибо.
23 ответов
на самом деле раздел, откуда вы взяли этот код представлено объяснение, а также:
для перемещения N дисков из колышка A в колышек C:
- переместить N-1 дисков из A В B. Это оставляет диск #N один на привязке a
- переместить диск #n из A в C
- переместите N-1 дисков из B В C, чтобы они сидели на диске #n
довольно ясно, что сначала вам нужно удалить n - 1 диски, чтобы получить доступ к nго. И что вы должны переместить их сначала на другой колышек, чем там, где вы хотите, чтобы полная башня появилась.
код в вашем посте имеет три аргумента, кроме количества дисков: a источник peg, a назначения колышек и временное peg на котором диски можно хранить между (где каждый диск с размером n - 1 подходит).
рекурсия происходит на самом деле дважды, там, один раз перед writeln
, после. Один перед writeln
будет двигаться n - 1 диски на временную привязку, используя привязку назначения в качестве временного хранилища (аргументы в рекурсивном вызове находятся в другом порядке). После этого оставшийся диск будет перемещен на привязку назначения, а затем вторая рекурсия принудит перемещение всей башни, переместив n - 1 башня от шпенька temp к шпеньку назначения, над диском n.
год назад у меня был курс функционального программирования и нарисовать эту иллюстрацию для алгоритма. надеюсь, это поможет!
(0) _|_ | |
__|__ | |
___|___ | |
____|____ ____|____ ____|____
(1.1) | | |
__|__ | |
___|___ _|_ |
____|____ ____|____ ____|____ (A -> B)
(1.2) | | |
| | |
___|___ _|_ __|__
____|____ ____|____ ____|____ (A -> C)
(1.3) | | |
| | _|_
___|___ | __|__
____|____ ____|____ ____|____ (B -> C)
(2.1) | | |
| | _|_
| ___|___ __|__
____|____ ____|____ ____|____ (A -> B)
(3.1) | | |
| | |
_|_ ___|___ __|__
____|____ ____|____ ____|____ (C -> A)
(3.2) | | |
| __|__ |
_|_ ___|___ |
____|____ ____|____ ____|____ (C -> B)
(3.3) | _|_ |
| __|__ |
| ___|___ |
____|____ ____|____ ____|____ (A -> B)
задача о 3 кольцах была разбита на 2 задачи о 2 кольцах (1.x и 3.x)
существует хорошее объяснение рекурсивной реализации Ханоя в http://www.cs.cmu.edu / ~cburch/survey/recurse/hanoiimpl.html.
резюме, если вы хотите переместить нижнюю пластину с палки A на палку B, вам сначала нужно переместить все меньшие пластины поверх нее от A до C. Второй рекурсивный вызов затем переместить пластины, которые вы переместили в C обратно на B после того, как ваш базовый корпус переместил одну большую пластину из A В B.
Я согласен, что это не сразу, когда вы впервые посмотрите на него, но это довольно просто, когда Вы дойдете до него.
базовый вариант: ваша башня имеет размер 1. Таким образом, вы можете сделать это за один ход, от источника непосредственно к dest.
рекурсивный случай: ваша башня имеет размер n > 1. Таким образом, вы перемещаете верхнюю башню размера n-1 на дополнительный колышек (by), перемещаете нижнюю "башню" размера 1 на целевой колышек и перемещаете верхнюю башню из by в dest.
так с простым случаем, вы есть башня высотой 2:
_|_ | |
__|__ | |
===== ===== =====
первый шаг: переместите верхнюю башню 2-1 (=1) на дополнительный колышек (средний, скажем).
| | |
__|__ _|_ |
===== ===== =====
далее: переместите Нижний диск в пункт назначения:
| | |
| _|_ __|__
===== ===== =====
и, наконец, переместить верхнюю башню (2-1)=1 в пункт назначения.
| | _|_
| | __|__
===== ===== =====
если вы думаете об этом, даже если башня была 3 или более, всегда будет пустой дополнительный колышек или колышек со всеми большими дисками для рекурсии, чтобы использовать, когда башни меняя вокруг.
предположим, что мы хотим переместить диск из A В C через B:
- переместите меньший диск в B.
- перенести другой диск в с.
- переместить B В C.
- перейти от A к B.
- переместить все из C в A.
Если вы повторите все вышеуказанные шаги, то диск перенесет.
Я чувствую боль!
хотя это старый пост, я думаю, что на самом деле нужно понять, это не "переместить это на то" подход, но что ответ включает в себя использование побочного эффекта рекурсии.
однако это учит читателя использовать результаты возвращенного результата в следующем рекурсивном вызове.
In Ханойская башня, ответ не в возвращенном результате как таковом, а в наблюдении за возвращенным результатом.
на магия возникает в упражнения rearrangment параметров функции.
да проблема действительно в трех частях:
- перемещение меньшей башни на запасной колышек
- перемещение последнего диска в пункт назначения
- перемещение оставшейся башни на запасной колышек к месту назначения вешалка.
В Программе:
(define (th n a b c)
(if (zero? n) 'done
(begin
(th (- n 1) a c b)
(display (list a c))
(newline)
(th (- n 1) b a c))))
(th 5 'source 'spare 'destination)
однако это отображение параметров функции, которая является решением проблемы, а самое главное понимание двойной древовидной структуре вызовов.
решение также передает силу proof by induction
и теплое сияние для всех программистов, которые боролись с обычными структурами управления.
Incidently, разрешить проблему вручную довольно удовлетворительный.
- подсчитайте количество дисков
- если даже, переместите первый диск на запасной колышек, сделайте следующий юридический шаг (не включая верхний диск). Затем переместите верхний диск на привязку назначения, сделайте следующий юридический ход(nittd). Затем переместите верхний диск на исходную привязку, сделайте следующий юридический ход(nittd)...
- если нечетно, переместите первый диск на привязку назначения, сделайте следующий юридический шаг (не включая верхний диск). Затем переместите верхний диск на запасной колышек, сделайте следующий законный ход (nittd). Затем переместите верхний диск на исходную привязку, сделайте следующий юридический ход(nittd)...
лучше всего делать, всегда держа верхний диск одной рукой и всегда двигая эту руку в одном направлении.
окончательное количество ходов для n
дисков 2^n - 1
на move n disc to destination
находится на середине процесса.
наконец, это смешно, как объяснением проблема к коллеге, вашим жене / мужу или даже собака (даже ее они не слушают) может цементировать просветление.
прочитав все эти объяснения, я решил взвесить метод, который мой профессор использовал для объяснения рекурсивного решения Ханойских башен. Вот алгоритм снова с n, представляющий количество колец, и A, B, C, представляющий колышки. Первый параметр функции-это количество колец, второй параметр представляет исходный колышек, третий-целевой колышек, а четвертый-запасной колышек.
procedure Hanoi(n, A, B, C);
if n == 1
move ring n from peg A to peg B
else
Hanoi(n-1, A, C, B);
move ring n-1 from A to C
Hanoi(n-1, C, B, A);
end;
меня учили в аспирантуре школа, чтобы никогда не стыдиться думать о малом. Итак, давайте рассмотрим этот алгоритм для n = 5. Вопрос, который нужно задать себе в первую очередь:Если я хочу переместить 5-е кольцо из A В B, где остальные 4 кольца? Если 5-е кольцо занимает peg A, и мы хотим переместить его на peg B, то другие 4 кольца могут быть только на peg C. В алгоритме выше функции Ханой (n-1, A, C, B) пытается переместить все эти 4 других кольца на колышек C, поэтому кольцо 5 сможет перейти от A к B. Следуя этому алгоритму, мы рассмотрим n = 4. Если кольцо 4 будет перемещено из A В C, где кольца 3 и меньше? Они могут быть только на колышке B. далее, для n = 3, Если кольцо 3 будет перемещено из A В B, где кольца 2 и 1? На колышке с, Конечно. Если вы продолжаете следовать этому шаблону, вы можете визуализировать, что делает рекурсивный алгоритм. Этот подход отличается от подхода новичка тем, что он смотрит на последний диск первым, а первый диск последним.
подумайте об этом как о стеке, диаметр дисков которого представлен целыми числами (4,3,2,1) Первый вызов рекурсии будет вызван 3 раза и, таким образом, заполнит стек времени выполнения следующим образом
- первый звонок : 1. Второй звонок : 2,1. и третий звонок: 3,2,1.
после окончания Первой рекурсии содержимое стека времени выполнения выводится на средний полюс от наибольшего диаметра до наименьшего (первый в последнем). Далее перемещается диск диаметром 4 к месту назначения.
второй вызов рекурсии такой же, как и первый, за исключением перемещения элементов от среднего полюса к месту назначения.
все просто. Предположим, вы хотите, чтобы перейти от A к C
Если есть только один диск, просто переместить его.
Если есть более одного диска, сделать
- переместить все диски (N-1 дисков), кроме нижнего от A до B
- переместить Нижний диск С A на C
- переместить N-1 дисков с первого шага от A до c
имейте в виду, что при перемещении N-1 дисков nth не будет проблемой вообще (как только он станет больше чем все остальные)
обратите внимание, что перемещение N-1 дисков повторяется по той же проблеме снова, пока n-1 = 1, в этом случае вы будете на первом if (где вы должны просто переместить его).
ответ на вопрос, как программа знает, что даже "src" для "aux", а нечетный "src" для " dst " для открытия хода лежит в программе. Если вы сломаете кулак с 4 дисками, то это выглядит так:
hanoi(4, "src", "aux", "dst");
if (disc > 0) {
hanoi(3, 'src', 'dst', 'aux');
if (disc > 0) {
hanoi(2, 'src', 'aux', 'dst');
if (disc > 0) {
hanoi(1, 'src', 'dst', 'aux');
if (disc > 0) {
hanoi(0, 'src', 'aux', 'dst');
END
document.writeln("Move disc" + 1 + "from" + Src + "to" + Aux);
hanoi(0, 'aux', 'src', 'dst');
END
}
также первый ход с 4-мя дисками (четными) переходит от Src к Aux.
Как предположили некоторые из наших друзей, я удалил предыдущие два ответа, и я консолидируюсь здесь.
Это дает вам ясное понимание.
каков общий алгоритм....
:solve(n,s,i,d) //solve n discs from s to d, s-source i-intermediate d-destination
{
if(n==0)return;
solve(n-1,s,d,i); // solve n-1 discs from s to i Note:recursive call, not just move
move from s to d; // after moving n-1 discs from s to d, a left disc in s is moved to d
solve(n-1,i,s,d); // we have left n-1 disc in 'i', so bringing it to from i to d (recursive call)
}
вот рабочий пример Нажмите здесь
public static void hanoi(int number, String source, String aux, String dest)
{
if (number == 1)
{
System.out.println(source + " - > "+dest);
}
else{
hanoi(number -1, source, dest, aux);
hanoi(1, source, aux, dest);
hanoi(number -1, aux, source, dest);
}
}
void TOH(int n, int a, int b){
/*Assuming a as source stack numbered as 1, b as spare stack numbered as 2 and c as target stack numbered as 3. So once we know values of a and b, we can determine c as there sum is a constant number (3+2+1=)6.
*/
int c = 6-a-b;
if(n==1){
cout<<"Move from "<<a<<" to "<<b<<"\n";
}
else{
// Move n-1 disks from 1st to 2nd stack. As we are not allowed to move more than one disks at a time, we do it by recursion. Breaking the problem into a simpler problem.
TOH(n-1, a, c);
// Move the last alone disk from 1st to 3rd stack.
TOH(1, a, b);
// Put n-1 disks from 2nd to 3rd stack. As we are not allowed to move more than one disks at a time, we do it by recursion. Breaking the problem into a simpler problem.
TOH(n-1, c, b);
}
}
int main() {
TOH(2, 1, 3);
cout<<"FINISHED \n";
TOH(3, 1, 3);
cout<<"FINISHED \n";
TOH(4, 1, 3);
return 0;
}
есть три башни, а именно исходная башня, башня назначения и вспомогательная башня. В исходной башне есть все диски, и ваша цель-переместить все диски в целевую башню и убедиться, что при этом вы никогда не помещаете больший диск поверх меньшего диска. Мы можем решить эту проблему, используя рекурсию в шагах ниже:
у нас есть n количество дисков на исходной башне
базовый регистр: n=1 Если в источнике есть только один диск башня, переместить его в пункт назначения башни.
рекурсивный случай: n >1
- переместите верхние N-1 диски из исходной башни в вспомогательную башню
- переместить единственный оставшийся, N-й диск (после шага 1) в пункт назначения башня!--17-->
- переместите N-1 диски, которые находятся в helper tower сейчас, в пункт назначения
башня, используя source tower в качестве помощника.
исходный код на Java:
private void towersOfHanoi(int n, char source, char destination, char helper) {
//Base case, If there is only one disk move it direct from source to destination
if(n==1){
System.out.println("Move from "+source+" to "+destination);
}
else{
//Step1: Move the top n-1 disks from source to helper
towersOfHanoi(n-1, source, helper, destination);
//Step2: Move the nth disk from source to destination
System.out.println("Move from "+source+" to "+destination);
/*Step3: Move the n-1 disks(those you moved from source to helper in step1)
* from helper to destination, using source(empty after step2) as helper
*/
towersOfHanoi(n-1, helper, destination, source);
}
}
первый рекурсивный вызов перемещает все части, кроме самой большой из источника, используя dest в качестве вспомогательной кучи. Когда все части, кроме самого большого, будут лежать, и самый большой из них свободен. Теперь вы можете переместить самый большой в dest и использовать другой рекурсивный вызов, чтобы переместить все части из by в dest.
рекурсивные вызовы ничего не будут знать о самой большой части (т. е. они будут игнорировать ее), но это нормально, потому что рекурсивные вызовы будут иметь дело только с с частями которые более небольшие и таким образом можно двинуть на и с самой большой части свободно.
будучи студентом CS, вы, возможно, слышали о математической индукции. Рекурсивное решение башни Ханоя работает аналогично - только другая часть действительно не теряется с B и C, как и полная башня заканчивается.
в простом смысле идея состоит в том, чтобы заполнить другую башню среди трех определенных башен в том же порядке дисков, что и в настоящее время, без большого диска, перекрывающего маленький диск в любое время во время процедуры.
пусть' A',' B 'и' C ' - три башни. "A" будет башней, содержащей " N " дисков изначально. "B" может использоваться как промежуточная башня, А " C " - целевая башня.
algo выглядит следующим образом:
- перемещение N-1 дисков из tower 'A' в ' B ' с помощью 'C'
- переместить диск из " A " в "C"
- переместить N-1 дисков из башни " B " В " C "с помощью "A"
код выглядит следующим образом в Java:
общественный класс TowerOfHanoi {
public void TOH(int n, int A , int B , int C){
if (n>0){
TOH(n-1,A,C,B);
System.out.println("Move a disk from tower "+A +" to tower " + C);
TOH(n-1,B,A,C);
}
}
public static void main(String[] args) {
new TowerOfHanoi().TOH(3, 1, 2, 3);
}
}
вот объяснение. Посмотрите на картинку ->
по телефону Movetower(3,a,b,c)
, вы намерены переместить все 3 диска из tower A
башня B
. Таким образом, последовательные вызовы ->
1. Movetower(3,a,b,c) // No Move needed
2. Movetower(2,a,c,b) // No move needed
3. Movetower(1,a,b,c) // Here is the time to move, move disc1 from a to b
4. Movetower(2,a,c,b) // Returning to this call again, this is the time to move disc2 from a to c
5. Movetower(1,b,c,a) // Again the time to move, this time disc1 from b to c
6. Movetower(3,a,b,c) // Returning to this call again, this is the time to move disc3 from a to b
7. Movetower(2,c,b,a) // Not the time to move
8. Movetower(1,c,a,b) // Here is the time to move, move disc1 from c to a
9. Movetower(2,c,b,a) // Returning to this call again, this is the time to move disc2 from c to b
10.Movetower(1,c,a,b) // Here is the time to move, move disc1 from a to b
надеюсь, что это помогает :)
Для Анимации:https://www.cs.cmu.edu / ~cburch/survey/recurse/hanoiex.html
вот мой код решения проблемы башен Ханоя с использованием рекурсии с golang. основной пакет
import "fmt"
func main() {
toi(4, "src", "dest", "swap")
}
func toi(n int, from, to, swap string) {
if n == 0 {
return
}
if n == 1 {
fmt.Printf("mov %v %v -> %v\n", n, from, to)
return
}
toi(n-1, from, swap, to)
fmt.Printf("mov %v %v -> %v\n", n, from, to)
toi(n-1, swap, to, from)
}`
Tower (N,source,aux.dest):
-
if N =1 Then Write : Source -> dest return end of if
-
переместить N-1 диск из источника peg в peg aux
call Tower (N-1, source, dest, aux)
- написать Источник - > dest
-
перемещение N-1 дисков из peg aux в peg dest
call Tower (N-1, source, dest, aux)
- возвращение
/** * */ пакет com.тест.рекурсия;
/** * @author kamals1986 рекурсивный алгоритм для задачи Ханойской башни * алгоритм растет по мощности(2, n). */ общественный класс TowerOfHanoi {
private static String SOURCE_PEG = "B";
private static String SPARE_PEG = "C";
private static String TARGET_PEG = "A";
public void listSteps(int n, String source, String target, String spare) {
if (n == 1) {
System.out.println("Please move from Peg " + source + "\tTo Peg\t"
+ target);
} else {
listSteps(n - 1, source, spare, target);
listSteps(1, source, target, spare);
listSteps(n - 1, spare, target, source);
}
}
public static void main(String[] args) {
long startTime = System.currentTimeMillis();
new TowerOfHanoi().listSteps(18, SOURCE_PEG, TARGET_PEG, SPARE_PEG);
long endTime = System.currentTimeMillis();
System.out.println("Done in " + (endTime - startTime) / 1000
+ "\t seconds");
}
}
def Hanoi(n, A, B, C):
if(n==1): print "move plates to empty peg"
else:
Hanoi(n-1, A, B, C)
print "move"+str(n)+" to peg "+C
Hanoi(n-1, B, C, A)
Я тоже пытаюсь получить рекурсию.
Я нашел способ, я думаю,
Я думаю об этом как о цепочке шагов (Шаг не является постоянным, он может изменяться в зависимости от предыдущего узла)
Я должен выяснить 2 вещи:
- предыдущий узел
- шаг вроде
- после шага, что еще перед вызовом (это аргумент для следующего звоните
пример
факторный
1,2,6,24,120 ......... или
1,2*(1),3*(2*1),4*(3*2*1,5*(4*3*2*1)
step=несколько по последнему узлу
после шага, что мне нужно,чтобы добраться до следующего узла, аннотация 1
ok
function =
n*f(n-1)
its 2 steps process
from a-->to step--->b
Я надеялся на эту помощь, просто подумайте о 2 thniks, не как добраться от узла к узлу, но узел-->шаг-->узел
узел-->шаг является телом функция step-->node-это аргументы другой функции
bye:) надеюсь, я помог