C言語によるハノイの塔のプログラムの実行の流れ

このQ&Aのポイント
  • C言語でのハノイの塔のプログラムの実行を学びます。
  • ハノイの塔のプログラムのソースコードと実行結果を紹介します。
  • プログラムの実行の流れについて詳しく解説します。
回答を見る
  • ベストアンサー

C言語によるハノイの塔のプログラムの実行の流れ

解きながら学ぶC言語の問題8-9のハノイの塔のプログラムについです。ソースコードは #include<stdio.h> #define N 3 void move(int no,int x,int y) { if(no>1) move(no-1,x,6-x-y); printf("%dを%d軸から%d軸へ移動\n",no,x,y); if(no>1) move(no-1,6-x-y,y); } int main(void) { move(N,1,3); return 0; } ============================== 実行結果 1を1軸から3軸へ移動 2を1軸から2軸へ移動 1を3軸から2軸へ移動 3を1軸から3軸へ移動 1を2軸から1軸へ移動 2を2軸から3軸へ移動 1を1軸から3軸へ移動 ============================== 最近大学の講義でこのプログラムの実行の流れを習ったのですが、どうにもよく理解することができません。 まず、move(no,x,y)にはそれぞれno=3,x=1,y=3の数が入っていますよね。 そして、no>1なので、move(no-1,6-x-y,y)の式に代入してmove(3-1,1,6-1-3)となるとno=2,x=1,y=2となるのでmove(2-1,1,6-1-2)となるのでこのときno=1,x=1,y=3。 この時点でno>1ではなくなりprintf関数によって、1を1軸から3軸へ移動、2を1軸から2軸へ移動と表示されると言うことだと思っているのですが。 ここから先がさっぱりです、なぜ1を3軸から2軸へ移動となるのでしょうか? 一つもどってno=2,x=1,y=2のときmove(no-1,6-x-y,y)の式に代入されるということなのでしょうか? どうして急にmove(no-1,x,6-x-y)の式で計算していたものがmove(no-1,6-x-y,y)の式で計算するようになるのか、がわかりません。 どちらも条件分岐はif(no>1)ですし、このif文が何のためにあるのかもよくわからないのです。 自分でも書いていて要点がはっきりしませんが、どなたかお願いいたします。

質問者が選んだベストアンサー

  • ベストアンサー
  • siffon9
  • ベストアンサー率64% (136/211)
回答No.3

move(no, x, y)の動作を言葉で書くと「x軸(以下from軸)にあるno枚の円盤を、y軸(以下to軸)へ移動させる」となります。関数の中の、「6-x-y」というのは、from軸でも、to軸でもない残りのもう一つの軸(以下other軸)番号を示します。 move関数の動作を説明すると以下の様になります。 まず、from軸にあるno枚の円盤を、「一番下の円盤」と「一番下を除く円盤の残りの円盤グループ」の2つに分けて考えます。そしてその2つに対して、動作は以下の3項目を順番に実行します。 1.if(no>1) move(no-1,x,6-x-y);   「一番下を除く円盤の残りの円盤グループ」をfrom軸から、other軸へ移動(move関数を再帰呼び出しして対応) 2.printf("%dを%d軸から%d軸へ移動\n",no,x,y);   "「一番下の円盤」をfrom軸から、to軸へ移動"を表示 3.if(no>1) move(no-1,6-x-y,y);   「一番下を除く円盤の残りの円盤グループ」をother軸から、to軸へ移動(move関数を再帰呼び出しして対応) 1と3に該当する部分で「if(no>1)」とあるのは、no=1(from軸の円盤が1枚)の場合は「一番下の円盤」のみ存在して「一番下を除く円盤の残りの円盤グループ」が無いので1と3を実行する必要が無いためです。 実際のプログラムでmainで呼ばれたmove(3, 1, 3) 「1軸の3枚の円盤を1軸から3軸へ移動」の動作を書くと以下の様になります。move関数の再帰呼び出し部分は、段付けをしました。段の深さが同じ部分が同じmove関数の動作を示しています。 頭の数字は前記move関数の動作の項目番号を示しています。 例えば「1-1」は、 「move(3, 1, 3)の1項目で呼び出されたmove関数の、更に1項目で呼び出されたmove関数」となります。 move(3, 1, 3) 「1軸の3枚の円盤を1軸から3軸へ移動」   1. move(2, 1, 2)…「1軸の上2枚の円盤を1軸から2軸へ移動」     1-1. move(1, 1, 3)…「1軸の上1枚の円盤を1軸から2軸へ移動」       no=1なので再帰呼び出しはしません       1-1-2. print "1を1(from)軸から3(to)軸へ移動"     1-2. print "2を1(from)軸から2(to)軸へ移動"     1-3. move(1, 3, 2)…「1軸の上1枚の円盤を3軸から1軸へ移動」       no=1なので再帰呼び出しはしません       1-3-2. print "1を3(from)軸から2(to)軸へ移動"   2. print "3を1(from)軸から3(to)軸へ移動"   3. move(2, 2, 3)…「2軸の上2枚の円盤を2軸から3軸へ移動」     3-1. move(1, 2, 1)…「1軸の上1枚の円盤を2軸から1軸へ移動」       no=1なので再帰呼び出しはしません       3-1-2. print "1を2(from)軸から1(to)軸へ移動"     3-2. print "2を2(from)軸から3(to)軸へ移動"     3-3. move(1, 1, 3)…「1軸の上1枚の円盤を1軸から3軸へ移動」       no=1なので再帰呼び出しはしません       3-3-2. print "1を1(from)軸から3(to)軸へ移動" move(3, 1, 3)は以上のような動作をしますので、上から順番にprintの行を拾い出せば実行結果と同じになるはずです。 ちょっと、ごちゃごちゃしましたがご理解の一助になれば幸いです。

