![見出し画像](https://assets.st-note.com/production/uploads/images/96248138/rectangle_large_type_2_5a074d1e11efdf0059589d5aec0beb3f.jpeg?width=800)
プログラミング学習の記録 #015(C)
C言語の学習は、教科書的には今回が最終となった。とりあえず、基本的なC言語の操作は、一通り学習できたと思う。実際に数値計算を行ううえでは、より高度な技術を習得していく必要があると思うが、それらについて学習するための基礎は、習得できただろう。今後は、C言語のより発展的な内容を学習していくとともに、他のプログラミング言語の学習も進めていきたいところである。
前回の復習
練習問題 12.1
複素数の加算、減算、割算を行う関数を作成し、積も含めて実際に複素数演算を行ってみよ。
#include <stdio.h>
#include <math.h>
typedef struct complex
{
double x,y ;
} comp;
comp plus(comp a, comp b)
{
comp w;
w.x = a.x + b.x ;
w.y = a.y + b.y ;
return w;
}
comp minus(comp a, comp b)
{
comp w;
w.x = a.x - b.x ;
w.y = a.y - b.y ;
return w;
}
comp times(comp a, comp b)
{
comp w;
w.x = a.x * b.x - a.y * b.y ;
w.y = a.x * b.y + b.x * a.y ;
return w;
}
comp divide(comp a, comp b)
{
comp w;
b.y = - b.y ;
w = times(a,b) ;
w.x = w.x / ( pow(b.x,2) + pow(b.y,2) ) ;
w.y = w.y / ( pow(b.x,2) + pow(b.y,2) ) ;
return w;
}
void result(comp w)
{
if(w.y < 0)
{
printf("%7.2f - %7.2f i\n",w.x,-w.y);
}else
{
printf("%7.2f + %7.2f i\n",w.x,w.y);
}
}
int main()
{
comp a,b ;
comp p,q,r,s;
int si;
si = 0 ;
printf("Complex numbers x+iy.\n");
printf("You should input the numbers; x y.\n");
printf("Input the complex number1: ");
scanf("%lf %lf",&a.x,&a.y);
printf("Input the complex number2: ");
scanf("%lf %lf",&b.x,&b.y);
p = plus(a,b) ;
q = minus(a,b) ;
r = times(a,b) ;
if(b.x == 0 && b.y == 0)
{
si = 1 ;
}else
{
s = divide(a,b) ;
}
printf("a = ");
result(a);
printf("b = ");
result(b);
printf("a+b = ");
result(p);
printf("a-b = ");
result(q);
printf("a*b = ");
result(r);
printf("a/b ");
if(si == 0)
{
printf("= ");
result(s);
}else
{
printf("is error.\n");
}
printf("HAPPY SMILE (^_^)v\n");
return 0;
}
関数の再帰呼び出し
C言語では、関数の中で、その関数自身を呼び出すことができる。たとえば、
$$
n!
= n \cdot (n-1) \cdot (n-2) \cdot \cdots \cdot 2 \cdot 1
= n \cdot [(n-1)!]
$$
と式変形できる。教科書を参考にして、関数の再帰呼び出しを行いつつ、ある自然数$${n}$$の階乗を求めるプログラムコードを書いた。
#include <stdio.h>
#include <stdlib.h>
int factorial(int n)
{
int r;
if(n > 1)
{
r = n * factorial(n-1);
}else if(n == 1)
{
r = 1 ;
}else
{
r = 0;
}
return r;
}
int main()
{
int n,r;
printf("Input the number n(>=0): ");
scanf("%d",&n);
if(n < 0)
{
printf("Error in n.\n");
exit(1);
}
r = factorial(n);
printf("%d! = %d\n",n,r);
printf("HAPPY SMILE (^_^)v\n");
return 0;
}
ファイル処理
C言語でファイルの入出力を行うときには、以下の手順での操作が必要となる。
ファイルを開く
ファイルにデータを書き込む/ファイルからデータを読み取る
ファイルを閉じる
これらの操作は、FILE型ポインタを用いて行う。
fopen
ファイルを開くときにはfopenを用いる。
FILE *sample1, *sample2;
sample1 = fopen("data1.txt","w") ;
sample2 = fopen("data2.txt","r") ;
fopenには、いくつかのモードがあり、基本的に、書き込むときには「w」、読み取るときには「r」を選択する。fopenのモードは、以下の通りである。
r データ読み取り
w データ書き込み
a データ追加書き込み
r+ データ読み取り・書き込み、ファイルが存在しなければNULL返却
w+ データ読み取り・書き込み、ファイルが存在しなければ新規作成、ファイルが存在すれば上書き
a+ データ追加書き込み・読み取り
fprintf
ファイルにデータを書き込むときにはfprintfを用いる。
fprintf(sample1, "%f %f\n",x,y);
書き込み先のファイルのポインタを指定し、書き込む内容を記載する。printfと似た方法で用いる。
fscanf
ファイルからデータを読み取るときにはfscanfを用いる。
fscanf(sample2, %lf %lf",&a,&b);
fclose
ファイルを閉じるときにはfcloseを用いる。
fclose(sample1);
fclose(sample2);
数値計算における注意事項
桁落ち
数値計算を行ううえで、計算精度の問題を意識する必要がある。たとえば、$${a=1.0000001, b=1}$$としたときに、$${a-b=0.0000001}$$となるはずである。この計算を行うプログラムコードを書いた。
#include <stdio.h>
int main()
{
float a,b,c;
a = 1.0000001 ;
b = 1 ;
c = a - b ;
printf("a = %15.14f\n",a);
printf("b = %15.14f\n",b);
printf("a-b = %15.14f\n",c);
printf("HAPPY SMILE (^_^)v\n");
return 0;
}
これを実行すると、以下のように表示された。
% ./a.out
a = 1.00000011920929
b = 1.00000000000000
a-b = 0.00000011920929
HAPPY SMILE (^_^)v
つまり、$${a=1.0000001}$$では有効数字が7桁であったが、$${a-b=1 \times 10^{-7}}$$では有効数字が1桁になっている。本来、float型は、約7桁の計算精度があるが、近い値の減算によって、計算精度が小さくなったということである。これを桁落ちという。ちなみに、double型で計算すると、実行結果は、以下のように表示された。
% ./a.out
a = 1.00000010000000
b = 1.00000000000000
a-b = 0.00000010000000
HAPPY SMILE (^_^)v
桁落ちについての学習は、このサイトやこのサイトを参考にした。
積み残し
級数和の計算において、相対的に極端に小さい数を加えるときに、正しく計算されないことがある。教科書を参考にして、初期値を入力し、微少な数を1000回加える計算を行うプログラムコードを書いた。
#include <stdio.h>
int main()
{
double sum, dx;
int i,n;
n = 1000 ;
dx = 1.0e-16 ;
printf("Input initial number: ");
scanf("%lf",&sum);
for( i = 0 ; i < n ; i = i + 1 )
{
sum = sum + dx ;
}
printf("sum = %16.15f\n",sum);
printf("HAPPY SMILE (^_^)v\n");
return 0;
}
これを実行して、初期値を0,1にすると、それぞれ以下のように表示された。
% ./a.out
Input initial number: 0
sum = 0.000000000000100
HAPPY SMILE (^_^)v
% ./a.out
Input initial number: 1
sum = 1.000000000000000
HAPPY SMILE (^_^)v
初期値0のときには正しく加算されているが、初期値1のときには加算が正しく行われていない。これを積み残しという。積み残しの問題は、小さい値から演算していくことで解決できることがある。
計算速度の向上
数値計算においては、実質的に同じ演算であっても、計算速度が異なることがある。以下のような事例が挙げられる。
乗算と除算は、加算と減算よりも多くの計算時間を要する。
数学関数や組込み関数は、単純な演算よりも多くの計算時間を要する。
ただ、関数を使用しないと、コードが見にくくなってしまうことがある。数学関数などを用いたコードを書いて実行し、正しく実行されてから、計算速度を向上させるようにコードを書き換えるとよい。
-----
動け!タイムライン
動物園か水族館にいきたいですね。