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

このQ&Aのポイント
  • ハノイの塔の関数内での進み方に関する質問です。
  • 6行目の(no, x, y)の初期値や、9行目のno=2or3の表示の理由がわかりません。
  • 10行目でのprintfが行われず、6行目に戻るのでしょうか?
回答を見る
  • ベストアンサー

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

■プログラムは後半に書いてます。よろしくお願いします。      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|}

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

  • ベストアンサー
  • Tacosan
  • ベストアンサー率23% (3656/15482)
回答No.3

再帰を考えるときに「10行目 に行くと n > 1 で6行目に戻りますよね?」と思っちゃダメです. #2 に書いてある「別の move」の意味を読み取ってください. 分かりやすいかどうか知らんしあんまり適切って感じもしないけど, こんな風に考えることもできます: 最初に main から呼び出される move を move0, move0 から呼び出される関数を move1, ... としてみてください. つまり void move1(int no,int x,int y) /* (b) */ { static int i=1; if(no>1) move2(no-1,x,6-x-y); printf("%dを%d軸から%d軸へ移動 : %d\n",no,x,y,i++); if(no>1) move2(no-1,6-x-y,y); } void move0(int no,int x,int y) /* (b') */ { static int i=1; if(no>1) move1(no-1,x,6-x-y); /* (a) */ printf("%dを%d軸から%d軸へ移動 : %d\n",no,x,y,i++); if(no>1) move1(no-1,6-x-y,y); } int main(void) { move0(N, 1, 3); /* 以下略 */ } のような感じです. このとき, (a) の関数呼び出しによって (b) と (b') のどちらに行くと思いますか? 「再帰呼び出し」というのは, 同じ関数を「改めて呼び出す」ということです. 間違っても「関数の先頭に戻る」という意味ではありません.

kurume39
質問者

お礼

ありがとうございます。 同じ奴では無いということですね! 一段目のmove1 その次に二段目のmove1 そして三段目のmove1に・・・ なんとなく再帰理解することが出来ました。ありがとうございます。

その他の回答 (2)

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

これは、「再帰」と呼ばれる方法です。 関数内で自分自身を呼びだします。 moveの動作は次の通りです。 9,10行目:n>1ならmove(no-1,x,6-x-y);を呼び出す 11行目:printf 12,13行目:n>1ならもmove(no-1,6-x-y,y);を呼び出す 14行目:関数の終りなのでreturn 10行目 3 1 3 10行目 2 1 2 10行目 1 1 3 printf とありますが、10行目でループしているのではなく、 3 1 3 mainの中からmove(3,1,3)で呼び出したもの 2 1 2 ↑のmoveの10行目で move(2,1,2)で呼び出したもの 1 1 3 ↑のmoveの10行目で move(1,1,3)で呼び出したもの printf ↑で呼び出されたmove(1,1,3)はn=1なので、もう別のmoveは呼び出さない と、 moveの中からmoveを呼び出したものです。 > しかし、returnもbreakもないためまた6行目に戻るのでしょうか? 戻りません。 途中で「別のmove」を実行しているので、6行目に戻っているように見えるだけです。 関数の最後まできたら自動的にreturnする、というのがCの動作です。 > (no, x, y) はどのような値で関数が始まるのでしょうか? どこから呼ばれるかは、順番に辿ればわかります。 実行して最初に呼ばれるのはmain関数です。 その中に move(N,1,3); とあるので、これが最初です。 > 9行目があるのにno=2or3で表示される理由がわからないです。 if(no>1) move(no-1,x,6-x-y); ですが、 {}が無いですよね? これは、ifの直後の ; までが{}に入っているのと同じことです。 if(no>1) { move(no-1,x,6-x-y); } どちらも正しい文法です。前者は初心者にはまぎらわしいので、{}を必ず付けるようにするとよいでしょう。 で、こうすると、printfには何も条件が付いていないことがわかります。

kurume39
質問者

お礼

ありがとうございました。理解出来ました。

kurume39
質問者

補足

■素早い返答ありがとうございますm(_ _)m ■関数の最後まできたら自動的にreturnする、というのがCの動作です。 知りませんでした!ありがとうございます! ■まだ分からない所が・・・時間があればもう一度よろしくお願いします。 10行目 に行くと n > 1 で6行目に戻りますよね? そうするとまた10行目・・・ となり結局11行目のprintfへたどり着くときは n = 1 以外ないような気がします。 11行目 に行けたということは n = 1 と言うことで…、そうすると 12行目 の条件式の存在意味が…ない? 本当に 12行目 の式は正しいんでしょうか? PS この問題と課題は「解きながら学ぶC言語」に掲載されていた例題です。

  • Tacosan
  • ベストアンサー率23% (3656/15482)
回答No.1

返り値が void であるような関数では, 最後の } にたどりついたら「return; が書かれている」ものとして処理されます. これで「returnもbreakもないため」という疑問は完全に解消されましたね.

kurume39
質問者

お礼

ありがとうございました。理解出来ました。

kurume39
質問者

補足