umineko1
質問者

お礼

返事が遅れて申し訳ありません。 大変ご親切のお答えいただいてありがとうございます。 まさに求めていた答えでした。 どうにも試験に出そうだったので心配だったのですが、おかげさまで理解することができました。 有難うございました。

その他の回答 (3)

回答No.4

引数をもう一つ増やした、こういうプログラムの方がわかりやすい気はしますが。 #include <iostream> void move(int from, int to) { std::cout << "move a disk from " << from << " to " << to << "\n"; } void hanoi(int n, int a, int b, int c) { // n 枚の円盤を a から b に移動する。ワークとして、c を使う if (n > 0) { hanoi(n - 1, a, c, b); // n - 1 枚を、b をワークにして、c に運んで、 move(a, b); // 最後の1枚を b に運んで、 hanoi(n - 1, c, b, a); // c に運んだ n - 1 枚を a をワークに、b に移動 } } int main() { hanoi(3, 1, 3, 2); return 0; } No1. で引用されている、http://www13.plala.or.jp/kymats/study/C++/Hanoi/Hanoi.html もこの形からスタートしています。 いずれにしても、再帰呼び出しを使う場合、流れを追いかけるとわからなくなるケースが多いです。 少なくとも、なぜ再起を使うとありがたいのかという御利益がわからなくなるケースは多いです。 この場合、ハノイの塔を解くアルゴリズムがあったとすると、n段の円盤をある軸からある軸へと動かすことはできるわけです。 だったら、n - 1 段の円盤を動かすこともできます。 それなら、 ・(既にあるはずのアルゴリズムで)n - 1 枚の円盤を、目的ではない軸に動かして、 ・最後の円盤を目的の軸において、 ・目的でない軸にある n - 1 枚の円盤を、目的の軸に(既にあるはずのアルゴリズムで)もどす。 と考えればできるわけです。 じゃ、具体的にはどう動かせば良いの? というのを見かけ上考えずに「そのアルゴリズムは既にあるもの」という前提で問題が解決できてしまうと言うのが、再帰呼び出しの御利益です。 で、あきらかに、冒頭の処理の方が考えやすいかなと思いますが。 例題のソースについて言えば、少なくとも、6 - x - y はやめてほしいなと思います。 せめて、x でも y でもない軸 というコメントはほしいかなと。

umineko1
質問者

お礼

