• ベストアンサー

再帰について(C言語)

今、再帰処理を勉強しています。 しかし、以下のプログラムがどうしても理解できません。 流れ的には一体どういう手順になっているのでしょうか? return i * fact( i - 1 )の部分を考えると頭が こんがらがってしまいます。 #include <stdio.h> int main( void ){  printf("5の階乗は %d です", fact(5) );  return 0; } int fact( int i ){  if( i == 1 ) return 1;  else return i * fact( i - 1 ); } --------実行結果---------- 5の階乗は 120 です

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

  • ベストアンサー
回答No.1

回数, i, return 1, 5, 5 * fact(5-1) 2, 4, 4 * fact(4-1) 3, 3, 3 * fact(3-1) 4, 2, 2 * fact(2-1) 5, 1, 1 5回目の戻り値が1なので、4回目の戻り値は、2 * 1 = 2。 4回目の戻り値が2なので、3回目の戻り値は、3 * 2 = 6。 3回目の戻り値が6なので、2回目の戻り値は、4 * 6 = 24。 2回目の戻り値が24なので、1回目の戻り値は、5 * 24 = 120。 結果、呼び元に返される戻り値は、120。 と、トレースすれば理解出来ました?

その他の回答 (9)

  • BLUEPIXY
  • ベストアンサー率50% (3003/5914)
回答No.10

fact(n) を求めたい時 fact(n-1) が判っていれば fact(n)=n*fact(n-1) ですね。 fact(x) を作っている最中に、fact(x)は、既に完成したものと考えてそれを使うということですね。 (実際に)使う時には、(それは既に完成しているので、) fact(i-1) とすれば、i-1 の階乗がその関数で求まるので利用するだけです。 ただ、こういう関数を作る時には、それが本当に終わるのかどうか不安になりますよね? なので、問題が(小さくなって)いずれ終わるようにしておく必要があります。 (新しく再帰的に呼び出す前に終了条件を調べる必要があります) 階乗の場合でいうとn*(n-1)*…*2*1 のように1までやればいいのですから-1 しておけばいずれ1になって呼び出しは終了します。

amazontester
質問者

お礼

皆さん本当にありがとうございます! 全員にレスを付けられなくて申し訳なく思いますが、 何とか理解できました。 でも再帰って不思議な概念ですね・・。

回答No.9

もう既に多くの方がご回答されていますので、以下は蛇足のアドバイスです。 再帰処理というのはある関数が自分自身を呼び出すことですね。 (1)main{}の中で引数を5にしてfact()関数を呼び出しています。 (2)呼び出されたfact()関数は引数を変数iに入れて次の演算をしています。もしi=1であれば関数値1を返しますが、今はi=5ですので、returni*fact(i-1)のところは5*fact(4)の値を関数値として返します。 (3)ここで留意すべきはfact(4)を呼んでいる点です(←再帰処理)。従って(2)と同じようにreturni*fact(i-1)のところは5*4*fact(3)の値を返すことになります。 (4)同様にしてi=2まで繰り返すとreturni*fact(i-1)のところは5*4*3*2*fact(1)となりますね。 (5)最後にfact(1)の呼び出しはif( i == 1 ) return 1により1を返し、関数処理ルーチンからmain{}ルーチンに戻ることになりますから、結局fact(5)の関数は5*4*3*2*1=120を返すことになります。

  • jacta
  • ベストアンサー率26% (845/3158)
回答No.8

単に流れが分からないだけであれば、 int fact(int i) {  int result;  printf("enter fact(%d)\n", i);  if (i == 1) result = 1;  else result = i * fact(i-1);  printf("leave fact(%d) : %d\n", i, result);  return result;  } のように、最初と最後にログ出力を行えばわかるようになるはずです。 再帰が分からないという人を見ていると、(特に階乗を例にしていると、)分からないのは処理の流れだけでなく、階乗が分かっていないことがよくあります。 また、本当に知りたいのは流れではなく、ループで済むものをなぜ再帰にするのかが分からないということだったりします。 そんなことはないのでしょうか?

回答No.7

