Download : sample-001.c
/*
* 2019/10/18 sample-001.c
*/
/*
* 二分法による代数方程式の解法
*
* 利用方法
* コンパイル
* cc -I ~/c/include -c sample-001.c
* リンク
* cc -o sample-001.exe sample-001.c -lm
* 実行
* ./sample-001.exe
*/
#include <stdio.h>
/*
* ε : EPSILON (誤差限界:これより小さい時は同じと見做す)の定義
*/
#define EPSILON 0.000001 /* define でε(EPSILON)を定義 */
/* 計算結果の精度の尺度になる */
/* 残念ながら「0」にはできない */
/* 今回は 0.00001 だが、もちろん、
0.01 とか 0.00000001 にしてもよい */
/*
* f(x) = x^3+2x^2-x-2
*/
double f(double x) {
return x * x * x + 2 * x * x - x - 2;
}
/*
* 二分法で、方程式 f(x)=0 の解 (x0) を求める
*
* min < x0 < max であり f(min)<0, 0<f(max) を仮定する
*
* solove_binary ( double min, double max )
*
*/
double solve_binary ( double min, double max ) {
/*
f(min) < 0 < f(max) (仮定) なので、答えは [min,max] の中にある
max-min < EPSILON なら、十分に精度が得られたとする
そうでなければ、中点 (mid = (min+max)/2 ) での
f の値 ( f(mid) ) の符号をみて、
正なら、答えは [min,mid] そうでなければ [mid,max] にある
*/
if ( (max-min) < EPSILON ) { /* 十分に狭い範囲にできた */
return (max+min)/2.0; /* 中点を答えにする */
} else if ( f((max+min)/2.0) > 0 ) { /* まだ大きい */
return solve_binary ( min, (max+min)/2.0 );
} else {
return solve_binary ( (max+min)/2.0, max );
}
}
/*
* main 関数
*/
int main ( void ) {
printf ( "方程式 f(x)=x^3+2x^2-x-2=0 の解を、区間[0,3]から探す\n" );
printf ( "f(0)=-2 < 0, f(3)=40 > 0 なので、[0,3] の中に解がある\n" );
printf ( "二分法で得られた答えは : %f になりました。\n",
solve_binary ( 0.0, 3.0 )
);
printf ( "答えを方程式の x に代入すると %f なので、ほぼ答えに近い事が分ります\n",
f( solve_binary ( 0.0, 3.0 ) ),
);
return 0;
}
$ ./sample-001.exe 関数 f(x)=x^2 を区間[0,1]で数値定積分する。 リーマンの定義に従って計算した答えは : 0.332833になりました。 解析的な計算の結果は 1/3 なので、誤差は -0.000500 になり、ほぼ答えに近い事がわかります $
Download : sample-002.c
/*
* 2019/10/18 sample-002.c
*/
/*
* リーマン積分
*
* 利用方法
* コンパイル
* cc -I ~/c/include -c sample-002.c
* リンク
* cc -o sample-002.exe sample-002.c
* 実行
* ./sample-002.exe
*/
/*
*
* 定積分
* \int_0^1 x^2 dx
* を「数値的」に解く
*
*/
#include <stdio.h>
/*
*
*/
#define FRACTIONAL 1000 /* 区間の等分数 */
/*
* f(x)=x^2
*/
double f(double x) {
/*
* 引数 x に対して、x の 二乗を値として返す関数
*/
return x * x;
}
/*
reman_sum ( int i, int n, double min, double max )
S_i 〜 S_{n^1} の和を計算する
*/
double reman_sum ( int i, int n, double min, double max ) {
if ( i < n ) { /* まだ計算が必要 */
/* 注目している短冊の面積と、残りの部分の面積の和を計算する */
return
reman_sum ( i + 1, n, min, max )
+
f(min+i*(max-min)/n)*(max-min)/n;
} else { /* もう、全て計算した */
return 0.0; /* 残る結果は 0 になる */
}
}
/*
* リーマン積分
*
* 関数の積分値が、小さな幅の短冊の面積の和で近似できる事を利用
*
* solove_reaman ( double min, double max )
*
*/
double solve_reman ( double min, double max ) {
/*
min から max までを積分
基本は reman_sum に任せる
*/
return reman_sum ( 0, FRACTIONAL, min, max );
}
/*
* main 関数
*/
int main ( void ) {
printf ( "関数 f(x)=x^2 を区間[0,1]で数値定積分する。\n" );
printf ( "リーマンの定義に従って計算した答えは : %f になりました。\n",
solve_reman ( 0.0, 1.0 )
);
printf ( "解析的な計算の結果は 1/3 なので、誤差は %f になり、ほぼ答えに近い事がわかります\n",
solve_reman ( 0.0, 1.0 ) - 1.0/3.0
);
return 0;
}
$ ./sample-002.exe 関数 f(x)=x^2 を区間[0,1]で数値定積分する。 リーマンの定義に従って計算した答えは : 0.332833 になりました。 解析的な計算の結果は 1/3 なので、誤差は -0.000500 になり、ほぼ答えに近い事がわかります $
/*
* 課題 CNAME-02
*
* 20190920 20190920-02-QQQQ.c
*
* printf の書式指定
* 整数値の出力書式は「%d」
* 浮動小数点数値の出力書式は「%f」
* printf の柔軟性を活用してほしい
*
*/
#include <stdio.h>
/*
*
*/
/*
* main
*/
int main( int argc, char *argv[] )
{
int ia = 7;
int ib = 15;
double da = 3.14;
double db = 1.59;
printf ( "%d + %d = %d\n", ia, ib, ia + ib ); /* 整数型の和 */
printf ( "%f / %f = %f\n", da, db, da / db ); /* 浮動小数点数の商 */
return 0;
}
/*
* 課題 CNAME-03
*
* 20190920 20190920-03-QQQQ.c
*
* scanf の書式指定
* 整数値の入力書式は「%d」
* 浮動小数点数値の入力書式は「%lf」
* !!! scanf は、printf と同程度に柔軟性があるが、これの利用は非推奨
* !!! => scanf での入力は、単体で行う事を奨励
* 入力結果は、変数に保存すが、その変数の名前の前に「&」を付ける
* !!! 「お呪い」として、当分は、このまま
*/
#include <stdio.h>
/*
*
*/
/*
* main
*/
int main( int argc, char *argv[] )
{
int i; /* キーボードから入力された整数値を保持する整数型変数 */
double d; /* キーボードから入力された浮動小数点数値を保持する浮動小数点数型変数 */
printf ( "整数値をキーボードから入力します : " );
scanf ( "%d", &i );
printf ( "入力された整数値は %d でした。\n", i );
printf ( "浮動小数点数値をキーボードから入力します : " );
scanf ( "%lf", &d );
printf ( "入力された浮動小数点数値は %f でした。\n", d );
return 0;
}
#include <stdio.h>
/*
数列 1/n が、0 に収束する様子をみたい
*/
int main(int argc, char *argv[]) {
double n;
for ( n = 1.0; n < 1000.0; n = n + 1.0 ) {
printf ( "1/%f = %20.18f\n", n, 1.0/n );
}
return 0;
}
#include <stdio.h>
/*
級数 Σ 1/n が
*/
int main(int argc, char *argv[]) {
double n; /* for 構文の制御変数としてふるまう */
double sum = 0.0; /* 変数の初期化 : 変数の宣言と同時に代入文を実行する */
for ( n = 1.0; n < 1000.0; n = n + 1.0 ) {
printf ( "1/%f = %20.18f\n", n, 1.0/n ); /* 数列の表示 */
sum = sum + 1.0/n; /* 和を計算 */
printf ( "sum = %20.18f\n", sum );
}
printf ( "sum = %20.16f\n", sum );
return 0;
}
#include <stdio.h>
/*
級数 Σ 1/n が
*/
int main(int argc, char *argv[]) {
double n; /* for 構文の制御変数としてふるまう */
double sum; /* 変数の初期化 : 変数の宣言と同時に代入文を実行する */
n = 0.0;
for ( sum = 0.0; n < 1000.0; sum = sum + 1.0/n ) {
/*
あまり、よくない例 (プログラムとしては、p-002.c と同様に趣旨 */
せっかく for 構文で、制御変数の操作をまとまられるのに、
その「マナー」を守っていない
読みずらい
間違いもふえる
=> あまり、利用しない方がよいが、「できる事」は知っておく必要がある
*/
printf ( "1/%f = %20.18f\n", n, 1.0/n ); /* 数列の表示 */
printf ( "sum = %20.18f\n", sum );
n = n + 1.0;
}
printf ( "sum = %20.16f\n", sum );
return 0;
}
#include <stdio.h>
#include <math.h> /* sqrt のような数学関数を利用す場合に必要 */
/*
代数方程式
a x^2 + b x + c = 0
=> 解析的に解ける
解の公式を利用すればよい
!! 解が異なる二実根を持つことを仮定する
*/
/*
solve : 二次方程式の係数 a, b, c を与えると、その二つの実根を表示する
根の計算には、解の公式を使う
-b +- sqrt(b^2 - 4ac)
x = ---------------------
2a
sqrt はあらかじめ C の数値ライブラリに含まれている関数で、
double 値の平方根を計算する
注意 : sqrt 関数の引数は、非負の double 値
*/
void solve ( double a, double b, double c ) {
/* 解が実根である事を「仮定」しているので、
判別式 D = b^2 - 4ac は非負として、プログラムを書いている */
/* 本当ならば、判別式の負号によって、場合分けが必要 */
printf ( "x = %f\n", ( -b + sqrt ( b * b - 4.0 * a * c ) ) / ( 2.0 * a ) );
printf ( "x = %f\n", ( -b - sqrt ( b * b - 4.0 * a * c ) ) / ( 2.0 * a ) );
}
int main(int argc, char *argv[]) {
double a;
double b;
double c;
printf ( "二次方程式の係数を入力します\n" );
printf ( "a=" );
scanf ( "%lf", &a );
printf ( "b=" );
scanf ( "%lf", &b );
printf ( "c=" );
scanf ( "%lf", &c );
solve ( a, b, c );
return 0;
}
#include <stdio.h>
#include <math.h> /* sqrt のような数学関数を利用す場合に必要 */
/*
代数方程式
a x^2 + b x + c = 0
=> 解析的に解ける
=> あえて、数値的に解く
*/
double f(double x, double a, double b, double c ) {
return a * x * x + b * x + c; /* f(x) = ax^2 + bx + c */
}
/*
solve : 二次方程式の係数 a, b, c を与えると、その二つの実根を表示する
関数 (f(x)=a x^2 + b x + c) の連続性を利用して、二分法で解く
関数の連続性
=> 中間値の定理
関数 f(x) が区間 [a,b] で連続で定義されていて
f(a) < y < f(b)
時、
ある c ( a <= c <= ) が存在し、f(c) = y
ほしいのは、f(x) が区間 [a,b] で連続で、
f(a) <= 0 <= f(b)
と仮定して、
f(c) = 0
となる c が、「方程式の解」になる
!!! 中間値の定理が、代数法手式の解の存在を保証する定理になっている
!!! => 存在定理
*/
void solve ( double a, double b, double c ) {
/* 話を簡単するために、答えが、0 と 10 の間にある事を仮定する */
/* 答えも一つしかださない ( もう一つの答えは、みつかった一つの答えを利用して
別に求めることにする */
/*
[方針]
解が区間 [a,b] にある事がわかっているときに、
[a,b] より狭い区間に解があるようにしながら、
区間の幅をへらす
*/
double left = 0.0;
double right = 10.0;
/* 仮定で、答えは、区間 [0.0,10.0] の中にある */
/* f(0.0) < 0 < f(10.0) */
/* この時点で解は、[0.0,10.0] の中にあるので、
この区間を半分して、そのどちらかにあるかを判定する */
/* 区間の半分の点での f(x) の値の負号で、
半分のどちらの区間にあるかを判定する事ができる */
double center;
while ( right - left > 0.00001 ) {
center = ( left + right ) / 2.0;
if ( f(center,a,b,c) > 0.0 ) { /* 答えが conter の左にあった */
right = center; /* 区間の右側(right)を修正(centerに)できる */
} else {
left = center;
}
}
printf ( "解は区間 [%f,%f] にある\n", left, right );
printf ( "おそらく解は %f の付近\n", (left+right)/2.0 );
}
int main(int argc, char *argv[]) {
double a;
double b;
double c;
printf ( "二次方程式の係数を入力します\n" );
printf ( "a=" );
scanf ( "%lf", &a );
printf ( "b=" );
scanf ( "%lf", &b );
printf ( "c=" );
scanf ( "%lf", &c );
solve ( a, b, c );
return 0;
}
#include <stdio.h>
#include <math.h> /* sqrt のような数学関数を利用す場合に必要 */
/*
代数方程式
a x^5 + b x + c = 0
=> 解析的に解ける
=> あえて、数値的に解く
a=1
b=-2
c=1
の時
x = 1.0 が解
f(0) = -1 < 0.0
f(10) > 0.0
*/
double f(double x, double a, double b, double c ) {
return a * x * x * x * x * x + b * x + c; /* f(x) = ax^5 + bx + c */
}
/*
solve : 二次方程式の係数 a, b, c を与えると、その二つの実根を表示する
関数 (f(x)=a x^5 + b x + c) の連続性を利用して、二分法で解く
関数の連続性
=> 中間値の定理
関数 f(x) が区間 [a,b] で連続で定義されていて
f(a) < y < f(b)
時、
ある c ( a <= c <= ) が存在し、f(c) = y
ほしいのは、f(x) が区間 [a,b] で連続で、
f(a) <= 0 <= f(b)
と仮定して、
f(c) = 0
となる c が、「方程式の解」になる
!!! 中間値の定理が、代数法手式の解の存在を保証する定理になっている
!!! => 存在定理
*/
void solve ( double a, double b, double c ) {
/* 話を簡単するために、答えが、0 と 10 の間にある事を仮定する */
/* 答えも一つしかださない ( もう一つの答えは、みつかった一つの答えを利用して
別に求めることにする */
/*
[方針]
解が区間 [a,b] にある事がわかっているときに、
[a,b] より狭い区間に解があるようにしながら、
区間の幅をへらす
*/
double left = 0.0;
double right = 10.0;
/* 仮定で、答えは、区間 [0.0,10.0] の中にある */
/* f(0.0) < 0 < f(10.0) */
/* この時点で解は、[0.0,10.0] の中にあるので、
この区間を半分して、そのどちらかにあるかを判定する */
/* 区間の半分の点での f(x) の値の負号で、
半分のどちらの区間にあるかを判定する事ができる */
double center;
while ( right - left > 0.00001 ) {
center = ( left + right ) / 2.0;
if ( f(center,a,b,c) > 0.0 ) { /* 答えが conter の左にあった */
right = center; /* 区間の右側(right)を修正(centerに)できる */
} else {
left = center;
}
}
printf ( "解は区間 [%f,%f] にある\n", left, right );
printf ( "おそらく解は %f の付近\n", (left+right)/2.0 );
}
int main(int argc, char *argv[]) {
double a;
double b;
double c;
printf ( "五次方程式の係数を入力します\n" );
printf ( "a=" );
scanf ( "%lf", &a );
printf ( "b=" );
scanf ( "%lf", &b );
printf ( "c=" );
scanf ( "%lf", &c );
solve ( a, b, c );
return 0;
}
[前回の内容]
浮動小数点数 (double 型)
「実数」に対応したデータ型 (cf. int 型は「整数」に対応)
「小数点を含む数」を扱う
構文
型を示すキーワード : double ( cf. 整数型の場合は int )
=> 変数の宣言の時
例:
double v; /* 浮動小数点数型の変数の宣言 */
v = 1.23: /* 小数点を含む値の代入 */
入出力
出力 : printf ( "%f", v ); /* 浮動小数点数型の値の出力 */
=> 「%f」だと小数点以下の表示の長さが足りない事がある
=> 「%20.15f」のように、表示の形式を指定して、より情報量の多い表示が可能
!!! 表示の桁数が少ないと、表示可能でないところの数値は、四捨五入されて表示される
!!! printf ( "%3.1f", v );
!!! => 1.2 になる
!!! 出力の指定が不十分だと、「実際の値と、表示される値が対応していない」可能性がある
!!! => 十進法(人間が普段扱っている形)と 二進法(コンピュータが普段扱っている形)の違いも関係する
!!! 1/5 => 0.2 (十進の場合は有限小数 / 二進法の場合は、無限、循環小数)
!!! => 本質的に「人間がとらえる『数』」と「実際にコンピュータが保持している『数』
!!! には、微妙な差(誤差)がある
!!! => 浮動小数点数の比較に 「==(等しい)」は使ってはいけない
入力 : scanf ( "%lf", &v ); /* double 型変数への浮動小数点数の入力 */
/* (実は..) 浮動小数点数型には float がある .. */
計算
四則 : double 型同士の四則計算(和差積商 : +, -, *, / ) ができて、
結果も double 型になる
cf 整数型の場合は、四則+余りの計算ができて、結果も整数型
!!! 整数型同士の割り算は、結果も整数型になる
!!! 11 / 3 => 3
!!! 11.0 / 3.0 => 3.666..
効果
# 整数が扱える
# 「123」=> 「123.000」: 小数点の位置が、最後の数字の右に隠れている
# <= 実は、小数点の位置が、最後の数字の左に隠れていると思う
# 「123」=> 「12.3000」
# と、整数の和差に関しては、矛盾がおきない
# 「123」+「456」=「579」 (普通の..)小数点が右にあって整数とする
# 「123」+「456」= 12.3 + 45.6
# = 57.9
# = 「579」
# 小数点の位置が「固定」なら、整数の計算で、小数点数計算が可能
# !!! 掛け算や割り算をすると、小数点の位置を処理しないといけないので、ちょっと複雑
# !!! => できない事はない
# =>
# 浮動小数点数:小数点の移動を自動的にやってくれる
# => 小数点が、どんどん左に移動する ( その数値の絶対値は小さくなり、0 に近づく ..)
# => 「収束」が扱えるようなる
浮動小数点数の導入によって「収束」が扱える
=> 実数の連続性 (の有限版:コンピュータ版) が扱える
「微積」や「解析学」で学ぶ内容が、そのままコンピュータで使える
数学は無限が扱えるが、コンピュータは有限にせざるをえないので、
(原理的に..)誤差を含む
# 誤差を気にしない(許容する)ならば、連続が扱えると思ってよい
「収束」による「問題の『解法』」が利用できる(数学的な知識の活用)
=> 本日は、この部分(浮動小数点数の応用)の話をする予定
for 構文
繰り返しを表現する、別の方法 ( cf. 再帰/while )
制御変数の操作を一箇所にまとめた「while 構文」
制御変数: 繰り返す文の中で、値が変更され、繰り返し条件の時に判定に利用される変数
=> 本来は、一つとは限らないが、一つの事も多い
=> for 構文では、この制御変数への操作を一か所にまとめて、わかりやすくする
構文 : for ( <初期化式>; <条件式>; <再初期化式> ) { <繰返し命令> }
!! 普通は、「<初期化式>; <条件式>; <再初期化式>」には、共通の変数(制御変数)が含まれる事が多い
!! must ではない
for 構文は、それと等価な、while 構文で書き換える事が可能(逆も真)
for 構文を使う事によって、(イデオムの形で..) 書きやすく、読みやすい
=> 積極的に活用する
# for 構文は、いろいろな良いイデオム(手本)になっているので、
# 他の人の「良い」for 構文をまねすると、プログラミングが上達する
[リダイレクション]
プログラムを実行する時(cf. ./p-001.exe )に、
printf による出力は、画面に表示されるが、
その後ろに "> ファイル名" を追加する (出力のリダイレクション) と、
画面に表示されていた内容が、画面には表示されなくなり、かつ、
その内容が、ファイルの中に上書して、記録される
!! リダイレクションを利用すると(簡単に..)実行結果(普通、画面に表示される)を、
!! ファイルに記録する事ができる
!! 結果を再利用可能
[本日の内容]
数値的解法
問題の答えを、誤差を含んだ形(ある程度の誤差を許容する形)で求める方法
<-> 解析的解法 : 誤差を含まない答えを求める方法(いわゆる、数学的な手法)
# 解析的解法は、必ずしも無限を含んでいるわけではないが、
# 無限が(直接[あるいは、間接:例『実数』]的に)含まれる事が多い
# => コンピュータでは扱えない
# => 解析的な手法に、誤差を許容する形で、数値的な解法を作り、解く事になる
例 : 代数方程式を解く事を考える
代数方程式 : 二次方程式を解く
二次方程式ならば、解析的に解く事が可能
p-004.c では、解の公式(解析的な手法)で答えをもとめた
p-005.c では、二次関数の連続性を利用して、数値的にもとめた(二分法)
1. 関数の連続性を利用している
2. 数値的なアプローチなので、誤差が生じてしまう
p-006.c では、五次方程式の解を、数値的にといている
=> (数学) 五次以上の代数方程式には、「公式」が作れない事がわかっている
=> 「公式を利用して、答えを求める」という解析的な手法は適用できない
(why ? : 方程式の解を求める公式は、その次数である 1, 2, 3, 4 という
自然数のもつ[方程式を解くのに都合のよい]性質を利用しているから )
<= 数値的アプローチを用いれば、解析的に求められない問題を解く事ができる。
=> 「収束」を利用するので...
時間がかかる => 計算機が必要
無限にはできない => 誤差が生まれる
現実の世界への「数学」の適用
コンピュータを使って、計算量を減らし、誤差を許容する事によって、「数学が役立つ」
課題プログラム内の「/*名前:ここ*/」の部分を書き換え「/*この部分を完成させなさい*/」の部分にプログラムを追加して、プログラムを完成させます。
Download : 20191018-01.c
/*
* 20181012-01-QQQQ.c
*
* 一つ浮動小数点数値をキーボードから入力し、その立方根を出力する
* 手段としては、「二分法」を使う
*
* コンパイル :
* cc -I ~/c/include -c 20181012-01-QQQQ.c
* cc -o 20181012-01-QQQQ.exe 20181012-01-QQQQ.o
* 実行 :
* ./20181012-01-QQQQ.exe
*
*/
#include <stdio.h>
#include "s_input.h"
#include "s_print.h"
/*
*
*/
#define EPSILON 0.00000001 /* 誤差幅 */
/*
* double regula_falsi_cubic_root ( double a, double min, double mid, double max )
* double a 立方根の元になる数(正を仮定している)
* double min, max 根の入る区間の範囲
* double mid min と mid の中点
* return a 立方根
* 二分法により、a の立方根を求める
* 0 < min < a の立方根 < max
*/
double regula_falsi_cubic_root ( double a, double min, double mid, double max ) {
if ( max - min < EPSILON ) { /* 十分に精度が上った */
return mid; /* 中点の値を答として返す */
} else { /* まだ、狭める必要がある */
/* min が解のどちら側にあるかを調べ.. それに併せて区間を調整 */
/* f(x)=x^3-a */
if ( mid * mid * mid - a < 0.0 ) { /* f(mid) の符号を確認 */
/* 解の左にあった */
/*
** この部分を完成させなさい
*/
} else { /* 解の右にあった */
return regula_falsi_cubic_root ( a, min, (min+mid)/2.0, mid );
}
}
}
/*
* double cubic_root ( double a )
* double a 立方根の元になる数
* return a 立方根
* a の立方根を求めて結果として返すが、
* 計算の基本は、regula_falsi_cubic_root にまかせる
* ここでは、計算の正規化を行う
*/
double cubic_root ( double a ) {
if ( a < 0.0 ) { /* a が負の数ならば.. */
/* -a の立方根を計算し、負数を返す */
/*
** この部分を完成させなさい
*/
} else if ( a < 1.0 ) { /* a が 1.0 以下なら */
/*
** この部分を完成させなさい
*/
/* 立方根は a と 1.0 の間にある */
} else { /* そうでなければ.. */
return regula_falsi_cubic_root ( a, 1.0, (1.0+a)/2.0, a );
/* 立方根は 1.0 と a の間にある */
}
}
/*
* void print_cubic_root ( double a )
* double a 立方根を計算する数
* 元の数と、立方根を出力する
*/
void print_cubic_root ( double a ) {
s_print_double ( a );
s_print_string ( " の立方根は " );
/*
** この部分を完成させなさい
*/
s_print_string ( " です。\n" );
}
/*
* main
*/
int main( double argc, char *argv[] )
{
s_print_string ( "実数値を一つ入力してください : " );
print_cubic_root ( s_input_double() );
return 0;
}
12.34
$ ./20191018-01-QQQQ.exe 実数値を一つ入力してください : 12.340000 12.340000 の立方根は 2.310850 です。 $
Download : 20191018-02.c
/*
* 20191018-02-QQQQ.c
*
* CSV ファイル内の総計を求める
*
* コンパイル :
* cc -I ~/c/include -c 20191018-02-QQQQ.c
* cc -o 20191018-02-QQQQ.exe 20191018-02-QQQQ.o
* 実行 :
* ./20191018-02-QQQQ.exe
*
*/
/*
* 第一引数で与えられた csv ファイル内の、
* A1:J10 (10x10) の行の和を J1:J10 に入れるた結果を
* 第二引数で与えられた csv ファイルに保存する。
*
* この課題を解く時には、
* PNAME/s_csv
* に、ファイル 20191018-02-QQQQ.c, 20191018-02.csv を保存し、
* make TEST=20191018-02-QQQQ
* で、コンパイル、リンク、実行を行う
* 20191018-DIRout.csv
* が作られ、その内容が表示されれば OK
*/
#include <stdio.h>
#include "s_cellio.h" /* CSV ファイルの操作に必要 */
#include "s_csvio.h"
/*
* void sum_row ( int row, int sum, int col, int col_max )
* row 処理対象になる行番号
* sum ここまでのセルの値の和
* col 処理対象になる列番号
* col_max 最大の列番号 - 1 であると同時に、総和の保存先の列番号
*/
void sum_row ( int row, int sum, int col, int col_max ) {
if ( col < col_max ) { /* 列番号がまだ、最大値になっていない */
sum_row ( row, sum + s_get_cell_with_position ( col, row ), col + 1, col_max );
/* 注目している row, col の値を、*/
/* sum に加えて、次の列(col+1)へ */
} else { /* 列が最大を越えたので.. */
/*
** この部分を完成させなさい
*/
/* その場所に、和を記録する */
}
}
/*
* void sum_all_row ( int row, int row_max )
* row 処理対象になる行番号
* row_max 最大の行番号 - 1
*/
void sum_all_row ( int row, int row_max ) {
if ( row < row_max ) { /* 行番号がまだ、最大値になっていない */
sum_row ( row, 0, 0, 10 ); /* その行の総和を計算する */
/* 次の行 (row+1) を計算 */
/*
** この部分を完成させなさい
*/
} else { /* 行が最大を越えたので.. */
/* やる事は何もない */
}
}
/*
* update_csv
*/
void update_csv ( char *in_file, char *out_file ) {
s_load_csv ( in_file ); /* 第一引数のファイルからデータを入手 */
sum_all_row ( 0, 10 ); /* 0 列目から 10 列分の総和を計算 */
/* 計算結果を第二引数のファイルに保存 */
/*
** この部分を完成させなさい
*/
}
/*
* main
*/
int main ( int argc, char *argv[] ) {
if ( argc == 3 ) { /* 引数の個数が 2 ( = 3-1 ) の時 */
update_csv ( argv[1], argv[2] );
/* 第一引数のファイルを変更して第二引数のファイルに */
} else {
printf ( "ファイル名を二つ指定してください\n" );
}
return 0;
}
$ ./20191018-02-QQQQ.exe $
Download : 20191018-03.c
/*
* 20191018-03-QQQQ.c
*
* 関数 sin(x) の区間 [0,π/4] の定積
*
* コンパイル :
* cc -I ~/c/include -c 20191018-03-QQQQ.c
* cc -o 20191018-03-QQQQ.exe 20191018-03-QQQQ.o
* 実行 :
* ./20191018-03-QQQQ.exe
*
*/
#include <stdio.h>
#include <math.h> /* 数学的関数 sin を利用するので.. */
#include "s_input.h"
#include "s_print.h"
/*
* リーマン積分を利用する
*/
#define FRACTIONAL 1000 /* 区間の等分数 */
/*
* f(x)=sin(x)
*/
double f(double x) {
/*
* 引数 x に対して、x の 正弦値 sin(x) を値として返す関数
*/
/*
** この部分を完成させなさい
*/
}
/*
reman_sum ( int i, int n, double min, double max )
S_i 〜 S_{n^1} の和を計算する
*/
double reman_sum ( int i, int n, double min, double max ) {
if ( i < n ) { /* まだ計算が必要 */
/* 注目している短冊の面積と、残りの部分の面積の和を計算する */
/*
** この部分を完成させなさい
*/
} else { /* もう、全て計算した */
return 0.0; /* 残る結果は 0 になる */
}
}
/*
* リーマン積分
*
* 関数の積分値が、小さな幅の短冊の面積の和で近似できる事を利用
*
* solove_reaman ( double min, double max )
*
*/
double solve_reman ( double min, double max ) {
/*
min から max までを積分
基本は reman_sum に任せる
*/
return reman_sum ( 0, FRACTIONAL, min, max );
}
/*
* main 関数
*/
int main ( void ) {
s_print_string ( "関数 f(x)=sin(x) を区間[0,π/4]で数値定積分する。\n" );
s_print_string ( "リーマンの定義に従って計算した答えは : " );
/*
** この部分を完成させなさい
*/
s_print_string ( "になりました。\n" );
s_print_string ( "解析的な計算の結果は 1-√2/2 なので、誤差は " );
s_print_double ( solve_reman ( 0.0, M_PI/4.0 ) - (1.0-sqrt(2.0)/2.0) );
s_print_string ( " になり、ほぼ答えに近い事がわかります\n" );
return 0;
}
$ ./20191018-03-QQQQ.exe 関数 f(x)=sin(x) を区間[0,π/4]で数値定積分する。 リーマンの定義に従って計算した答えは : 0.292616になりました。 解析的な計算の結果は 1-√2/2 なので、誤差は -0.000278 になり、ほぼ答えに近い事がわかります $