C言語で正の整数nを受け取って、フィボナッチ数列を求めるプログラム

このQ&Aのポイント
  • C言語で正の整数nを受け取り、フィボナッチ数列を求めて表示するプログラムがありますが、答えがおかしくなる問題があります。
  • また、大きな値を指定するとプログラムが停止し、ファイルを保存する際に不具合が起こる問題もあります。
  • これらの問題について、解決策を教えていただきたいです。
回答を見る
  • ベストアンサー

C言語で正の整数n を受け取って、この数列の第1 項から第n 項までの

C言語で正の整数n を受け取って、この数列の第1 項から第n 項までのフィボナッチ数列を求めて表示、および結果をファイルに保存するプログラムを作ってみました。 ですが、答えがおかしくなります(具体的にはーがつくものが交互にでてきます)・・・コンパイルはできたのですが・・・ 環境はvisual c++2010expressです。 また、a[ ]の配列をもっと増やす方法はないでしょうか・・・大きな値を指定してやると、プログラムを実行したら「このプログラムは停止しました~」という画面が出てきます・・・ また、ファイルを保存するときにfprintfで保存しようと思うのですがa+だとうまく保存されるのですがw+だと最後から一つ前のものしか書き込まれません・・・なぜなのでしょうか・・ どなたか教えていただけないでしょうかm( )m ソースはこちらです #include<stdio.h> int main(void){ int i; int n; int a[9999]; printf("n? "); scanf("%d",&n); while(n<0){ printf("nは0より大きい整数でお願いします\n"); printf("n? "); scanf("%d",&n); } for(i=1;i<=n;i++){ if( i == 1 || i == 2 ){ a[i]=1; } else{ a[i]=a[i-1]+a[i-2]; } } for(i=1;i<=n;i++){ printf("a[%d] = %d\n",i,a[i]); } for (i=1;i<n;i++) { FILE *file; file = fopen("k04a.txt","w+"); fprintf(file,"a[%d] = %d\n",i,a[i]); fclose(file); } return 0; }

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

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

> a[46] = 1836311903 > a[47] = -1323752223 となることでしたら、符号付きint(32bit)で表現できる範囲をオーバーフローした結果なので「正しい」です。 a[45] = 1134903170 = 0x43a53f82 a[46] = 1836311903 = 0x6d73e55f a[47] = 2971215073 = 0xb11924e1 ↑最上位が1なので、符号付きの場合、負の値(2の補数表現) → -1323752223 また、符号無し(unsinged int)でやっても、 a[48] = 4807526976 = 0x11e8d0a40 →32bitまで 0x1e8d0a40 = 512559680 とオーバーフローします。 64bit整数を使うともう少し長くなりますが、93までです。 根本対策は、必要なだけのビット幅を持った整数を使うことです。 Cでも多倍長精度整数のライブラリがありますし、自作もでますが、 数列をファイルに出力するだけなら、PythonやRubyといった、無限精度整数を持っている言語を使うのが簡単です。 >また、a[ ]の配列をもっと増やす方法はないでしょうか・・・ >大きな値を指定してやると、プログラムを実行したら「このプログラムは停止しました~」という画面が出てきます・・・ 9999 と指定しているので、9998までしか保証されません。 Cでは、配列の添字のチェックはしないので、それより大きな値を使ってもなにくわぬ顔で実行しようとします。 方法として ・nが決まってから、必要な大きさの領域をmalloc/callocを使って確保する(使用後の解放は忘れずに) ・今回ので言えば、直前2つの値だけわかればいいので、それだけを確保し、値は随時出力する。 >また、ファイルを保存するときにfprintfで保存しようと思うのですがa+だとうまく保存されるのですがw+だと最後から一つ前のものしか書き込まれません・・・なぜなのでしょうか・・ openしたときのファイルポインタの位置の違いでしょう。 w+はポインタが先頭なので、書き込むとそこから上書き a+はファイルの最後に移動するので、書き込めばその続きから。 それより、1回のfopenでまとめて書いた方が効率よくないですか? 上の「2つだけ確保」と合せて int a[2]; int A ; a[0]=0; a[1]=0; FILE *file; file = fopen("k04a.txt","w"); for (i=1;i<n;i++) { if( i == 1 || i == 2 ){ A=1; }else{ A=a[0]+a[1]; } a[0]=a[1]; a[1]=A; printf("a[%d] = %d\n",i,A); fprintf(file,"a[%d] = %d\n",i,A); } fclose(file);

sandrock-kai
質問者

補足