処理の流れは考えない……というのも手かもしれません。 まず、「階乗」はおわかりだと思いますが、 5! =  5×4×3×2×1 です。 ここで、 4×3×2×1 というのは、よく見れば、4!です。 つまり、5!=5×4!です。 というのは、いいですよね。 同じように、 n!=n×(n-1)!です。 これをそのままプログラミングすると、 int fact(n) { return n * fant(n - 1); } です。fact(n - 1) は、n - 1 の階乗ですから。 (実際には、これだと、無限に計算し続けるので、 n == 1 の時の条件がついていますが) 実際に、再帰の流れを追うのは(再帰を使うのが有効であればあるほど)大変になります。逆に、流れを追うことで、流れが理解できない処理は再帰で書くことができなくなる傾向があります。 もともと、再帰というのは、全体の処理の流れがわからなくても、一部分(今の場合は、n! と (n - 1)! )の関係と、再帰の終了条件(今の場合は、n == 1)がわかれば、処理が書けるということが特徴です。 ですから、流れを追って処理を理解するよりは、階乗の隣通しの関係をそのまま、Cに置き換えたという理解の方がいいような気がします。

  • nerosuke
  • ベストアンサー率33% (39/115)
回答No.6

>>return i * fact( i - 1 )の部分を考えると頭が >>こんがらがってしまいます 再帰は初心者には結構とっつきにくいとおもいます。 頭で考えずに、fact関数の中にprintfを入れて#No1さんの表の用に 必要な値を出力してみれば流れが掴めるかと・・・

noname#45950
noname#45950
回答No.5

私もプログラマーなりたての頃、再帰処理の概念が理解できませんでした。 考え方としては・・・他の関数を呼ぶ代わりに、自分自身(例題では"fact"関数です)を呼ぶということです。 ただ、終了条件がないと、無限ループになってしまうので、ここでは終了条件として「iが1の時」が指定されているわけです。 いかがでしょう? かえって混乱させちゃったら、ごめんなさい。

  • Yeti21
  • ベストアンサー率47% (396/830)
回答No.4

