質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.35%
C

C言語は、1972年にAT&Tベル研究所の、デニス・リッチーが主体となって作成したプログラミング言語です。 B言語の後継言語として開発されたことからC言語と命名。そのため、表記法などはB言語やALGOLに近いとされています。 Cの拡張版であるC++言語とともに、現在世界中でもっとも普及されているプログラミング言語です。

関数

関数(ファンクション・メソッド・サブルーチンとも呼ばれる)は、はプログラムのコードの一部であり、ある特定のタスクを処理するように設計されたものです。

Q&A

解決済

1回答

5046閲覧

C言語での矩形近似によるフーリエ級数展開が上手くいきません...

ty59rhgaeraoih

総合スコア1

C

C言語は、1972年にAT&Tベル研究所の、デニス・リッチーが主体となって作成したプログラミング言語です。 B言語の後継言語として開発されたことからC言語と命名。そのため、表記法などはB言語やALGOLに近いとされています。 Cの拡張版であるC++言語とともに、現在世界中でもっとも普及されているプログラミング言語です。

関数

関数(ファンクション・メソッド・サブルーチンとも呼ばれる)は、はプログラムのコードの一部であり、ある特定のタスクを処理するように設計されたものです。

0グッド

1クリップ

投稿2020/07/02 00:58

編集2020/07/02 23:40

前提・実現したいこと

次のような三角波、正弦波の矩形近似によるフーリエ級数展開を考えています。コメント文で示した部分(3か所)をどう記述すればよいかご教示いただければ幸いです。