返り値が void であるような関数だと「return; が書かれている」ものとして処理されるんですね! 理由を知れる方が記憶に残るので・・・ありがとうございます! よければ上の補足質問よろしくお願いしますm(_ _)m

関連するQ&A

  • 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文が何のためにあるのかもよくわからないのです。 自分でも書いていて要点がはっきりしませんが、どなたかお願いいたします。

  • ハノイの塔

    ★自分が理解している事 「(n-1)ハノイが解けると仮定するとnハノイも解けること」 は理解できます。 そして数学的帰納法によりすべての自然数についてハノイは「解ける」 ここまではわかります。 ★わからないのは、その「解き方」です。 「解ける」ことはわかるのですが「解き方」がわからないのです。 何故このプログラムで正解が表示されるのかが理解できないのです。 確かに、紙と鉛筆でプログラムの流れを追っていくと解けています。 しかし、何でこのプログラムで解けるのかがわからないのです。 棒xは出発点 棒yは目的地 棒zは作業棒 です。 (n-1)ハノイをひとかたまりと考え、作業棒Zに移す。 nハノイを目的地Yに移す。 (n-1)ハノイをひとかたまりと考え、目的地Yに移す。 そんな説明で確かに納得した気になりはします。 しかしこのプログラムでは(n-2)以下の場合についてはいっさい語っていません。 何かだまされてる気がします。 このプログラムでは(n-1)について語っていますが (n-2)以下については全く語っていません。 数学的帰納法により「解ける」ことは証明済みですが、 「その解き方」がこのプログラムでよいという「証明」はありますでしょうか? 理解のコツはありますでしょうか? よろしくお願いいたします。 #include<stdio.h> #include<stdlib.h> void hanoi(int n, char x, char y, char z) { if(n==0) {/* 何もしない */} else { hanoi(n-1,x,z,y); printf("%c->%c,",x,y); hanoi(n-1,z,y,x); } } int main(void) { int num; scanf("%d",&num); hanoi(num,'A','B','C'); return 0; }

  • 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があるのでどこがつながっているのか順番がわかりません。 具体的な数値の手順を教えてください。

  • 配列

    このプログラムを #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) \n)",x,y); RUL(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); } 実行結果は (0 0) (0 1) ・ ・ ・ (0 254) (0 255) になります。これを2次元配列で表したのですがどのようにいじればいいでしょうか?おねがいします。

  • プログラム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++の関数で

    Visual C++で6の4乗を求めるプログラムを作ろうとしたのですがうまくいきません。どこが間違っているか教えていただけないでしょうか? #include "stdafx.h" int get; bekijyo(int,int); void main(void) { int number1,number2; int kekka; number1=6; number2=4; kekka=get; bekijyo(number1,number2); printf("%dの%dは%dです。); } int getbekijo(int x,int y) { int z; if(y==1) return(x); z=x; getbekijyo(x,y-1); return(z); }

  • 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言語で書かれたハノイの塔のプログラムをZ80で動作させたいのですが、アセンブルするとどうなるのでしょうか??教えてください。 void move(char n,char a,char b){ if(n>1)move(n-1,a,6-a-b); if(n>1)move(n-1,6-a-b,b); } int main(){ char n=5; move(n,1,2); }

  • ヒルベルト曲線のプログラム(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変換とエントロピー符号にかけ圧縮させその圧縮率を求めその後画像はちゃんと戻るかを調べるために復元のプログラムを書かないいけませんがそのプログラムがわかりません。教えてください。

  • 配列の要素数に変数を入れたい

    配列に数の入力履歴を入れて最後にその数を出力したいのですが、変数を入れることはできないと勉強した記憶がありまさにその通りコンパイルエラーが出ました。 他に何か方法はありませんでしょうか。 /* 課題1-3 */ #include <time.h> #include <stdio.h> #include <stdlib.h> #include <math.h> int main(void) { int i; int no1; /* 範囲1 */ int no2; /* 範囲2 */ int max; /* 大きい乱数 */ int min; /* 小さい乱数 */ int y; /* 当てさせる数 */ int stage; /* 入力回数 */ int x; /* 読み込んだ値 */ int n; /* 入力制限 */ srand(time(NULL)); no1 = rand(); no2 = rand(); if(no1 > no2){ max = no1; min = no2; } else{ max = no2; min = no1; } y = min + rand() % (max-min); n = ceil(log(max-min)/log(2)); int a[n]; /*←配列の要素数をn個にしたい*/ printf("%d以上%d以下の整数を当ててください。\n", min, max); stage = 0; do{ printf("残り%d回。いくつでしょう:\n", n - stage); scanf("%d", &x); a[stage++] = x; if(y > x) printf("小さいです。\n"); else if(y < x) printf("大きいです。\n"); }while(y != x && stage < n); if(y != x) printf("残念でした。正解は%dです。", y); else printf("正解です。%d回目で正解しました。", stage); puts("\n---入力履歴---"); for(i=0; i<stage; i++) printf("%2d : %4d %+4d\n", i+1, a[i], a[i] - y); return (0); }

専門家に質問してみよう