Ханойские башни. Переместить некоторое число упорядоченных по убыванию дисков разного размера, нанизанных на вертикальный стержень 1, с помощью стержня 2 на стержень 3. Диски перемещаются согласно двум простым правилам
Перекладывать диски можно только по одному
Нельзя класть большой диск на меньший.
Эта головоломка известна давно. В русской литературе она впервые появилась в 1902 году в книге Е.Игнатьева "В царстве смекалки", а автором принято считать французского математика Э. Люка, создавшего ее на основе древних легенд. Вот одна из них. На стержне надето 64 золотых диска, и буддийские монахи с момента сотворения мира, без устали сменяя друг друга, день и ночь переносят их с одного стержня на другой. Когда все диски будут перенесены, наступит конец света.
Доказано, что оптимальное количество перекладываний 2^n-1, где число дисков равно n. Поэтому монахам понадобится 2^64-1 действие, что бы выполнить свою работу. Если тратить на каждое действие по секунде, то пройдет 18 446 744 073 709 551 615 секунд, т.е. более 500 миллиардов лет.
Если бы в пирамиде был только один диск, решение очевидно - перенесем его на третий стержень, и пирамида собранна. А если дисков два ? Тогда положим сначала меньший диск на второй стержень, затем перенесем второй диск на третий стержень, а за ним и третий. За три действия мы смогли переложить оба диска на третий. Назовем стержни соответственно исходный, вспомогательный, и конечный. Так как задачу можно решить для n-1 дисков, то ее можно решить и для n дисков, т.е. рекурсия очевидна, а ключ к решению лежит в решении задачи всего лишь для двух дисков, которое состоит из 3 основных шагов, описанных выше: 1. перенести башню из n-1 дисков с исходного на вспомогательный. 2. переложить n-ый диск с исходного на конечный. 3. перенести башню из n-1 дисков со вспомогательного на конечный.
Разбирая алгоритм и программу, важно понять "изменение" нумерации стержней.
uses crt; const n=4; {число колец} procedure tower (n:byte; init, aux,fin:char); begin if n=1 then write (init, #26, fin, ' ') else begin tower (n-1, init, fin, aux ); write (init, #26, fin, ' '); tower (n-1, aux, init, fin); end end;
begin tower (n,'1','2','3'); readkey; end.
Нерекурсивное решение было придуманно только в 1980 году. Оно состояло из чередования перемещений двух видов:
Перенести наименьший диск с того стержня, на котором он находиться в данный момент, на стержень, следующий в порядке движения часовой стрелки.
Перенести любой диск, кроме наименьшего. Второй шаг не произвольный, т.е. всегда найдется лишь одно перемещение.
Этот алгоритм медленнее рекурсивного.
А вот, программа, визуализирующая решение задачи. (TP7, BP7, текст предоставила http://forum.pascalnet.ru/index.php?showuser=233) текст:hanoy.pas ( 7.33 килобайт )
Кол-во скачиваний: 1690
gotoxy(25,2); textcolor(red); write('HANOI TOWERS'); gotoxy(1,4); textcolor(white); writeln(' There are 3 pegs named A,B,C. One peg is source peg,'); writeln('one is destination peg, the other peg is temporary peg.');writeln; writeln(' There are N discs. The smallest disc is disc1 and the '); writeln('largest disc is disc N. There can be maximum 20 discs.');textcolor(red);writeln; write(' GOAL : ');textcolor(white); writeln('All discs will be carried from source peg into'); writeln(' the destination peg.'); writeln;textcolor(red); write(' RULES : 1) ');textcolor(white); writeln('Only one disc can be moved at a time, and'); writeln(' this disc must be the top disc on a peg.');textcolor(red);writeln; write(' 2) ');textcolor(white); writeln('A larger disc can never be placed on top'); writeln(' of a smaller disc.');writeln; writeln(' IT IS NOT A RECURSIVE PROGRAM');writeln; writeln(' Press any key to continue'); readkey;clrscr; repeat write('Enter the N number : ');readln(n); if (n=0)or(n=1)or(n>20) then clrscr; until (n<>0)and(n<>1)and(n<=20);
write('Enter the source peg(A,B,C) : ');readln(source); source:=upcase(source); repeat write('Enter the destination peg : ');readln(dest); source:=upcase(source); dest:=upcase(dest); if dest=source then begin gotoxy(28,3); clreol; gotoxy(1,3);end; until dest<>source; detectgraph(gd,gm); initgraph(gd,gm,''); cleardevice; setbkcolor(7); end; {----------------------------------------------------------} Procedure Push(item:integer; var head:nodeptr); begin new(yeni); yeni^.no:=item; yeni^.next:=head; head:=yeni; end; {-----------------------------------------------------------} Function Pop(var head:nodeptr):integer; var temp:nodeptr; begin temp:=head; head:=head^.next; pop:=temp^.no; dispose(temp); end; {-----------------------------------------------------------} Function usalma(a:integer):integer; var i,s:integer; begin s:=1; if a=1 then s:=2 else for i:=1 to n do s:=s*2; usalma:=s-1; end; {-----------------------------------------------------------} Function tekmi(a:integer):byte; begin if a mod 2=1 then tekmi:=1 else tekmi:=2; end; {-----------------------------------------------------------} Procedure ciz(s1,s2,s3:aray20; sa1,sa2,sa3:byte); var i,m1,m2,m3:byte; begin setfillstyle(1,red); m1:=0; m2:=0; m3:=0; setcolor(1); outtextxy(95,325,'A'); outtextxy(295,325,'B'); outtextxy(495,325,'C'); setcolor(red); outtextxy(295,370,'Press any key to continue'); setcolor(yellow); for i:=sa1-1 downto 1 do begin m1:=m1+1; str(s1[i],cevir); bar(85-4*s1[i],313-13*m1,109+4*s1[i],323-13*m1); outtextxy(95,313-13*m1,cevir); end;
for i:=sa2-1 downto 1 do begin m2:=m2+1; str(s2[i],cevir); bar(85-4*s2[i]+200,313-13*m2,309+4*s2[i],323-13*m2); outtextxy(295,313-13*m2,cevir); end;
for i:=sa3-1 downto 1 do begin m3:=m3+1; str(s3[i],cevir); bar(85-4*s3[i]+400,313-13*m3,509+4*s3[i],323-13*m3); outtextxy(495,313-13*m3,cevir); end; setcolor(red); end; {----------------------------------------------------------} Procedure yazdir; var i:byte; begin cleardevice; sayac1:=1; sayac2:=1; sayac3:=1; temp1:=top1; temp2:=top2; temp3:=top3;
while temp1<>nil do begin uzun1[sayac1]:=temp1^.no; temp1:=temp1^.next; sayac1:=sayac1+1; end;
while temp2<>nil do begin uzun2[sayac2]:=temp2^.no; temp2:=temp2^.next; sayac2:=sayac2+1; end;
while temp3<>nil do begin uzun3[sayac3]:=temp3^.no; temp3:=temp3^.next; sayac3:=sayac3+1; end;
if (source='A')and(dest='C') then ciz(uzun1,uzun2,uzun3,sayac1,sayac2,sayac3); if (source='A')and(dest='B') then ciz(uzun1,uzun3,uzun2,sayac1,sayac3,sayac2); if (source='B')and(dest='C') then ciz(uzun2,uzun1,uzun3,sayac2,sayac1,sayac3); if (source='B')and(dest='A') then ciz(uzun3,uzun1,uzun2,sayac3,sayac1,sayac2); if (source='C')and(dest='A') then ciz(uzun3,uzun2,uzun1,sayac3,sayac2,sayac1); if (source='C')and(dest='B') then ciz(uzun2,uzun3,uzun1,sayac2,sayac3,sayac1); end; {-----------------------------------------------------------} begin initialize;
for i:=n downto 1 do push(i,top1); yazdir; readkey;
if tekmi(n)=1 then begin if tekmi(top1^.no)=1 then push(pop(top1),top3) else push(pop(top1),top2); end else begin if tekmi(top1^.no)=1 then push(pop(top1),top2) else push(pop(top1),top3); end;
son:=1; yazdir; outtextxy(20,370,'Number of move : 1'); readkey; deger:=usalma(n);
if tekmi(n)=1 then begin
repeat
if (top1<>nil)and(top1^.no<>son) then begin if (tekmi(top1^.no)=1)and(top1^.no<top3^.no) then begin son:=top1^.no; str(son,cevir); Push(pop(top1),top3); control:=10; yazdir; end; if (tekmi(top1^.no)=2)and(top1^.no<top2^.no)and(control=0) then begin son:=top1^.no; str(son,cevir); Push(pop(top1),top2); control:=10; yazdir; end; end;
if (top2<>nil)and(top2^.no<>son)and(control=0) then begin if (tekmi(top2^.no)=1)and(top2^.no<top1^.no) then begin son:=top2^.no; str(son,cevir); Push(pop(top2),top1); control:=10; yazdir; end; if (tekmi(top2^.no)=2)and(top2^.no<top3^.no)and(control=0) then begin son:=top2^.no; str(son,cevir); Push(pop(top2),top3); control:=10; yazdir; end; end;
if (top3<>nil)and(top3^.no<>son)and(control=0) then begin if (tekmi(top3^.no)=1)and(top3^.no<top2^.no) then begin son:=top3^.no; str(son,cevir); Push(pop(top3),top2); control:=10; yazdir; end; if (tekmi(top3^.no)=2)and(top3^.no<top1^.no)and(control=0) then begin son:=top3^.no; str(son,cevir); Push(pop(top3),top1); control:=10; yazdir; end; end; control:=0; counter:=counter+1; str(counter,cevir); outtextxy(20,370,'Number of move :');outtextxy(150,370,cevir); readkey; until deger=counter; end;
if tekmi(n)=2 then begin repeat if (top1<>nil)and(top1^.no<>son) then begin if (tekmi(top1^.no)=1)and(top1^.no<top2^.no) then begin son:=top1^.no; str(son,cevir); Push(pop(top1),top2); control:=10; yazdir; end; if (tekmi(top1^.no)=2)and(top1^.no<top3^.no)and(control=0) then begin son:=top1^.no; str(son,cevir); Push(pop(top1),top3); control:=10; yazdir; end; end;
if (top2<>nil)and(top2^.no<>son)and(control=0) then begin if (tekmi(top2^.no)=1)and(top2^.no<top3^.no) then begin son:=top2^.no; str(son,cevir); Push(pop(top2),top3); control:=10; yazdir; end; if (tekmi(top2^.no)=2)and(top2^.no<top1^.no)and(control=0) then begin son:=top2^.no; str(son,cevir); Push(pop(top2),top1); control:=10; yazdir; end; end;
if (top3<>nil)and(top3^.no<>son)and(control=0) then begin if (tekmi(top3^.no)=1)and(top3^.no<top1^.no) then begin son:=top3^.no; str(son,cevir); Push(pop(top3),top1); control:=10; yazdir; end; if (tekmi(top3^.no)=2)and(top3^.no<top2^.no)and(control=0) then begin son:=top3^.no; str(son,cevir); Push(pop(top3),top2); control:=10; yazdir; end; end; control:=0; counter:=counter+1; str(counter,cevir); outtextxy(20,350,'Number of move :');outtextxy(150,350,cevir); readkey; until deger=counter; end; end.
Вот скриншот работы программы:
А вот один из нерекурсивных алгоритмов (автор http://forum.pascalnet.ru/index.php?showuser=69)
program HanoyTower; type TInt=longint; procedure Tower(n:TInt); var m,k,l,s:TInt; iz,v,c,x:byte; begin n:=longint(round(exp(n*ln(2))))-1; for m:=1 to n do begin iz:=1; c:=2; v:=3; k:=1; l:=n; s:=(k+l) div 2; repeat if m<s then begin x:=v; v:=c; c:=x; l:=s-1; end; if m>s then begin x:=iz; iz:=c; c:=x; k:=s+1; end; if m=s then begin writeln(iz,'-',v); break; end; s:=(k+l) div 2; until 2*2=5; end; end;
var n:TInt; begin n:=4; Tower(n); readln; end.
И на последок, еще одна программа (автор http://forum.pascalnet.ru/index.php?showuser=1235) визуализирующая решение.
program Hanoy; uses Crt,Graph; type ArrType =array [1..1] of byte; var Col, Beg,End_ :byte; Arr :^ArrType; i,j :integer;