Pomoc - Szukaj - Użytkownicy - Kalendarz
Pełna wersja: Wieże Hanoi
Forum PHP.pl > Forum > Gotowe rozwiązania > Algorytmy, klasy, funkcje
dr_bonzo
Co to robi? Rozwiazuje "Wieze Hanoi", uzywa minimalnej liczby ruchow i pokazuje je kolejno.

PHP5 REQUIRED

  1. <pre><?php
  2. class HanoiStack
  3. {
  4.     private $arrStack = array();
  5.     
  6.     public function HanoiStack()
  7.     {
  8.     }
  9.     
  10.     public function pushItem( $intItem )
  11.     {
  12.         $intItem = intval( $intItem );
  13.         
  14.         if ( count( $this->arrStack ) === 0 )
  15.         {
  16.             array_push( $this->arrStack, $intItem );
  17.         }
  18.         else
  19.         {
  20.             if ( $intItem > $this->getLastItem() )
  21.             {
  22.                 throw new Exception( 'Cannot put bigger brick on smaller (' . $intItem . ' on ' . $this->getLastItem() . ')' );
  23.             }
  24.             
  25.             array_push( $this->arrStack, $intItem );
  26.         }
  27.     }
  28.     
  29.     public function popItem()
  30.     {
  31.         if ( count( $this->arrStack ) === 0 )
  32.         {
  33.             print_r( $this->arrStack );
  34.             throw new Exception( 'Cannot pop intem from empty stack' );
  35.         }
  36.         
  37.         return array_pop( $this->arrStack );
  38.     }
  39.     
  40.     public function getLastItem()
  41.     {
  42.         if ( count( $this->arrStack ) === 0 )
  43.         {
  44.             return -1;
  45.         }
  46.         
  47.         return $this->arrStack[ count( $this->arrStack ) - 1 ];
  48.     }
  49.     
  50.     public function __toString()
  51.     {
  52.         $s = '[ ';
  53.         
  54.         foreach ( $this->arrStack as $v )
  55.         {
  56.             $s .= $v . ' ';
  57.         }
  58.         
  59.         $s .= &#092;"]n\";
  60.         return $s;
  61.     }
  62. }
  63.  
  64. class Hanoi
  65. {
  66.     private $arrStacks = array();
  67.     
  68.     private $intN;
  69.     
  70.     private $intMoves;
  71.     
  72.     public function Hanoi( $intN )
  73.     {
  74.         $this->intN = intval( $intN );
  75.         
  76.         for ( $i = 0; $i < 3; $i++ )
  77.         {
  78.             $this->arrStacks[ $i ] = new HanoiStack();
  79.         }
  80.         
  81.         for ( $i = $this->intN - 1; $i >= 0; $i-- )
  82.         {
  83.             $this->arrStacks[ 0 ]->pushItem( $i ); 
  84.         }
  85.     }
  86.     
  87.     public function solve()
  88.     {
  89.         $this->printTowers();
  90.         
  91.         // z wiezy 0 na wieze 2
  92.         if ( $this->intN <= 0 )
  93.         {
  94.             throw new Exception( 'Wrong intN' );
  95.         }
  96.         
  97.         $i_max = intval( pow( 2, $this->intN ) - 1 );
  98.         
  99.         $intBrickOnePosition = 0;
  100.         $intMoveDirection = ( $this->intN % 2 === 1 ) ? -: 1;
  101.         
  102.         for ( $i = 0; $i < $i_max; $i++ )
  103.         {
  104.             if ( $i % 2 === 0 )
  105.             {
  106.                 // zdejmij jedynke (tu zero: indeksujemy klocki od zera)
  107.                 $intItem = $this->arrStacks[ $intBrickOnePosition ]->popItem();
  108.                 print( '(' . $intBrickOnePosition . ')--[ ' . $intItem . ' ]-->(' . ( $intBrickOnePosition + $intMoveDirection + 3 ) % 3 . ')' . &#092;"n\" );
  109.                 $intBrickOnePosition = ( $intBrickOnePosition + $intMoveDirection + 3 ) % 3;
  110.                 // i przenies ja w keirunku $intMoveDirection
  111.                 $this->arrStacks[ $intBrickOnePosition ]->pushItem( $intItem );
  112.             }
  113.             else
  114.             {
  115.                 $arrTowerIndices = array();
  116.                 $arrTopItems = array();
  117.                 $x = 0;
  118.                 
  119.                 
  120.                 // znajdz wieze z min i max brickiem !== 1
  121.                 for ( $j = 0; $j < 3; $j++ )
  122.                 {
  123.                     if ( $j !== $intBrickOnePosition )
  124.                     {
  125.                         $arrTowerIndices[ $x ] = $j;
  126.                         $arrTopItems[ $x ] = $this->arrStacks[ $j ]->getLastItem();
  127.                         $x++;
  128.                     }
  129.                 }
  130.                 
  131.                 $intTowerWithSmallerTopItem = ( $arrTopItems[ 0 ] < $arrTopItems[ 1 ] ) ? $arrTowerIndices[ 0 ] : $arrTowerIndices[ 1 ];
  132.                 $intTowerWithBiggerTopItem = ( $arrTopItems[ 0 ] > $arrTopItems[ 1 ] ) ? $arrTowerIndices[ 0 ] : $arrTowerIndices[ 1 ];
  133.                 
  134.                 if ( $this->arrStacks[ $intTowerWithSmallerTopItem ]->getLastItem() === -1 )
  135.                 {
  136.                     // zamien je to ta pierwsza jest pusta
  137.                     $temp = $intTowerWithSmallerTopItem;
  138.                     $intTowerWithSmallerTopItem = $intTowerWithBiggerTopItem;
  139.                     $intTowerWithBiggerTopItem = $temp;
  140.                 }
  141.                 
  142.                 // przenies klocek
  143.                 $intItem = $this->arrStacks[ $intTowerWithSmallerTopItem ]->popItem();
  144.                 print( '(' . $intTowerWithSmallerTopItem . ')--[ ' . $intItem . ' ]-->(' . $intTowerWithBiggerTopItem . ')' . &#092;"n\" );
  145.                 $this->arrStacks[ $intTowerWithBiggerTopItem ]->pushItem( $intItem );
  146.                 
  147.                 
  148.             }
  149.             
  150.             $this->printTowers();
  151.         }
  152.     }
  153.     
  154.     private function printTowers()
  155.     {
  156.         print( '0: ' );
  157.         print( $this->arrStacks[ 0 ] );
  158.         print( '1: ' );
  159.         print( $this->arrStacks[ 1 ] );
  160.         print( '2: ' );
  161.         print( $this->arrStacks[ 2 ] );
  162.         print( &#092;"n\" );
  163.     }
  164. }
  165.  
  166.  
  167. class HanoiTowers
  168. {
  169.     public static function main()
  170.     {
  171.         try
  172.         {
  173.             $objHanoiTower = new Hanoi( 3 );
  174.             $objHanoiTower->solve();
  175.         }
  176.         catch ( Exception $e )
  177.         {
  178.             print( $e );
  179.         }
  180.     }
  181. }
  182.  
  183. HanoiTowers::main();
  184. ?></pre>
GrayHat
mozna jakis exampel?
dr_bonzo
Ehhh... a chociarz uruchomiles ten skrypt?
Wystarczy go skopiowac i uruchomic -- wyswietli ci kolejne ruchy i kolejne stany wiez.
GrayHat
a wogole co to wierze hanoi??
dr_bonzo
WieZe -- google wiedzialo ale nie powiedzialo?
http://en.wikipedia.org/wiki/Tower_of_Hanoi
Bielo
Sam algorytm rozwiązywania Hanoi wygląda tak:
  1. <?php
  2.  
  3. function hanoi($dyskow, $z, $na)//hanoi(ilosc dyskow do przelozenia, slupek na ktorym zaczynamy, slupek na ktory pr
    z
  4. nosimy)
  5. {
  6. if($dyskow==1)
  7. {
  8. print(&#092;"Przesun dysk nr \".$dyskow.\" z \".$z.\" na \".$na.\"<br />\");
  9. }
  10. else
  11. {
  12. hanoi($dyskow-1, $z, 3-$z-$na);
  13. print(&#092;"Przesun dysk nr \".$dyskow.\" z \".$z.\" na \".$na.\"<br />\");
  14. hanoi($dyskow-1, 3-$z-$na, $na);
  15. }
  16. }
  17.  
  18. ?>

Przyklad pzepisany z ksiazki Helionu "Algorytmy, struktury danych i techniki programowania", sprawdzalem pieknie dziala.

Nie przygladalem sie za bardzo Twojemu skryptowi, ale mysle ze mozna bylo zrobic to krocej
dr_bonzo
Twoj algorytm jest rekurencyjny, a moj nie. Oba opisane sa w wikipedii:

Cytat
Alternately move disc 1 and one of the larger discs. If two of the larger discs are available, always move the smaller one onto the larger. When you move an odd-numbered disc, always move it one peg clockwise; when you move an even-numbered disc, always move it one peg counterclockwise.

An even easier way to remember this solution is to notice that the smallest disk gets every second move, and always moves in the same direction. In between smallest disk moves, there is only one legal move that does does not involve moving the smallest disk again.

----

Cytat
Nie przygladalem sie za bardzo Twojemu skryptowi, ale mysle ze mozna bylo zrobic to krocej

No pewnie, w ogole porzucic klasy, obsluge bledow, skrocic printTowers(), itd.
Bielo
to co ja napisalem to jest tylko algorytm, a nie caly skrypt. Po dodaniu do niego obslugi bledow i przerobieniu na wersje obiektowa, bylby chyba krotszy od Twojego, ale rozumiem tez uzycie wersji nerekurencyjnej
Radarek
A mi nawet nie chce sie czytac i probowac zrozumiec wersje iteracyjna, bo po co? Wersja rekurencyjna jest tak intuicyjna i zrozumiala, ze nie trzeba nic wiecej. To niesamowite, ze te kilka linijek jest poprawnym rozwiazaniem [i najoptymalniejszym] podanego problemu. Ale braw nie bije bo znalem juz wczesniej to rozwiazanie winksmiley.jpg
M4chu
Najoptymalniejszym? Dla mnie najoptymalniejsze to najszybsze, a rekurencyjne takie napewno nie jest.
dr_bonzo
Cytat
Ale braw nie bije bo znalem juz wczesniej to rozwiazanie

Prawda jest taka ze nudzilo mi sie tamtej nocy i napisalem ten algorytm.
A wpadlem na niego (rozwiazujac na papierze dziesiatki razy WH) dawno temu.

Cytat
Wersja rekurencyjna jest tak intuicyjna i zrozumiala

A sprobuj go zastosowac w praktyce w realu...
Bakus
@Bielo: Jeżeli masz pomysł, to go rozwiń, napisz skrypt i umieść w osobnym temacie.
Radarek
Cytat
Najoptymalniejszym? Dla mnie najoptymalniejsze to najszybsze, a rekurencyjne takie napewno nie jest.


Dlaczego nie?smile.gif Zapewniam cie ze jest. Powiedz jaki jest powod dla ktorego mialo byc nie byc?

Cytat
Prawda jest taka ze nudzilo mi sie tamtej nocy i napisalem ten algorytm.
A wpadlem na niego (rozwiazujac na papierze dziesiatki razy WH) dawno temu.


Przyznam ci sie, ze ja tez kiedys wpadlem na to rozwiazanie, ale mniejsza z tym.

Cytat
A sprobuj go zastosowac w praktyce w realu...


Hm.. jesli pisze program to po to zeby robil cos za mnie. Np. program sortujacy za pomoca quicksorta lub heapsorta. Sprobuj zrobic to w praktyce. Bez sensu gadanie... smile.gif
bela
Cytat(Radarek @ 2005-05-29 15:38:41)
Cytat

Najoptymalniejszym? Dla mnie najoptymalniejsze to najszybsze, a rekurencyjne takie napewno nie jest.


Dlaczego nie?smile.gif Zapewniam cie ze jest. Powiedz jaki jest powod dla ktorego mialo byc nie byc?

Udowodnij, że jest najoptymalniejsze winksmiley.jpg
Radarek
Cytat(bela_666 @ 2005-05-29 15:12:29)
Cytat(Radarek @ 2005-05-29 15:38:41)
Cytat

Najoptymalniejszym? Dla mnie najoptymalniejsze to najszybsze, a rekurencyjne takie napewno nie jest.


Dlaczego nie?smile.gif Zapewniam cie ze jest. Powiedz jaki jest powod dla ktorego mialo byc nie byc?

Udowodnij, że jest najoptymalniejsze winksmiley.jpg

Hm.. ilosc ruchow potrzebnych do przeniesienia N krazkow wynosci 2^(n-1) i taka jest zlozonosc programu. Szybciej sie nie da. Zakladajac, ze wersja iteracyjna wykonuje taka sama ilosc ruchow to i tak jest mniej optymalna. Czemu? Bo zawiera wiecej instrukcji smile.gif. Przepiszcie obra programy na C/C++, skompilujcie do kodu assemblerowego i porownajcie rozmiary kodu. Stawiam, ze mniejszy bedzie rekurencyjny.
dr_bonzo
Ale wez tez pod uwage wielokrotne wywolywanie funkcji, co takze zajmuje zasoby i cpu (trzeba utworzyc zmienne lokalne, odlozyc je na stos, wywolac funkcje, zdjac zmienne z powrotem, itd). Ale nie wiem, ktora z metod bedzie szybsza.
Radarek
Cytat(dr_bonzo @ 2005-05-29 16:50:27)
Ale wez tez pod uwage wielokrotne wywolywanie funkcji, co takze zajmuje zasoby i cpu (trzeba utworzyc zmienne lokalne, odlozyc je na stos, wywolac funkcje, zdjac zmienne z powrotem, itd). Ale nie wiem, ktora z metod bedzie szybsza.

Tak wzialem to pod uwage. Rekurencyjne wywolanie funkcji to odlozenie na stos zmiennych lokalnych funkcji oraz adresu powrotu. Nie wierzysz chyba zeby wersja rekurencyjna bedzie dluzsza? smile.gif
onlyX
A mi wyrzuciło taki błąd w tym pierszym skrypcie:
Kod
Parse error: parse error, expecting `T_OLD_FUNCTION' or `T_FUNCTION' or `T_VAR' or `'}'' in D:\usr\test\hanoi.php on line 4
dr_bonzo
Bo musisz miec PHP5.

PS. Uzupelniony 1szy post o to info
piratt
A wzial ktos pod uwage fakt ze w wersji rekurencyjnej skonczy sie baaardzo szybko pamiec? winksmiley.jpg Rekurencja nie jest zalecana przy duzej liczbie wywolan.
Bakus
to raczej nie stanowi problemu - w koncu po wywolaniu skryptu pamiec jest zwalniana...
piratt
Po wykonaniu. A pamiec sie skonczy w trakcie;) Zauwaz ze ilosc wywolac rekurencyjnych rosnie wykladniczo. Wiec przy odpowiedniej ilosci(zwlaszcza tej 'historycznej'-63krazki?) nie ma szans zeby starczylo pamieci, nawet liczac tylko po 4 bajty na jmp'y;)
To jest wersja lo-fi głównej zawartości. Aby zobaczyć pełną wersję z większą zawartością, obrazkami i formatowaniem proszę kliknij tutaj.
Invision Power Board © 2001-2025 Invision Power Services, Inc.