御回答ありがとうございます。 別の考え方を示していただきありがとうございました。 どうにも自分は再帰呼び出しと言うものを分かっていないようですね…。参考にさせていただきます。 有難うございました。

  • kmee
  • ベストアンサー率55% (1857/3366)
回答No.2

http://okwave.jp/qa/q6791162.html 少し前に答えたものです。

umineko1
質問者

お礼

御回答ありがとうございます。 なるほど、そのようなコードもあるのですか…。 参考にしたいと思います。ありがとうございました。

回答No.1

 こんばんは。 ハノイの塔を攻略せよ! http://www13.plala.or.jp/kymats/study/C++/Hanoi/Hanoi.html  状態を「イメージする」が重要ではないかと。  あと、ハノイの塔のルールを確認することですね。  このページの説明でなんとかなりませんかね。

umineko1
質問者

お礼

御回答ありがとうございます。 確かに、リンク先では図がふんだんに使われていてイメージがつかみやすそうですね…。 参考にしたいと思います。ありがとうございました。

関連するQ&A

  • ハノイの塔 関数内の進み方

    ■プログラムは後半に書いてます。よろしくお願いします。      no x y ――――――― 10行目 3 1 3 10行目 2 1 2 10行目 1 1 3 printf ■この後の5回(できれば10回)の進み方を教え貰えると嬉しいです。 ■わからない理由 printfのとき、(no, x, y) = (1, 1, 3)でno = 1。 そのため13行目は行われない。 しかし、returnもbreakもないためまた6行目に戻るのでしょうか?(答えを見ると6行目に行きそうです。) (no, x, y) はどのような値で関数が始まるのでしょうか? 9行目があるのにno=2or3で表示される理由がわからないです。 01|#include <stdio.h> 02|#include <conio.h> 03| 04|#define N 3 05| 06|void move(int no,int x,int y) 07|{ 08| static int i=1; 09| if(no>1) 10| move(no-1,x,6-x-y); 11| printf("%dを%d軸から%d軸へ移動 : %d\n",no,x,y,i++); 12| if(no>1) 13| move(no-1,6-x-y,y); 14|} 15| 16|int main(void) 17|{ 18| move(N,1,3); 19| 20| _getch(); 21| return(0); 22|}

  • プログラムC

    前にも質問したのですがヒルベルト曲線を用いて画像をスキャンするプログラムとビットマップの画像を読み込むプログラムを用いてお互いの座標を関連付けてヒルベルト曲線の座標にビットマップの1画素ずつの値を代入したいのですがどうしてもうまくいかなく質問しました。 前に質問した物はhttp://oshiete1.goo.ne.jp/kotaeru.php3?q=1106704 にあります。ヒルベルトは質問覧にビットマッピはNO2の補足にあります。プログラムを書くと800字を超えてしまうのでそのようにしました。 ヒルベルトは他に #include<stdio.h> int x,y; int n; void RUL(int n),DLU(int n),LDR(int n),URD(int n); main() { scanf("%d",&n); printf("#位相%dのヒルベルト曲線\n",n); x=0; y=0;printf("(%d %d) ",x,y); RUL(8); } void RUL(int n) { if(n<=0) {return;} URD(n-1);x=x+1;printf("(%d %d)",x,y); RUL(n-1);y=y+1;printf("(%d %d)",x,y); RUL(n-1);x=x-1;printf("(%d %d)",x,y); DLU(n-1); } void DLU(int n) { if(n<=0) {return;} LDR(n-1);y=y-1;printf("(%d %d)",x,y); DLU(n-1);x=x-1;printf("(%d %d)",x,y); DLU(n-1);y=y+1;printf("(%d %d)",x,y); RUL(n-1); } void LDR(int n) { if(n<=0) {return;} DLU(n-1);x=x-1;printf("(%d %d)",x,y); LDR(n-1);y=y-1;printf("(%d %d)",x,y); LDR(n-1);x=x+1;printf("(%d %d)",x,y); URD(n-1); } void URD(int n) { if(n<=0) {return;} RUL(n-1);y=y+1;printf("(%d %d)",x,y); URD(n-1);x=x+1;printf("(%d %d)",x,y); URD(n-1);y=y-1;printf("(%d %d)",x,y); LDR(n-1); } があります。

  • ヒルベルト曲線のプログラム(C言語)

    私は画像の勉強をしています。それで従来の水平スキャンではなく2次元相関をたもったまま1次元配列に変換できるヒルベルトスキャンとBWT変換とエントロピー符号化を用いて圧縮する方法を勉強しています。しかしヒルベルトのほうができなくて困っています。 プログラムの実行結果でヒルベルト曲線通る座標を示すプログラムを教えてください。だいたいは書けましたがうまくいきません。プログラムは #include<stdio.h> int x,y; main() { int n; void RUL(int n),DLU(int n),LDR(int n),URD(int,n); scanf("%d",&n); printf("#位相%dのヒルベルト曲線\n",n); x=0; y=0;printf("%d\n",x,y); RUL(n);printf("\n"); } void RUL(int n) { if(n<=0) {return;} URD(n-1);x=x+1;printf("%d %d\n",x,y); RUL(n-1);y=y+1;printf("%d %d\n",x,y); RUL(n-1);x=x-1;printf("%d %d\n",x,y); DLU(n-1); } void DLU(int n) { if(n<=0) {return;} LDR(n-1);y=y-1;printf("%d %d\n",x,y); DLU(n-1);x=x-1;printf("%d %d\n",x,y); DLU(n-1);y=y+1;printf("%d %d\n",x,y); RUL(n-1); } void LDR(int n) { if(n<=0) {return;} DLU(n-1);x=x-1;printf("%d %d\n",x,y); LDR(n-1);y=y-1;printf("%d %d\n",x,y); LDR(n-1);x=x+1;printf("%d %d\n",x,y); URD(n-1); } void URD(int n) { if(n<=0) {return;} RUL(n-1);y=y+1;printf("%d %d\n",x,y); URD(n-1);x=x+1;printf("%d %d\n",x,y); URD(n-1);y=y-1;printf("%d %d\n",x,y); LDR(n-1); } です。それをBWT変換とエントロピー符号にかけ圧縮させその圧縮率を求めその後画像はちゃんと戻るかを調べるために復元のプログラムを書かないいけませんがそのプログラムがわかりません。教えてください。

  • C言語 ハノイの塔

    #include<stdio.h> void hanoi(int n,char a,char b,char c); int main(void) { int n=3; printf("円板の枚数 ⇒ " ); hanoi(n,'a','b','c'); return 0; } void hanoi(int n,char a,char b,char c) { if(n > 0){ hanoi(n - 1,a,c,b); printf("%d番の板を %c から %c に移動\n",n,a,b); hanoi(n - 1,c,b,a); } } このときの再帰の処理がわかりません。。 再帰の間にprintfがあるのでどこがつながっているのか順番がわかりません。 具体的な数値の手順を教えてください。

  • C言語のプログラムについて

    三角形を判定するプログラムを作ったのですが直角三角形ができるはずがないのに直角三角形の判定が出てしまいます。簡単なことなのかもしれませんが自分ではわからなくなってしまったのでご指摘してもらいたいです。 #include<stdio.h> void tri(int x,int y,int z) { if((x*x==y*y+z*z)||(y*y==x*x+z*z)||(z*z==x*x+y*y)) { printf("これは直角三角形です。"); printf("これは三角形です。"); }else if((x+y>=z)||(x+z>=y)||(y+z>=x)) { printf("これは三角形ではありません。"); }else{ printf("これは三角形です。"); } } int main(void) { int e1,e2,e3; printf("3辺を入力してください"); scanf("%f,%f,%f",&e1,&e2,&e3); tri(e1,e2,e3); return(0); }

  • C言語のプログラムを見てください

    ある100行の値がx列、y列の2列あるファイルを読み込んでそれを配列に入れ、yの最小値及びそれと同じ行にあるxの値を表示するプログラムを書きたいのですがy列の最小値を表示するプログラムを書き終えた所でコンパイルして実行してみると正しく値が表示されませんでした。それどころか実行するたびに値が変わってしまいます。どこがおかしいのかわからないため、ご指摘のほどよろしくお願いします。また、できれば同じ行にあるx列の値も表示させるプログラムを教えてください。 よろしくお願いします。 #include <stdio.h> #include <stdlib.h> #define N 100 int main(void) { int x[N],i; double y[N],min; FILE *fp; fp=fopen("book.dat","r"); if(fp==NULL){ puts("can't open file!"); exit(-1); } for(i=0;i<N;i++){ fscanf(fp,"%d %lf", &x[N],&y[N]); printf("x=%d\n y=%lf\n",x[N],y[N]); } min=y[0]; for(i=1;i<N;i++){ if(y[i]<min) min=y[i]; } fclose(fp); printf("最小値:%lf\n",min); return 0; }

  • c言語 パスカルの三角形

    c言語でパスカルの三角形を出力するプログラムを作りたいのですが、上手くいきません。 何を直せばいいのか教えてください。 #include <stdio.h> #define N 10 int main(void){ int i, j = 1, x, y; int d[N][N]; /* 三角形を作成 */ for (i = 1 ; i < N ; i++){ d[i][0] = 1; while (j <= i - 1){ d[i][j] = d[i-1][j-1] + d[i-1][j]; j ++; } } /* 三角形の表示 */ for (y = 0; y < N; y++) { for (x = 0; x < N-y; x++) printf(" "); for (x = 0; x < y; x++) printf("%3d ", d[x][y]); printf("\n"); } return 0; } 実行結果 -2147417616 2665208 1629976532 1627572249 1629101723 1 1629982744 2665256 2665548 3407923 1629345053 1627571017 0 3538997 1629739051 10 1629345053 2665368 3670071 2665384 1629739040 1627927140 2665244 1628040295 57 1628810863 1629476960 1628602749 2665560 2665304 1629345053 0 1629739040 1629740576 1628992224 2 4411498 1628040588 -2147417600 0 1629476960 1629740664 1629739040 1 267574 0

  • javaハノイの塔について

    public class hanoinotou { static void move(int n,int a,int b , int c) { if(n>1) move(n-1,a,c,b); System.out.println("円盤"+n+":"+a+"→"+c); if(n>1) move(n-1,b,a,c);} public static void main(String args[]){ move(3,1,2,3); } } ↑このプログラムの動き方を教えてください よろしくお願いします

  • cプログラム

    #include<stdio.h> /*Calc MAX of (a,b)*/ int max(int x,int y) { if(x>y) return x; else return y; } /*Calc n!*/ void fact(int n) { int i,ans; ans=1; for(i=n;i>=1;i--){ ans*=i; } printf("ans=%d\n",ans); } /*END*/ void end() { printf("Thanks\n"); exit(0); } /*Main*/ int main() { int key; int a,b,saidai; int n; while(1){ puts("\n=====Main MENU ====="); puts("1.......max(a,b)"); puts("2.......n!"); puts("9.......END\n"); printf("Input No(1,2,9)=?"); scanf("%d",&key); switch(key){ case 1: printf("Inputs:a,b?"); scanf("%d,%d",&a,&b); saidai=max(a,b); //Call max(a,b) printf("max(%d,%d)=%d\n",a,b,saidai); break; case 2: printf("Input:n?"); scanf("%d",&n); fact(n); break; case 9: end(); break; default: printf("!!!!!Miss Input_No!!!!!\n"); break; } } のプログラムなのですが、1の処理を行った場合max(a,b)の値が正しく表示されません どこを直せばいいでしょうか? return(0);

  • C言語

    forの直後で1+2+3+4+5+・・・・・・・と加算し続ける式がわからないので教えてください。 #include<stdio.h> int main(void) { char moji; int i,sum; printf("正の整数を1から順に加算します。n\"); printf("加算を開始してよろしいですか。(Y=実行。N=終了)\n"); moji=getchar(); if(moji==y) { for(i=2;sum>=1001;i++) { この部分がわかりません; printf("加算値は%dです。¥n",sum); } }else if(moji=='n'){ printf("終了します。\n"); }else{ printf("YまたはNを入力してください。\n"); } return 0; }

専門家に質問してみよう