f(t) = 1/2(1 - 2t/T) -2/T < t < T/2
1/2 t = ± T/2
f(t) = 1/2{ 1 + cos(2πt / T) -T/2 ≦ t ≦ T/2

該当のソースコード

C

1#include <stdio.h> 2#include <math.h> 3#define M_PI 3.14159265 4#define MM 300 //フーリエ級数展開の打ち切り次数 5#define Tp 1.0 //周期波形の周期 6 7double aa[MM+1], bb[MM+1] ; //フーリエ展開係数 8int NN = 3 * MM ; //数値積分の分割数 9 10double ft(double t) 11{ 12 double zero = 1.0e-8 ; 13 while ( t < -Tp/2.0 ){ t = t+Tp ;if( t > -Tp/2.0 ) break ;} 14 while ( t > Tp/2.0 ){ t = t-Tp ;if( t < Tp/2.0 ) break ;} 15 16 17 if( fabs( t+Tp/2.0 ) < zero || fabs( t-Tp/2.0) < zero ) return 0.5 ; 18 else return 0.5 - t/Tp; //ここは間違っている可能性あり① 19 20 21 return 0.5 + 0.5 * cos((2.0 * M_PI * t) / Tp); //ここは間違っている可能性あり② 22} 23 24double ak_dt(int k, double t) 25{ 26 return ft(t) * cos( (double)k * 2.0 * M_PI / Tp * t ) ; 27} 28 29double fcoeff_ak( int k ) 30{ /* 関数f(t)のフーリエ展開級数 a_kを数値積分で求める */ 31 double t, i, ak = 0.0, dt = Tp/(double)NN ; 32 for( i = 0 ; i < NN ; i ++ ) { 33 t = -Tp/2.0 + (double)i * dt ; 34 ak = ak + sin(t) / t ; // ここがよくわかっていません。③ 35 } 36 37 return ak ; 38} 39 40double bk_dt(int k, double t) 41{ 42 return ft(t) * sin( (double)k * 2.0 * M_PI / Tp * t ) ; 43} 44 45double fcoeff_bk(int k) 46{ 47 double t, i, bk = 0.0, dt = Tp/(double)NN ; 48 for( i = 0 ; i < NN ; i ++ ) { 49 t = -Tp/2.0 + (double)i * dt ; 50 bk = bk + sin(t) / t ; // 同様にここがわかりません。④ 51 } 52 return bk ; 53} 54 55double fxfreq( double *aa, double *bb, int M, double t) 56{ 57 int k ; double ff = 0.0 ; 58 for ( k = 0 ; k <= MM ; k++ ) ff = *aa*cos(((double)k * 2.0 * M_PI * t)/ Tp ) + *bb*sin(((double)k * 2.0 * M_PI * t)/Tp ) ;//ここは合っているのかどうかわかりません。⑤ 59 return ff; 60} 61 62int main() 63{ 64 int i, k, n=2 ; double t, fh ; 65 int KK = 1000 ; 66 67 aa[0] = fcoeff_ak(0)/2.0 ; bb[0] = 0.0 ; 68 for (k=1; k<=MM; k++) { 69 aa[k] = fcoeff_ak(k) ; bb[k] = fcoeff_bk(k) ; 70 } 71 72 73 for (i=0; i<= n*KK; i++) { 74 t = -Tp*(double)n/2.0 + Tp/(double)KK * (double)i ; 75 fh = fxfreq( aa, bb, MM, t ) ; 76 printf(" %10.8f %10.8f %10.8f\n", t, ft(t), fh ); 77 } 78 return 0; 79}

試したこと

実行してみましたが、正しい結果が得られませんでした。実行結果の一部分を掲載いたします。ft(t)は正しいと思います。fhもft(t)と近い値になるはずだと思うのですが、同じ値を行ったり来たりしています。
-0.00900000 0.50900000 -137.14071854
-0.00800000 0.50800000 -359.03903645
-0.00700000 0.50700000 359.03904488
-0.00600000 0.50600000 137.14070490
-0.00500000 0.50500000 -443.79666119
-0.00400000 0.50400000 137.14071399
-0.00300000 0.50300000 359.03903926
-0.00200000 0.50200000 -359.03904207
-0.00100000 0.50100000 -137.14070945
-0.00000000 0.50000000 443.79666119
0.00100000 0.49900000 -137.14070945
0.00200000 0.49800000 -359.03904207
0.00300000 0.49700000 359.03903926
0.00400000 0.49600000 137.14071399
0.00500000 0.49500000 -443.79666119
0.00600000 0.49400000 137.14070490
0.00700000 0.49300000 359.03904488
0.00800000 0.49200000 -359.03903645
0.00900000 0.49100000 -137.14071854
0.01000000 0.49000000 443.79666119
0.01100000 0.48900000 -137.14070035
0.01200000 0.48800000 -359.03904769
0.01300000 0.48700000 359.03903364
0.01400000 0.48600000 137.14072308
0.01500000 0.48500000 -443.79666119

補足情報(FW/ツールのバージョンなど)

Visual Studio Code (Version 1.46)を使用しています。

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

maai

2020/07/02 14:45

フーリエ級数展開の定義とソースコード中の変数の意味を書いてください(特にMM) そもそもフーリエ級数展開の求め方が間違っているのでは…?
ty59rhgaeraoih

2020/07/02 23:41

追記いたしました。①~⑤の文が穴埋めになっている形式なのでそれ以外の部分は間違っていません。宜しくお願い致します。
Penpen7

2020/07/03 00:34 編集

本質的な部分ではありませんが、math.hをインクルードしているため、#define M_PI 3.14159265 の定義は必要ありません。 各数学定数を使用するために、 #define _USE_MATH_DEFINES #include <math.h> とする必要はあるかもしれませんが。 double fcoeff_bk, fcoeff_akのiはループ変数なのでdoubleではなくintでは?
Penpen7

2020/07/03 05:46 編集

f(t) = 1/2(1 - 2t/T) -2/T < t < T/2    1/2 t = ± T/2 と f(t) = 1/2{ 1 + cos(2πt / T) -T/2 ≦ t ≦ T/2 の2種類の関数をフーリエ級数展開したいという意味ですかね?(3つ並んでいてわかりづらかったです) else return 0.5 - t/Tp; //ここは間違っている可能性あり① まではあってると思いますが、 return 0.5 + 0.5 * cos((2.0 * M_PI * t) / Tp); //ここは間違っている可能性あり② は別の関数だからいらないのでは?(そもそもこのreturn文は実行されることはなく不要です)
Penpen7

2020/07/03 01:04 編集

学校の課題でしょうから、私からはヒントだけお出しします。 ak = ak + sin(t) / t ;とbk = bk + sin(t) / t ;は間違っています。 ここでは, フーリエ級数展開の係数を求めますが、係数が積分によって定義されているために、数値積分を行います。 数値積分には、区分求積、シンプソンの式、台形公式等がありますが、手っ取り早く実装できるのは区分求積です。ご質問のコードで微小区間の幅をかけていないということは、積分を単なる足し算だと考えていませんか? もう一度、「区分求積」とフーリエ級数展開の「係数」の定義をみてください。 https://gihyo.jp/dev/serial/01/java-calculation/0072 https://mathtrain.jp/fourierseries
ty59rhgaeraoih

2020/07/03 06:25

ご指導ありがとうございます。 おっしゃる通り、三角波と正弦波の2種類の関数をフーリエ級数展開するプログラムです。 t = -Tp/2.0 + (double)i * dt ; が何をしているのか分かりません…
Penpen7

2020/07/03 06:58 編集

t = -Tp/2.0 + (double)i * dt ;は i=0の時, t=-Tp/2.0 i=NNの時, t=-Tp/2.0+NN*dt=Tp/2.0 (∵dt=Tp/NN) となります。すなわち、-Tp/2.0<=t<Tp/2.0の範囲を最初-Tp/2.0からdtずつ増やしていきながら、いずれTp/2.0にたどり着くと考えてください。(Tp/2.0の方に=はつかないので完全にTp/2.0にはなりませんが簡単のため) なぜこのようなことをしているのかというと、区分求積では関数f(t)をt=aからt=bまで積分するとすると、f(t)*dt=(関数の値)*(幅)をtをaからbまでdt(微小幅)ずつ「ずらしながら」足し合わせていく操作をするからです。 関数の値を求めるためにはtの値が必要のため、tの値をその文で計算しています。
ty59rhgaeraoih

2020/07/15 02:51

回答ありがとうございます。遅れてしまい申し訳ございません。あれからずっと考えていたのですが、 矩形近似のx_i-x_i-1(横)に当たる部分はdtなのでしょうか。また、y_i-1(縦)にあたる部分はなんでしょうか。 そう考えて ak = ak+ak_dt(i,t) * dt ; としてみましたが、うまくいきませんでした。 また、main関数内の for (k=1; k<=MM; k++) { aa[k] = fcoeff_ak(k) ; bb[k] = fcoeff_bk(k) ; /* フーリエ展開係数 a_k, b_k を求める */ ; } の部分でなぜfor文を使っているのか分かりません。 回答宜しくお願い致します。
Penpen7

2020/07/15 03:13 編集

x_i-x_i-1が積分の方向(dx)を言っているのだとすれば長方形の横幅にあたります。このソースコードであればdtにあたりますね。 y_{i-1}は被積分関数に対応します。先日コメントした参考URL(https://mathtrain.jp/fourierseries)のa_nの式であれば、被積分関数は2/T*f(x)*cos(2*pi*n*x/T)ですのでyにはこれを当てはめることになります。 for (k=1; k<=MM; k++) { aa[k] = fcoeff_ak(k) ; bb[k] = fcoeff_bk(k) ; } は全てのkに対して、結果を得たいためにfor文を使っています。 (Tp/kが周期に対応)
Penpen7

2020/07/15 03:08 編集

ak = ak+ak_dt(i,t) * dt ;であっていると思います。 同様にbk = bk + bk_dt(i,t) * dt;としましたか?
ty59rhgaeraoih

2020/07/15 03:16

回答ありがとうございます。bkの部分も変更しました。コンパイルでエラーは出ませんが、実行しても何も出力されません… それと、fxfreqと名前を付けた関数内の for ( k = 0 ; k <= MM ; k++ ) ff = *aa*cos(((double)k * 2.0 * M_PI * t)/ Tp ) + *bb*sin(((double)k * 2.0 * M_PI * t)/Tp ) ; は正しいですか? 回答宜しくお願い致します。
Penpen7

2020/07/15 03:28 編集

ソースコードを質問の編集でもう一度貼り直していただけませんか? 自分の手で動かして確認したいので... fxfreqは何をする部分でしょうか?おそらく求めたフーリエ係数からf(t)を再現する作業をしているのですかね? (fhとftが同じになるはずとのことからそのように予想できますが)
Penpen7

2020/07/15 03:31 編集

もしその意味なら*aaはaa[k], *bbはbb[k]とするのが正しいです。 配列を引数に取りたいときはポインタを使うことがあります。
ty59rhgaeraoih

2020/07/15 04:29

かしこまりました。よろしくお願いします。 fxfreqはフーリエ展開係数 a_k,b_kを求めています。
ty59rhgaeraoih

2020/07/15 04:29

#include <stdio.h> #include <math.h> #define M_PI 3.14159265 #define MM 300 #define Tp 1.0 double aa[MM+1], bb[MM+1] ; int NN = 3 * MM ; double ft(double t) { double zero = 1.0e-8 ; while ( t < -Tp/2.0 ){ t = t+Tp ;if( t > -Tp/2.0 ) break ;} while ( t > Tp/2.0 ){ t = t-Tp ;if( t < Tp/2.0 ) break ;} if( fabs( t+Tp/2.0 ) < zero || fabs( t-Tp/2.0) < zero ) return 0.5 ; else return 0.5 - t/Tp; return 0.5 + 0.5 * cos((2.0 * M_PI * t) / Tp); } double ak_dt(int k, double t) { return ft(t) * cos( (double)k * 2.0 * M_PI / Tp * t ) ; } double fcoeff_ak( int k ) { double t, i, ak = 0.0, dt = Tp/(double)NN ; for( i = 0 ; i < NN ; i ++ ) { t = -Tp/2.0 + (double)i * dt ; ak = ak + ak_dt(i,t) * dt ; } return ak ; } double bk_dt(int k, double t) { /* フーリエ展開係数 b_k を求めるときの被関数関数 */ return ft(t) * sin( (double)k * 2.0 * M_PI / Tp * t ) ; } double fcoeff_bk(int k) { /* 関数 f(t) のフーリエ展開係数 b_k を数値積分で求める */ double t, i, bk = 0.0, dt = Tp/(double)NN ; // 初期化 bk=0.0 を忘れずに! for( i = 0 ; i < NN ; i ++ ) { t = -Tp/2.0 + (double)i * t ; bk = bk + bk_dt(i,t) * dt ; // 矩形近似,または台形近似による積分 } return bk ; } double fxfreq( double *aa, double *bb, int M, double t) { /* M 次までのフーリエ展開係数を用いて,ある t 点での f(t) を計算する */ int k ; double ff = 0.0 ; for ( k = 0 ; k <= MM ; k++ ) ff = aa[k]*cos(((double)k * 2.0 * M_PI * t)/ Tp ) + bb[k]*sin(((double)k * 2.0 * M_PI * t)/Tp ) ; return ff; } int main() { int i, k, n=2 ; double t, fh ; int KK = 1000 ; /* 1周期分のグラフ描写の点数 */ aa[0] = fcoeff_ak(0)/2.0 ; bb[0] = 0.0 ; /* フーリエ展開係数 a_0, b_0 を求める */ ; for (k=1; k<=MM; k++) { aa[k] = fcoeff_ak(k) ; bb[k] = fcoeff_bk(k) ; /* フーリエ展開係数 a_k, b_k を求める */ ; } /* 求めたフーリエ展開係数から n 周期分の波形 fh を再構成・描写する */ for (i=0; i<= n*KK; i++) { t = -Tp*(double)n/2.0 + Tp/(double)KK * (double)i ; fh = fxfreq( aa, bb, MM, t ) ; printf(" %10.8f %10.8f %10.8f\n", t, ft(t), fh ); } return 0; }
guest

回答1

0

ベストアンサー

引数を間違えている箇所等がありました。(見比べてください)
デバッグのため、ソースコードを少し改変しています。

C

1#include <math.h> 2#include <stdio.h> 3#define MM 100 4 5const double Tp = 1.0; 6const int NN = 3*MM; 7const double dt = Tp / (double)NN; 8double aa[MM + 1], bb[MM + 1]; 9 10double ft(double t) 11{ 12 double zero = 1.0e-8; 13 while (t < -Tp / 2.0) { 14 t = t + Tp; 15 if (t > -Tp / 2.0) 16 break; 17 } 18 while (t > Tp / 2.0) { 19 t = t - Tp; 20 if (t < Tp / 2.0) 21 break; 22 } 23 24 if (fabs(t + Tp / 2.0) < zero || fabs(t - Tp / 2.0) < zero) 25 return 0.5; 26 else 27 return 0.5 - t / Tp; 28} 29 30double ak_dt(int k, double t) 31{ 32 return ft(t) * cos((double)k * 2.0 * M_PI / Tp * t); 33} 34 35double fcoeff_ak(int k) 36{ 37 double ak = 0.0; 38 for (int i = 0; i < NN; i++) { 39 double t = -Tp / 2.0 + (double)i * dt; 40 ak += ak_dt(k, t) * dt; 41 } 42 ak *= 2 / Tp; 43 return ak; 44} 45 46double bk_dt(int k, double t) 47{ /* フーリエ展開係数 b_k を求めるときの被関数関数 */ 48 return ft(t) * sin((double)k * 2.0 * M_PI / Tp * t); 49} 50 51double fcoeff_bk(int k) 52{ /* 関数 f(t) のフーリエ展開係数 b_k を数値積分で求める */ 53 double bk = 0.0; 54 for (int i = 0; i < NN; i++) { 55 double t = -Tp / 2.0 + (double)i * dt; 56 bk += bk_dt(k, t) * dt; // 矩形近似,または台形近似による積分 57 } 58 bk *= 2 / Tp; 59 return bk; 60} 61 62double fxfreq(double* aa, double* bb, int M, double t) 63{ /* M 次までのフーリエ展開係数を用いて,ある t 点での f(t) を計算する */ 64 double ff = 0.0; 65 for (int k = 0; k <= MM; k++) 66 ff += aa[k] * cos(((double)k * 2.0 * M_PI * t) / Tp) + bb[k] * sin(((double)k * 2.0 * M_PI * t) / Tp); 67 return ff; 68} 69 70int main() 71{ 72 const int KK = 1000; /* 1周期分のグラフ描写の点数 */ 73 74 aa[0] = fcoeff_ak(0) / 2.0; 75 bb[0] = 0.0; /* フーリエ展開係数 a_0, b_0 を求める */ 76 for (int k = 1; k <= MM; k++) { 77 aa[k] = fcoeff_ak(k); 78 bb[k] = fcoeff_bk(k); /* フーリエ展開係数 a_k, b_k を求める */ 79 } 80 81 /* 求めたフーリエ展開係数から n 周期分の波形 fh を再構成・描写する */ 82 for (int i = 0; i <= KK; i++) { 83 double t = -Tp * 0.5 + Tp / (double)KK * (double)i; 84 double fh = fxfreq(aa, bb, MM, t); 85 printf(" %10.8f %10.8f %10.8f\n", t, ft(t), fh); 86 } 87 return 0; 88}

結果

投稿2020/07/15 05:23

Penpen7

総合スコア698

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.35%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問