• ベストアンサー

sin(x)の近似について

sinxの数値計算 任意のxに対するsinxの値をマクローリン展開を利用して近似し、誤差の限界(n番目の値が1*10^-8)になるまでもとめよ。 という問題なんですが、for文でいろいろやってみたのですが、n番目の値が1*10^-8までというのがどうしてもできません。 C言語です。 たぶん、階乗の部分が間違ってると思われます。 下のが自分で考えたものです。アドバイスいただけたら嬉しいです。 #include <stdio.h> int main(void) { signed int i,v; double x=-1; double a,m,n=1.0; double sinx = 0.0; a=x; v=1; printf("a=%f\n",a); printf("sinx = %f\n",sinx); while(1) { sinx = sinx + a; m=x; m=m*x; a = (-1)*v*m/((n+1)*(n+2)); n=n+2; v=v*(-1); if ((a < 1e-8) && (a > -1e-8)) break; } printf("sinx = %f",sinx); return (0); }

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

  • ベストアンサー
  • chie65535
  • ベストアンサー率43% (8506/19341)
回答No.2

#include <stdio.h> /*#include <math.h>*/ /*検算用に使うsin(x)用。検算が必要ならコメントを外す*/ int main(void) { int n,v; double a,k,l,xx; double x; double sinx; printf("sin(x)のxを入力:"); scanf("%lf",&x); /*誤差の限界は「10の-8乗」なので精度を「8桁」に指定*/ /*printf("sin = %.8f\n",sin(x));*/ /*検算用にライブラリ関数でのsin(x)を表示。検算が必要ならコメントを外す*/ /*変数の初期化*/ sinx = 0.0; n = 1; /*2n+1。1,3,5,7…と増加*/ v = 1; /*加算か減算かのフラグ。1,0,1,0,1,0…と変化*/ k = 1.0; /*(2n+1)!。最初は1!なので1*/ l = x; /*xの(2n+1)乗。最初はxの1乗なのでx*/ xx = x * x; /*xの1乗にxの2乗を掛け、xの3乗、5乗、7乗…を作る為に用意した「xの2乗」*/ while(1) { a = (1.0 / k) * l; /*赤枠の部分式*/ if (v) sinx += a; /*nが1,5,9…なら加算*/ else sinx -= a; /*nが3,7,11…なら減算*/ n += 2; /*nを2づつ加算*/ k *= (n - 1) * n; /*(2n+1)!を計算*/ l *= xx; /*xの(2n+1)乗を計算*/ v = !v; /*加算と減算の入れ替え*/ /*誤差の限界判定はsinxにaを加減算した後で行う*/ /*そうしないとxが3.1415926535の時に結果が*/ /*ライブラリのsin関数と食い違ってしまう*/ if ((a > -1e-8) && (a < 1e-8)) break; /*誤差の限界*/ } /*誤差の限界は「10の-8乗」なので精度を「8桁」に指定*/ printf("sinx = %.8f",sinx); /*結果を表示*/ return (0); }

wainder
質問者

お礼

たびたび、質問に答えていただきありがとうございました。感謝しています。

その他の回答 (3)

回答No.4

もう一度失礼します。 少しの間違いを除けば基本的に質問者さんのコードであっているし、 そちらのほうがセンスがいいので、それをもとにしたほうがベターです。 質問者さんはわかっているはずなので念のためですが、サインの展開式 sin x = Σ[m=0,∞] (-1)^m x^{2m+1}/(2m+1) ! = Σ[m=0,∞] a(m) から a(m) = (-1)^m x^{2m+1}/(2m+1) !   = (-1)^(m-1) x^{2(m-1)+1}/(2(m-1)+1) ! × (-1)x^2/[(2m)(2m+1)] = a(m-1) × (-1)x^2/[(2m)(2m+1)] これを利用したアルゴリズムです。 こんなかんじですね。 sx=0.0; a=x; n=1; printf("sin(%lf) = %.8lf\n", xin, sin(x)); printf("----------------------------------------------\n"); do { sx += a; printf("%3d: sin(%lf) = %.8lf, a(%d)=%le\n", n, x, sx, n, a); a = a*(-1)*x*x/(n+1)/(n+2); n+=2; } while(fabs(a)>1e-8); printf("----------------------------------------------\n"); printf("a(%d)=%le\n", n, a); xが大きな数になるとオーバーフローするので、 0<x<2πになるように前処理はしておいたほうがいいと思います。

  • tatsu99
  • ベストアンサー率52% (391/751)