>>となることでしたら、符号付きint(32bit)で表現できる範囲をオーバーフローした結果なので「正しい」です。 なるほどー 確かにそうでした! ただunsigend charで指定しても47ですでに-1323752223だったり・・・・ >>根本対策は、必要なだけのビット幅を持った整数を使うことです。 おおお!多倍長精度整数のライブラリとかあるんですね! ありがとうございます! rudyやPythonは使ったことがなくて>< rudyはいつかやりたいんですけどね・・・・・ >>nが決まってから、必要な大きさの領域をmalloc/callocを使って確保する(使用後の解放は忘れずに) mallocやcalloc関数ですかー実は初耳で(汗 勉強してきます! >それより、1回のfopenでまとめて書いた方が効率よくないですか? 上の「2つだけ確保」と合せて おおおおおお なるほど! 確かにこういう風に書いたほうが効率いいですね! いろいろと勉強になりました! ありがとうございました!

その他の回答 (4)

  • Kules
  • ベストアンサー率47% (292/619)
回答No.5

A No.1のKulesです。 まあ他の回答者が書かれているのですでにその間違いには気付かれているとは思いますが… >そうかunsigned intなら整数ってことにできますねー^^ unsigned intにすることで、表現できる最大の整数の値がちょっと増える(大体倍になる)だけで、 本当に「焼け石に水」程度の効果しかありません。私も使ったことはありませんが、多倍長精度整数やら 無限精度整数やらといった、もっと大きな数値を扱えるような型を使うのがいいんでしょうね。 以上、参考までに。

sandrock-kai
質問者

お礼

わざわざありがとうございます! たしかintでマイナスであった分がプラスで持ってこれるようになるから倍になるんでしたよね! うろ覚えではありますが・・・・・・・ kmeeさんにも教えていただきましたが、多倍長精度整数やら無限精度整数等大きな数値を扱える型があるらしいですね! 自分も初めて聞きました・・・・・ この際なのでそれでもう一度やってみようかと思っています! 大変参考になりました! ありがとうございます!

  • okormazd
  • ベストアンサー率50% (1224/2412)
回答No.4

intなので,nはせいぜい46までしか正しい値を示しません。 fopenやfcloseをforの外に出したほうがいい。 いずれも前の回答者さんのとおりです。 >一つ前のものしか書き込まれません は, for (i=1;i<n;i++) { を, for (i=1;i<=n;i++) { にすればいいのではないですか。

sandrock-kai
質問者

お礼

あっ! なんとイコールを付けていなかったとは ありがとうございました!

  • neko1963
  • ベストアンサー率49% (127/258)
回答No.2

> a+だとうまく保存されるのですがw+だと最後から一つ前のものしか書き込まれません・・・ fopenのモードでは ・"a+" ・・・ 読み込み/追加書き出し ・"w+" ・・・ 読み込み/書き出し となっています。 "a+" ではファイルの最後に追加していきます。 "w+" ではファイルの内容が失われます。 そもそも、for ループの中で fopenを行っているのはなぜでしょうか? for (i=1;i<n;i++) { fprintf(file,"a[%d] = %d\n",i,a[i]); } にして、 FILE *file; file = fopen("k04a.txt","w+"); はプログラムの前の方に移動、 fclose(file); は return 0;の直前に移動すれば良いのではないでしょうか。

sandrock-kai
質問者

お礼

>>そもそも、for ループの中で fopenを行っているのはなぜでしょうか? 確かにその必要はなかったです! ありがとうございます! おかげさまでファイルの書き込みはうまくいくようになりました! まあ入力した項のひとつ前しかなぜか表示されませんが・・・ w+やa+の説明もわかりやすくありがとうございました!

  • Kules
  • ベストアンサー率47% (292/619)
回答No.1

私もCはほとんど素人ですが… フィボナッチ数列って結構な勢いで値増えていきますよね?第30項で83万ぐらいになってしまいます。 intってそんなに大きな数字は表せなかったような… というわけで1つの解決法としては、ほぼ焼け石に水ですが aをunsigned intで宣言する、あるいはlongとかlong longとかで宣言する というのがあると思います。 また、aを9999までしか用意していないのなら、nを10000より大きな数字を入れた時点でバッファオーバーラン?とかいうのが起きてだめになります。 さらに言うと、Cでの配列は要素0から定義されていたはずなので、少々気持悪いですが for(i=1;i<=n;i++){ ではなく for(i=0;i<n;i++){ とした方がいいように思います。 以上、参考になれば幸いです。

sandrock-kai
質問者

お礼

>>intってそんなに大きな数字は表せなかったような あっ・・・・・・・・そうでしたそういえば制限がありました・・・・・・ なるほど!ありがとうございました! >>さらに言うと、Cでの配列は要素0から定義されていたはずなので、少々気持悪いですが for(i=1;i<=n;i++){ ではなく for(i=0;i<n;i++){ とした方がいいように思います。 確かに0から指定してやることができるのはできるのですが、ここでは表示するときにa[1]から表示して見たかったので・・・・ ありがとうございました!助かりました! そうかunsigned intなら整数ってことにできますねー^^

関連するQ&A

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

    #include<stdio.h> #define N 3 void inputAns(int *row,int *col,int data[][N]); void printAns(int ID,int data[][N]); int main(){ int row[N]={2,3,6},col[N]={8,5,3},answer[N][N]; int i,ID; printf("Input your ID number :\n"); scanf("%d",&ID);inputAns(row,col,answer); printAns(ID,answer); return(1); } void inputAns(int *row,int *col,int data[][N]){ int i,j; printf(" Input Answers of matrxi Q :\n"); for(i=0;i<N;i++){ for(j=0; j<N; j++){ printf("%2d+%2d=",row[i],col[j]); scanf("%d",&data[i][j]); } } } void printAns(int ID, int data[][N]){ FILE *fp; int i,j; fp=fopen("ans.dat","a+t"); fprintf(fp,"%d\n",ID); for(i=0; i<N; i++){ for(j=0; j<N; j++) fprintf(fp," %3d",data[i][j]); fprintf(fp,"\n"); } fclose(fp); } この百マス計算のプログラムの28行目からを書き換えて以下のような画面出力を求めたいです。 >./a.exe ID=50413001 [ 9/9 ] ID=50413002 [ 5/9 ] ID=50413003 [ 7/9 ] ID=50413004 [ 6/9 ] ID=50413005 [ 5/9 ] == correct rate === 4/5 3/5 5/5 5/5 1/5 4/5 4/5 1/5 5/5 =================== > 読み込むファイル(ans.dat)は以下のものです。 50413001 10 7 5 11 8 6 14 11 9 50413002 10 7 5 11 9 5 13 10 9 50413003 10 7 5 11 5 6 14 10 9 50413004 10 11 5 11 5 6 14 10 9 50413005 9 11 5 11 5 6 14 10 9 読み込みから集計がうまくいかず困っています。 どなたか教えてくれませんか?

  • C言語の問題を教えてください。初心者です。

    1000以下の整数を入力して、それ以下の素数を出力するプログラムをつくっています。 一応自分でやってみたのですが、どうもうまくできません。自分ではどこが間違っているのかさっぱりなので間違い箇所を指摘し、どうすれば良いのかを教えていただきたいです。ほかに効率的なやりかたがあったらあわせてお願いします。 初心者なので易しく説明していただけるとありがたいです。 <自分でやってみた> #include <stdio.h> int main(){ int i,j,N; int a[1000]; for (i=2; i<=1000; i++) { a[i]=1; } for (i=2; i<=1000; i++) { for (j=i++; j<=1000; j++) { if (a[j]=1 && j%i==0) { a[j]=0; } } } printf("2以上1000以下の整数を入力してください\n"); scanf("%d",&N); printf("%d以下の素数は\n",N); for (i=2; i<=N; i++) { if (a[i]=1) { printf("%d\n",i); } } } よろしくお願いしますm(_ _)m

  • c言語について

    #include <stdio.h> MakeData(int *a,int n){ int i,b[5]={5,1,4,7,2}; for(i=0;i<5;i++){a[i]=b[i];} } BubbleSort(int n,int *a){ ここに流れ図に沿ったプログラムを作る } main(){ int i,n,a[100]; n=5; MakeData(a,n); for(i=0;i<n;i++){printf("%4d ",a[i]);}printf("¥n"); printf("並べ替え後¥n"); BubbleSort(n,a); for(i=0;i<n;i++){printf("%4d ",a[i]);}printf("¥n"); } この課題が分かりません。 もし詳しい方がいらっしゃいましたら教えて頂けると助かります。

  • c言語について

    C言語で、二つの整数値を読み込んで、前者の値が後者の何%であるかを実数で表示するプログラムを作成しようとして以下のコードを書きました。 #include <stdio.h> int main(void) { int n1, n2 ; puts("二つの整数を入力してください。") ; printf("整数A : \n") ; scanf("%d" , &n1) ; printf("整数B : \n") ; scanf("%d" , &n2) ; printf("Aの値はBの%f%%です。\n" , (double)(n1 / n2) * 100) ; return 0 ; } 上記コードを実行すると、0.000000%のような結果になります。 そこで、最後のキャスト演算子を使用した後の式で(n1 / n2) * 100がまずいのかなと考え、n1 / n2 * 100にしたら上手くいきました。 なぜ、上記コードだと上手くいかないんでしょうか?

  • C言語の、階乗を使うプログラムの問題を教えて下さい

    C言語の、このプログラムを作るのが分かる方、教えて下さい。階乗を使う問題です。 「キーボードで整数aを入力するとn!>aとなるときの最小のnを出力するプログラムを作りなさい」という問題です。 分からず苦戦しています。 nをキーボードで入力してn!を求めるには #include<stdio.h> int main(void) { int kekka,n,i; printf("n=? \n"); scanf("%d",&n); kekka=1; for(i=1;i<=n;i++) { kekka=kekka*i; } printf("%d!は%dです。\n",n,kekka); return 0; } とすればいいのは自分で作れたのですが、問題にあるn!>aのプログラムが分からず困っています。 分かる方、お願いいたします

  • C言語で分からないところがあるのですが……

    C言語で分からないところがあるのですが…… すみません。C言語の課題で分からないところがあり、質問しに来ました。 ユーザから数を受けて、そこまでのフィボナッチ数列を表示させるというプログラムです。 下のソースコード(でいいんですよね?)は正しいやつです。 for文を使って、繰り返しの作業を行うことに成功しましたが、doとwhileに書き換える作業がうまくできません。 どなたか助けてください。 #include<stdio.h> fib(int n) { if(n == 1)return(1); else{ if(n == 2) return (1); else return fib(n-1) + fib(n-2); } } main() { int n, i ; printf("INPUT the number. : "); scanf("%d",&n); for(i=1; i<=n; i++){ printf("F%d = %d\n",i, fib(i)); } }

  • C言語で複数列のデータを1列のみ読み込みたい

    行m列の任意のデータの処理を行うプログラムで, 列ごとの統計を行うためにm列目のデータを取り出したいのですが,うまくいきません. どのようなコードを書けばいいでしょうか? 自分で作ってみたのは以下のようなプログラムです(ファイルを開いて→m列目の読み込みの部分) EOFを使っているためか,行数のiには全データ数が入ってしまいます. void main (void) { FILE* fp; int i, j; i=0, j=0; char FilePath[500]; char Folder[100]; char File[50]; printf("Folder Name:"); scanf("%s",&Folder); printf("File Name:"); scanf("%s",&File); sprintf(FilePath,"%s/%s",Folder,File); if(( fp = fopen (FilePath,"r")) == NULL){ printf("cannot open '%s'\n", FilePath); exit(1); } //ここまではうまく動きます while (fscanf(fp, "%lf", &A[i][0]) != EOF{ i++; } while (fscanf(fp, "%lf", &A[0][j]) != EOF){ j++; } printf("A[%d][%d]", i, j); int n, m;              //n,mはこの後for文で使いたいので登場してもらいました printf("input 'n':"); scanf("%d", &n); printf("input 'm':"); scanf("%d", &m); }

  • フィボナッチ数列 c言語

    c言語でフィボナッチ数列を求めるプログラムを再帰関数をつくり作れという問題でしたのように作りました。 windowsでcygwinというものを使ってコンパイルしています #include <stdio.h> int fib(int); main() { int n,i; printf(\"第何項までのフィボナッチ数? n=\"); scanf(\"%d\",&n); fib(0)=0; fib(1)=1; for(i=2;i<=n;i++){ print(\"f(%d)=%d\",n, fib(n));} } エラーは $ gcc fib2.c fib2.c: In function `main\': fib2.c:10: error: invalid lvalue in assignment fib2.c:10: error: invalid lvalue in assignment とでました。 どこかちがいますか?

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

    #include<stdio.h> int main() { int i,n,total; for(;;){ /* 無限ループ*/ printf("整数n?"); /* nの値の表示 */ scanf("%d",&n); /* ifとbreakを使った終了判定 */ if(n<0)break; total=1; for(i=1;i<=n;i++){ printf("i=%d ",i); total*=i; /* total←total*i(階乗の計算) */ } printf("total=%d\n",total); /* totalの値(結果)の表示 */ } printf("Thanks\n"); /* 終了メッセージの表示 */ return(0); } これは階乗を求めるプログラムなのですが、i++ではなくi--をつかって求める場合 どのように変更すればよいでしょうか? for(i=1;i<=n;i++){ あたりをいろいろ弄ってみたのですが、求めてる結果は得られませんでした

  • この数列プログラムの解説お願いします c言語です

    初項が2です。 #include<stdio.h> int main(void){ int a, n, k, sum = 0; printf("Type k\n"); scanf("%d",&k); a = 2; for(n = 2; n <= k+1; n++){ sum += a; printf("a = %d, sum = %d\n",a, sum); a += (n-1)*(n-1); } return 0; } for文の()内の意味とかよくわかんないです