また誤字です。m(_"_)m 再起→再帰

  • Yeti21
  • ベストアンサー率47% (396/830)
回答No.3

再起処理は難しく考える必要はありません。 落ち着いて手順どおり追えば良いです。 1.mainからfact(5)が呼ばれる。 2.factからfact(4)が呼ばれる。 3.factからfact(3)が呼ばれる。 4.factからfact(2)が呼ばれる。 5.factからfact(1)が呼ばれる。 6.fact(1)から1が戻され、2*1が計算される。 7.fact(2)から2が戻され、3*2が計算される。 8.fact(3)から6が戻され、4*6が計算される。 9.fact(4)から24が戻され、5*24が計算される。 10.fact(5)から120が返される。

  • tkun62
  • ベストアンサー率23% (37/159)
回答No.2

factが呼ばれた時にもう1つ別のfactが発生し 生成されたfactはreturnで消えると考えれば 分かり易いかと思います。

関連するQ&A

  • c言語の再帰で(関数呼び出し)+1がわからない

    再帰がどのように処理されているのか理解するために、再帰の時に +1 してみたところ 0! = 1 1! = 2 2! = 5 3! = 16 4! = 65 5! = 326 6! = 1957 7! = 13700 8! = 109601 9! = 986410 10! = 9864101 となりました。 普通の階乗の値を求めた最後に +1され、それが戻されると思ったのですが違いました。 これはどういう処理がされているのでしょうか? #include <stdio.h> int kaijo(int); int main() { int i; for (i = 0; i < 11; i++) printf("%d! = %d\n", i, kaijo(i)); return 0; } int kaijo(int n) { if (n == 0) return 1; else return n * kaijo(n - 1) + 1; }

  • return 1

    #include<stdio.h> int fact(int num); int main(void) { int i; printf("Input figure freely:"); scanf("%d", &i); printf("%d", fact(i)); return 0; } int fact(int num) { if(num>0){ return num * fact(num-1); }else{ return 1; } } -------------------------------------------- 上のプログラムは再帰呼び出しを使った階乗計算の プログラムです。 func()関数内のreturn 1の意味をどなたか教えて いただけないでしょうか?

  • 困ってます…nCrを求めるC言語プログラミング

    nCr、つまりn個のうちr個を取り出すときの場合の数を求めるプログラミングを作りたいのですが、どうもうまくいきません。 関数combinationを作って求めるという指定もあり、自分で出来るとこまで作ってみたのですが訳がわからなくなってしまい、かなり困っています…; コンパイルは出来るのですが実行してもセグメントエラーが出るばかりで… すみませんがご指摘していただけないでしょうか…? #include<stdio.h> //階乗を計算する関数 int fact(int num){ int i; if(num < 0){ return -1; } else if(num == 0){ return 1; } else if(num == 1){ return 1; } else { i = num * fact(num - 1); return i; } } //コンビネーションを計算 int combination(int n, int r) { int fact(int num); int i; i=fact(n)/fact(r)/fact(n-r); return combination(n-1, r-1)-combination(n,r-1); } int main(void) { int n, r; while ( printf("n r を入力して下さい。"), scanf("%d%d", &n, &r) == 2 ) { printf("nCr(%d,%d)=%d\n", n, r, combination(n, r)); } return 0; }

  • C言語について

    次のような問題です。 問 自然数nを入力し、nを3で割って割り切れるかどうかを判定し結果を表示する。「割り切れる」、「1余る」、「「2余る」のいずれかが入るものとする。 このようなものをつくりました。 #include<stdio.h> int main(void) { int n; printf("自然数:"); scanf("%d",&n); if(n==0){ printf("割り切れる\n"); }else if(n==1){ printf("1余る\n"); }else{ printf("2余る"); } return(0); } これで合っているかよろしくお願いします。

  • エラー C言語 プログラミングについて

    #include<stdio.h> int leapYear(int); int main(void){ int year,i; for(i=2001;i=2999;i++){ year=i; printf("%d leap = %d \n",i,leapYear(int year)); return 0; } } int leapYear(int year){ if(year%100==0){ return 0; } else if(year%400==0){ return 1; } else if(year%4==0 && year%100!=0){ return 1; } } をコンパイルすると11行目に式の構文エラーが出るんですが どうしてでしょうか?? 間違ってない気がするんですけど。。

  •  現在、私はC言語を学んでいます。

     現在、私はC言語を学んでいます。  プログラミングの初期の初期の問題なんですが、 「Hello World」という有名なプログラムがありますよね? それについての質問です。 #include<stdio.h> main() { printf("Hello World"); return 0; } も #include<stdio.h> main(void) { printf("Hello World"); return 0; } も #include<stdio.h> int main() { printf("Hello World"); } もちゃんと表示できます。 ここで質問です。 int main(void) int main() main() main(void) はどう違うんですか? あと、 return 0; はあっても無くてもいいようなんですが どういう意味があるんでしょうか?

  • 再帰プログラム

    strに格納されている文字数を数えるプログラムです。 #include<stdio.h> int rstrlen(char *); int main(void) { char str[] = {"abcdefghijk"}; printf("文字数:%d\n",rstrlen(str)); return 0; } int rstrlen(char *p) { if(*p) { p++; printf(p); return 1 + rstrlen (p); } else return 0; } return 1 + rstrlen (p);の部分で再帰をし1をプラスすることにより文字数をカウントしmainのprintfで文字数を表示しているのですがカウントしている値はどこに格納していてどのようにmainに返しているのかが分かりませんでした。教えてください。

  • c言語についての質問です。

    #include<stdio.h> int main(void){ int a; printf("1文字たいぷしてください。\n"); scanf("%d",&a); if(a>=65 && a<=90){ printf("大文字です。\n"); } else if(a>=97 && a<=122){ printf("小文字です。\n"); } else{ printf("大文字でも小文字でもありません\n"); } return 0; } このプログラムは正しくなくて、 intをchar %dを%cにかえなければなりません。 なぜintはダメなんでしょうか? できれば丁寧に教えてください。 お願いします。

  • c言語のフローチャートについての質問です

    #include <stdio.h> int main (void) { int n; for (n=1900; n<2000; n++) { if (n%4==0 && n%100!=10) printf ("%d",n); else if(n%400==0) printf ("%d",n); } printf("\n"); return 0; } をどなたかフローチャートに直してください JIS規格のものでお願いします

  • C言語の質問です

    下記の素数か素数でないか調べるコードで、 (1)変数名にis_primeとありますが、isは何を意味しているのですか? (2)is_prime = 1;とするのがわかりません。 (3)以下、return 0; まで、どういう流れかわかりません よろしければコメント以下から1行ずつ教えてもらえるとうれしいです。 #include <stdio.h> int main(void) { int num, i, is_prime; printf("判定したい数を入力してください: "); scanf("%d", &num); /* 約数があるかどうか調べる */ is_prime = 1; for(i=2; i<=num/2; i=i+1) if((num%i)==0) is_prime = 0; if(is_prime==1 && num > 1) printf("素数です"); else if (num > 1) printf("素数ではありません"); return 0; }

専門家に質問してみよう