回答No.3

以下のようにして下さい。 ----------------------- #include <stdio.h> // xのn乗を求める double mypow(double x,int n) { int i; double ans = x; for (i = 1; i < n; i++){ ans *= x; } return ans; } // nの階乗を求める double myfact(int n) { if (n == 1) return 1.0; return (myfact(n-1) * (double)n); } int main(void) { int i; int fugou = 1; double x=-1; double v,absv; double sinx = 0.0; printf("x=%f\n",x); printf("sinx = %f\n",sinx); for (i = 1; ; i += 2){ // xのi乗 / iの階乗 を求める v = mypow(x,i) / myfact(i); // 誤差の10の-8乗未満なら終了 //一旦、絶対値を求め、絶対値で判断する(その方がわかりやすい) absv = v; if (absv < 0.0) absv *= (-1.0); //負なら正にする(絶対値にする) if (absv < 1.0e-8) break; //絶対値で比較 sinx += (v*fugou); fugou *= (-1); //iが3,7,11...の時は引き算なのでマイナスにする } printf("sinx = %f\n",sinx); return (0); } -----------------------

回答No.1

>a = (-1)*v*m/((n+1)*(n+2)); は a = a*(-1)*x*x/(n+1)/(n+2); (Cらしく書けば a *= (-1)*x*x/(n+1)/(n+2);) です。n-2の時のaを掛け忘れています。変数v,mは不要ですが、 少しでも演算速度をあげるならm=x*xを【ループの外】に書いてx*xをmに置き換える。 nは整数のほうがいいでしょう。 >v=v*(-1); で符号を変えた後、もう一度a=(-1)*・・・で-1を掛けているので、結局符号は変わっていませんね。

wainder
質問者

お礼

間違えてた意味がわかりました。どうもありがとうございました。

関連するQ&A

  • モンテカルロ法の面積近似

    Mが2の32乗の、線形合同法で乱数を発生し、 モンテカルロ法を使って、 領域a = {(x,y)|x≧1,y≧2,(x-1)^2 + (y-2)^2 ≦1}の面積の近似値を計算するプログラムを作成し、これを利用してn=10000とした場合の上の領域の面積、および円周率πの近似値を求めよ。 (関数を使う) という課題が出たのですが、乱数の出し方?が変であまり近似されません(^_^;) どなたかどこが変か教えてください(>_<) これだと、2と4になってしまうんです(/_;) #include<stdio.h> #include<stdlib.h> #include<math.h> #define N 100 /*領域Rを含む面積T 領域R=求める面積*/ #define Nx 1 #define Ny 2 /*乱数を発生させる関数*/ unsigned long int random_g(unsigned long int x0, unsigned long int a, unsigned long int c); int main(){ unsigned long int x0, a, c, M; double x, y, s; int n, count_in, count_out; count_in = 0; count_out = 0; printf("x0: ", x0 ); scanf("%d", &x0); a = 61; c = 49; for(n = 0; n < 10000; n++){ /*乱数を発生*/ x = (random_g(x0, 4*a*n+1, c) / (double)4294967296)*Nx + 1; y = (random_g(x0, 4*a*n+1, c) / (double)4294967296)*Ny + 1; if(x >= 1 && y >= 1 && pow(x-1, 2) + pow(y-2, 2) <= 1) count_in++; else count_out++; } s = (double)count_in/(count_in + count_out)*Nx*Ny; printf("s = %f\n", s); printf("pi = %f\n", 2*s); return 0; } unsigned long int random_g(unsigned long int x0, unsigned long int a, unsigned long int c){ int i; x0 = (a*x0 + c); /*printf("%u\n", x0);*/ return x0; }

  • C言語なんですがうまくうごきません。

    X=1においてX^nをm回微分した値を求めるプログラムを作っているのですが、 何度も考えて訂正したりしてるのですが、どこが悪いのかわかりません 再帰関数を使ってます。 デバッグして調べてみてるのですが、うまくいってるようにみえるのですが、最後の値が0になります。 nを大きい値にすると、マイナスになったりするんです。 よくわかりません。 ちなみにnとmは正で、mは10までの数を入力します。 このプログラムの基本形を変えないで問題改善することはできるのでしょうか? わかる人いましたら教えてください。 #include <stdio.h> double differentiate(double n, int m); int main(void) { int a, b; printf("Input 2 number\n"); fflush(stdout); scanf("%d %d", &a, &b); printf( "a = %d\nb = %d\n", a, b ); printf("Answer = %d\n", differentiate(a, b)); return 0; } } double differentiate(double n, int m) { if(m == 1){ return n; }else{ return n * n-1 * differentiate(n-1, m-1); } }

  • 数値解析法

    このHeun法のプログラムをRunge-Kutta法にするにはどうしたらいいですか? #include <stdio.h> #include <math.h> double f1(double y) { return y; } double f2(double y) { return -4*y; } int main(){ double a=0; double b; int m=10; int n; double h; double x,y; int k; double e; double f; double k1,k2; printf("Heun法計算例:y=e^x, y=1/e^4x\n\n"); // y = e^x b = 1; for(n=100;n<=10000;n*=100){ h = (b-a)/n; printf("y' = y: h(=dx) = %.1e (y=e^x)\n",h); x = a; y = 1; for(k=0;k<=n;k++) { x = k*h; if(k%(n/m)==0) { f = exp(x); e = fabs(y-f); printf("x=%.2f, y=%f, e^x=%f er=%.0e\n",x,y,f,e); } // Heun's method k1 = h*f1(y); k2 = h*f1(y+k1); y += (k1+k2)/2; } }

  • 二分法のプログラムについて

    下の用なプログラムを作ったのですがどうしても正しい答えを導くことができません。自分でもいろいろ調べてみましたがわかりません。誰かご教授宜しくお願いします。 #include<stdio.h> #include<stdlib.h> #define MAX 10 int n , count; double c[MAX+1]; double a,b,e; void nyuuryoku(void) { int i; printf("nの入力>"); scanf("%d",&n); if(n>MAX){printf("最大次数を超えている");exit(1);} else if(n<0){printf("nが負");exit(2);} else{for(i=0;i<=n;i++){printf("係数の値>");scanf("%lf",&c[i]);} }} double f(double x) {double y; int i; y = c[0]; for(i=1;i<=n;i++){ y=y*x+c[i];} return y; } void hani(void){ printf("aの値>");scanf("%lf",&a); printf("bの値>");scanf("%lf",&b); printf("eの値>");scanf("%lf",&e); if(e<=0){printf("eが0または負"); exit(3);} if(f(a)==0){printf("%f",f(a)); exit(4);} if(f(b)==0){printf("%f",f(b)); exit(5);} if(f(a)*f(b)>0){printf("初期値異常"); exit(6);}} double nibun(void) {double c; if(b>a){ while(b-a>e){ count++; c=(a+b)/2; if(f(c)==0){ return c;} if(f(a)*f(c)<0){b=c;} if(f(b)*f(c)<0){a=c;} } return a;} if(a>b){ while(a-b>e){ count++; c=(a+b)/2; if(f(c)==0){ return c;} if(f(b)*f(c)<0){a=c;} if(f(a)*f(c)<0){b=c;} } return a;} } void syutsuryoku(double x){ printf("x=%lf\n",x); printf("f(x)=%lf\n",f(x)); printf("繰り返し回数=%d\n",count); } int main(void){ double ans; count=0; nyuuryoku(); hani(); ans = nibun(); syutsuryoku(ans); }

  • c プログラム 

    以下のプログラムは,第n項までのe^xのマクローリン展開をさせるものです. これを修正して,理論値と近似値の誤差がある値(自分で入力)になったときに,計算を終了させるにはどうしたらよいでしょうか.御教授いただければ幸甚 です. ---------------------------------------- #include <stdio.h> #include <math.h> int main(void) { int n; double x=1.0,y=1.0,e=1.0,err; int i; double f=1.0,p=1.0; printf("x="); scanf("%lf",&x); printf("n="); scanf("%d",&n); printf("Mclaurin展開によるn項までのexp(x)の\n n 理論値 近似値 誤差\n"); for(i=1;i<=n;i++){ f*=(double)i; p*=x; y+=p/f;近似値 e=exp(x);理論値 err=e-y;誤差 printf("%2d %12.8e %12.8e %12.8e\n",i,e,y,err); } return 0; }

  • プログラミングで・・・

    以下のプログラムにおいて,N回 s=--- s=---  ・  ・  ・ と表示させるにはどうしたらよいでしょか. ----------------------------------------- /*台形公式*/ #include<stdio.h> #include<math.h> double f(double x); int main(void){ /*Define variablest*/ int i,N; double a,b; double dx,xi,s,err; /*Function*/ printf("f=sin(x)+1/2cos10x\n"); /*Integral Field*/ a=0; b=M_PI; printf("a=0\n"); printf("b=pai\n"); /*Inputs data*/ printf("N="); scanf("%d",&N); /*width of integral's range*/ dx=(b-a)/(double)N; s=f(a)*0.5; /*for Loop*/ s=0; for(i=1;i<=N; i++){ xi=a+dx*(double)i; s=s+f(xi); } s=s+f(b)*0.5; s=s*dx; printf("s=%6.3e err=%6.3e\n",s,err); return 0; } double f(double x){ return sin(x)+1/2*cos(10*x); }

  • C言語のプログラム

    C言語で2つの微分方程式をEuler法、Heun法、Runge-Kutta法により求めるプログラムを作りたい。ただし、初期条件はx=0,y=1とする。また、間隔Δxを変えたときの解の変化を調べたい。 Euler法のプログラムはどうにか分かったのですが、Heun法、Runge-Kutta法のプログラムがわかりません。 Euler法のプログラム #include_<stdio.h> #include_<math.h> int_main(){ __double__a=0; __double__b; __int_____m=10; __int_____n; __double__h; __double__x,y; __double__dydx; __int_____k; __double__e; __double__f; __printf("オイラー法計算例:y=e^x,_y=1/e^4x\n\n"); __//_y_=_e^x __b_=_1; __for(n=100;n<=10000;n*=100){ ____h_=_(b-a)/n; ____printf("y'_=_y:_h(=dx)_=_%.1e_(y=e^x)\n",h); ____x_=_a;_y_=_1; ____for(k=0;k<=n;k++)_{ ______x_=_k*h; ______if(k%(n/m)==0)_{ ________f_=_exp(x); ________e_=_fabs(y-f); ________printf("x=%.2f,_y=%f,_e^x=%f_er=%.0e\n", ________x,y,f,e); ______} ______dydx_=_y; ______y_=_y_+_dydx*h; ____} __} __printf("\n"); __//_y_=_1/e^4x __b_=_4; __for(n=100;n<=10000;n*=100){ ____h_=_(b-a)/n; ____printf("y'_=_-4y:_h(=dx)_=_%.1e_(y=1/e^4x)\n",h); ____x_=_a;_y_=_1; ____for(k=0;k<=n;k++)_{ ______x_=_k*h; ______if(k%(n/m)==0)_{ ________f_=_exp(-4*x); ________e_=_fabs(y-f); ________printf("x=%.2f,_y=%f,_1/e^4x=%f_er=%.0e\n", ________x,y,f,e); ______} ______dydx_=_-4*y; ______y_=_y_+_dydx*h; ____} __} __return_0; } 分かる方がいましたら、回答よろしくお願いします。

  • 自然対数の底を求める

    自然対数の底eを、第n項まで求めるプログラムを作成するのですが #include <stdio.h> #include <math.h> int main(void) { double e, x, k; int n=2*k-1; if(k==0) return(0.0); return(pow((x-1)/(x+1), n)/n + add(x, k-1)); double x=5.0; x = add(x,20)*2.0; printf("f(5.0)=%f\n",x); }if ( x >= 1 ) { printf("整数を%d個入力してください--->", x); for ( k = 0 ; k <= x-1 ; k++) { scanf("%d", &a); s += a ; } printf("これらの数の合計は%dです\n", s); } else { printf("この値は不適です\n"); } return 0; このようなプログラムを作ってみたのですが自分でも理解できていない状態です。 e=1+1/1!+1/2!+1/3!…1/n! の再帰呼出しを使いたいのですが、アドバイスお願い致します。

  • C言語の質問です

    ニュートン法のプログラムを組んだのですがPAD図が描けなくて困ってます どなたか回答お願いします /* newton.c: ニュートン法 */ #include <stdio.h> // printf, fprintf, fgets, sscanf #include <math.h> // fabs double a;    //グローバル変数 double f(double x)  //fは関数f(x) { return x * x - a; //f(x)=x~2-a } double df(double x)  //dfはf(x)の導関数f’(x) { return 2 * x; //df(x)/dx = 2x } double newton(double x, double (*f)(double), double (*df)(double), double eps) { int n = 0;     //初期化 double x0, err;    //x0は初期値 printf("# n, x, err\n"); printf("%4d, % .15e\n", n, x); do { n++; x0 = x; x = x0 - f(x0) / df(x0); //切片でy=0、x=x1 err = fabs(x-x0); printf("%4d, % .8e, % .15e\n", n, x, err); } while (err >= eps); return x; } /*メインルーチン*/ int main(void) { double x, eps = 1.0e-10; //収束条件 char s[128]; fprintf(stderr, " a = "); fgets(s, 128, stdin); sscanf(s, "%lf", &a); while (a <= 0.0) { fprintf(stderr, "'a' には正の数を入れてください。\n"); fprintf(stderr, " a = "); fgets(s, 128, stdin); sscanf(s, "%lf", &a); } x = (a + 1.0) / 2.0; x = newton(x, f, df, eps);   //ニュートン法の実装 printf("\n# √(%e) = % .15e\n", a, x); return 0; }

  • C言語 ニュートン法

    何をすればいいかよくわからなくて躓きました わかる方どうかお願いします 問 以下に示すプログラムで、x1 = x0 - f (x0) / f '(x0)を用いて、方程式の左辺関数 f (x) (= x 2 - a ) およびその導関数 f '(x) を独立させろ。 入力変数 a はグローバル変数としてよい。 導関数 f '(x)はプログラム内ではdfと表記することとする。 また、適切なコメントも追加すること。 /* newton.c: ニュートン法 */ #include <stdio.h> // printf, fprintf, fgets, sscanf #include <math.h> // fabs double newton(double x, double (*f)(double), double (*df)(double), double eps) {   int n;   double a = 0, x, x0, err, eps = 1.0e-10;   printf("# n, x, err\n");   printf("%4d, % .15e\n", n, x);   do {     n++;     x0 = x;     x = (x0 + a/x0) / 2;     err = fabs(x-x0);     printf("%4d, % .8e, % .8e\n", n, x, err);   } while (err > eps);     return x; } int main(void) {    double a = 0, x, x0, err, eps = 1.0e-10;    char s[128];   fprintf(stderr, " a = "); fgets(s, 128, stdin); sscanf(s, "%lf", &a);   while (a <= 0.0) {    fprintf(stderr, "'a' には正の数を入れてください。\n");   fprintf(stderr, " a = "); fgets(s, 128, stdin); sscanf(s, "%lf", &a);   }   x = (a + 1.0) / 2.0;   printf("\n# sqrt(%e) = % .15e\n", a, x);    return 0; }