【minjijo.cのソースコード】
/*
* 最小二乗法
* minjijo.c
*/
#include <stdio.h>
#include <stdlib.h>
#include <math.h> /* exp() */
#include <string.h> /* strlen(), strcat() */
#include <ctype.h> /* isdigit() */
#include <time.h>
#define N 21
#define BUF_SIZE 256
/*
* 自作のbool型の定義
*/
typedef enum {
false, /* 0 */
true /* 1 */
} bool;
/*
* ユーザー定義関数の関数原型宣言
*/
void print_tile(void);
void select_func(void);
bool is_valid_number(char buf[]);
void input_data(void);
void ffv(int p, double a, double *b);
void calc(void);
void output(void);
void gen_filename(char filename[]);
bool retry(void);
/*
* グローバル変数
*/
int f, g, n, i, j, l;
double xx, yy, p, q, h, s, fx, gx, c[4][4], d[4];
double x[N], y[N], x2[N], x3[N], a[N][4], b[4][N];
double xi;
/*
* メイン関数
*/
int main(void)
{
/* 乱数系列の設定 */
srand((unsigned int)time(NULL));
while (1) {
/* 標題 */
print_tile();
/* 関数の種類の選択 */
select_func();
/* データの入力 */
input_data();
/* 計算 */
calc();
/* 出力 */
output();
/* 操作を続けるかどうかの選択 */
if (!retry())
break;
}
return EXIT_SUCCESS;
}
/*
* 標題を表示する関数
*/
void print_tile(void)
{
printf(" このプログラムは最小二乗法によって \n");
printf(" y = a*f(x) + b*g(x) \n");
printf(" の形の曲線を当てはめるものです。\n\n");
printf("基本関数f(x)、g(x)を1~4の番号で選択");
printf("してください。\n");
}
/*
* 関数の種類を選択する関数
* is_valid_number()関数を使用する。
*/
void select_func(void)
{
char buf[BUF_SIZE], zz;
while(1) {
printf("f(x) = [1:(x)、2:(1/x)、3:(e^x)]--> ");
fgets(buf, sizeof buf, stdin);
sscanf(buf, "%d%c", &f, &zz);
buf[strlen(buf) - 1] = '\0';
if (!is_valid_number(buf)) {
printf("無効な入力です。\n");
printf("半角数値を入力してください。\n");
continue;
} else if (((f < 1) || (3 < f)) || (zz != '\n')) {
printf("無効な入力です。\n");
printf("1から3の整数を入力してください。\n");
continue;
} else
break;
}
while(1) {
printf("g(x) = [1:(x)、2:(1/x)、3:(e^x)、4:(定数)]--> ");
fgets(buf, sizeof buf, stdin);
sscanf(buf, "%d%c", &g, &zz);
buf[strlen(buf) - 1] = '\0';
if (!is_valid_number(buf)) {
printf("無効な入力です。\n");
printf("半角数値を入力してください。\n");
continue;
} else if (((g < 1) || (4 < g)) || (zz != '\n')) {
printf("無効な入力です。\n");
printf("1から4の整数を入力してください。\n");
continue;
} else
break;
}
}
/*
* 文字列が正しい形式の数値かどうかを判定する関数
*/
int is_valid_number(char buf[])
{
int i = 0;
/* 最初は符号(省略可) */
if (buf[i] == '+' || buf[i] == '-')
i++;
/* 次に数字(省略不可) */
if (!isdigit(buf[i]))
return false;
i++;
/* 次に数字列(省略可) */
while (isdigit(buf[i]))
i++;
/* 次に小数点(省略可) */
if (buf[i] == '.') {
i++;
/* 次に数字(小数点がある場合は省略不可、なければ省略可) */
if (!isdigit(buf[i]))
return false;
i++;
/* 次に数字列(省略可) */
while (isdigit(buf[i]))
i++;
}
/* 次にeまたはE(省略可) */
if (buf[i] == 'e' || buf[i] == 'E') {
i++;
/* 次に符号(省略可) */
if (buf[i] == '+' || buf[i] == '-')
i++;
/* 次に数字(eまたはEがある場合は省略不可、なければ省略可) */
if (!isdigit(buf[i]))
return false;
i++;
/* 次に数字列(省略可) */
while (isdigit(buf[i]))
i++;
}
/* 他に余計な文字がなければ真、あれば偽 */
return buf[i] == '\0';
}
/*
* データを入力する関数
* ffv()関数を使用する。
*/
void input_data(void)
{
char buf[BUF_SIZE], ch, zz;
while(1) {
printf("データの個数nは何個ですか?(2 ≦ n ≦ 19) n = ? ");
fgets(buf, sizeof buf, stdin);
sscanf(buf, "%d%c", &n, &zz);
buf[strlen(buf) - 1] = '\0';
if (!is_valid_number(buf)) {
printf("無効な入力です。\n");
printf("半角数値を入力してください。\n");
continue;
} else if (((n < 2) || (19 < n)) || (zz != '\n')) {
printf("無効な入力です。\n");
printf("2から19の整数を入力してください。\n");
continue;
}
printf("データ x の値は小から大の順に入力する。\n");
for (i = 1; i <= n; i++) {
printf("X = ");
fgets(buf, sizeof buf, stdin);
sscanf(buf, "%lf%c", &x[i], &zz);
printf("Y = ");
fgets(buf, sizeof buf, stdin);
sscanf(buf, "%lf%c", &y[i], &zz);
/* 関数を呼び出す */
xi = x[i];
ffv(f, xi, &fx); ffv(g, xi, &gx);
x2[i] = fx; x3[i] = gx;
a[i][1] = x2[i]; a[i][2] = x3[i]; a[i][3] = y[i];
b[1][i] = a[i][1]; b[2][i] = a[i][2];
}
printf("\n正しく入力しましたか?(y/n) ");
fgets(buf, sizeof buf, stdin);
sscanf(buf, "%c%c", &ch, &zz);
if ((ch == 'y' || ch == 'Y') && zz == '\n')
break;
}
}
/*
* 基本関数の関数値を求める関数
*/
void ffv(int p, double a, double *b)
{
switch (p) {
case 1:
*b = a;
break;
case 2:
*b = 1.0 / a;
break;
case 3:
*b = exp(a);
break;
case 4:
*b = 1.0;
break;
default:
*b = a;
break;
}
}
/*
* 計算をする関数
*/
void calc(void)
{
/* tA・A を計算して配列 c[2][3] に入れる*/
for (i = 1; i <= 2; i++)
for (j = 1; j <= 3; j++) {
c[i][j] = 0.0;
for (l = 1; l <= n; l++)
c[i][j] += b[i][l] * a[l][j];
}
/* 正規方程式をガウス・ジョルダン法で解く */
for (i = 1; i <= 2; i++) {
p = c[i][i];
for (j = i; j <= 3; j++)
c[i][j] /= p;
for (l = 1; l <= 2; l++) {
if (l != i) {
q = c[l][i];
for (j = i; j <= 3; j++)
c[l][j] -= q * c[i][j];
}
}
}
/* 答を配列d[1]、d[2]に入れる */
for (i = 1; i <= 2; i++)
d[i] = c[i][3];
printf("\n求めた基本関数の係数の入力\n");
printf(" a = d[1] = %lf\n", d[1]);
printf(" b = d[2] = %lf\n", d[2]);
}
/*
* 出力をする関数
* gen_filename()関数を使用する。
*/
void output(void)
{
char buf[BUF_SIZE], ch, zz, filename[BUF_SIZE];
FILE *fout;
while (1) {
printf("数表を出力しますか?(y/n) ");
fgets(buf, sizeof buf, stdin);
sscanf(buf, "%c%c", &ch, &zz);
if ((ch == 'y' || ch == 'Y') && zz == '\n') {
while(1) {
printf("\nエンターキーを押せば数表を出力します。\n");
fgets(buf, sizeof buf, stdin);
sscanf(buf, "%c", &zz);
if (zz != '\n') {
printf("無効な入力です。\n");
printf("エンターキーを押してください。\n");
continue;
} else
break;
}
/* グラフを描くための準備(数表を出力) */
h = (x[n] - x[1]) / 50.0; xx = x[1];
while (1) {
printf("数表を画面に出力しますか、ファイルに出力しますか?\n");
printf("画面なら1を、ファイルなら2を入力してください: ");
fgets(buf, sizeof buf, stdin);
sscanf(buf, "%c%c", &ch, &zz);
if ((ch == '1') && (zz == '\n')) {
for (i = 0; i <= 50; i++) {
ffv(f, xx, &fx); ffv(g, xx, &gx);
yy = d[1] * fx + d[2] * gx;
printf("%10.6lf %10.6lf\n", xx, yy);
xx += h;
}
goto repeat;
}
else if ((ch == '2') && (zz == '\n')) {
gen_filename(filename);
fout = fopen(filename, "w");
if(fout == NULL) {
printf("ファイルを開けません。");
printf("プログラムを終了します。\n");
exit(EXIT_FAILURE);
}
for (i = 0; i <= 50; i++) {
ffv(f, xx, &fx); ffv(g, xx, &gx);
yy = d[1] * fx + d[2] * gx;
fprintf(fout, "%10.6lf %10.6lf\n", xx, yy);
xx += h;
}
fclose(fout);
goto repeat;
}
else {
printf("無効な入力です。\n");
printf("1または2を入力してください: ");
continue;
}
}
}
else if ((ch == 'n') && (zz == '\n'))
break;
else {
printf("無効な入力です。\n");
printf("yまたはnを入力してください: ");
continue;
}
}
repeat:
;
}
/* 現在日付時刻からファイル名を生成する関数 */
void gen_filename(char filename[])
{
struct tm *t;
time_t tt;
char snumrand[10];
time(&tt);
t = localtime(&tt);
sprintf(filename, "minjijo%04d%02d%02d_%02d%02d%02d_",
1900+(t->tm_year), 1+(t->tm_mon), t->tm_mday,
t->tm_hour, t->tm_min, t->tm_sec);
sprintf(snumrand, "%04d", rand() % 10000);
filename = strcat(filename, snumrand);
filename = strcat(filename, ".txt");
}
/*
* 操作を続けるかどうかを選択する関数
* true: 続ける、false: 終了する。
*/
bool retry(void)
{
bool ret;
char buf[BUF_SIZE], ch, zz;
while (1) {
printf("\n続けますか?(y/n) ");
fgets(buf, sizeof buf, stdin);
sscanf(buf, "%c%c", &ch, &zz);
if ((ch == 'y' || ch == 'Y') && zz == '\n') {
printf("\n+++++\n\n");
ret = true;
break;
} else if ((ch == 'n' || ch == 'N') && zz == '\n') {
ret = false;
break;
}
printf("無効な入力です。\n");
printf("yまたはnを入力してください。\n");
}
return ret;
}
C:\Users\skonishi\Documents>minjijo
このプログラムは最小二乗法によって
y = a*f(x) + b*g(x)
の形の曲線を当てはめるものです。
基本関数f(x)、g(x)を1~4の番号で選択してください。
f(x) = [1:(x)、2:(1/x)、3:(e^x)]--> 1
g(x) = [1:(x)、2:(1/x)、3:(e^x)、4:(定数)]--> 2
データの個数nは何個ですか?(2 ≦ n ≦ 19) n = ? 7
データ x の値は小から大の順に入力する。
X = 0.2
Y = 12.1
X = 0.5
Y = 4.9
X = 1.0
Y = 2.9
X = 2.0
Y = 2.1
X = 4.0
Y = 2.1
X = 8.0
Y = 3.4
X = 10.0
Y = 4.3
正しく入力しましたか?(y/n) y
求めた基本関数の係数の入力
a = d[1] = 0.398093
b = d[2] = 2.401050
数表を出力しますか?(y/n) y
エンターキーを押せば数表を出力します。
[エンター]キー
数表を画面に出力しますか、ファイルに出力しますか?
画面なら1を、ファイルなら2を入力してください: 2
続けますか?(y/n) y
+++++
このプログラムは最小二乗法によって
y = a*f(x) + b*g(x)
の形の曲線を当てはめるものです。
基本関数f(x)、g(x)を1~4の番号で選択してください。
f(x) = [1:(x)、2:(1/x)、3:(e^x)]--> 1
g(x) = [1:(x)、2:(1/x)、3:(e^x)、4:(定数)]--> 4
データの個数nは何個ですか?(2 ≦ n ≦ 19) n = ? 13
データ x の値は小から大の順に入力する。
X = 0.898
Y = -291.1
X = 0.937
Y = -288.4
X = 0.987
Y = -289.1
X = 1.046
Y = -285.7
X = 1.111
Y = -283.5
X = 1.188
Y = -280.0
X = 1.297
Y = -277.2
X = 1.414
Y = -273.9
X = 1.548
Y = -268.6
X = 1.692
Y = -261.5
X = 1.976
Y = -254.0
X = 2.257
Y = -243.1
X = 2.591
Y = -231.0
正しく入力しましたか?(y/n) y
求めた基本関数の係数の入力
a = d[1] = 35.249991
b = d[2] = -322.677334
数表を出力しますか?(y/n) y
エンターキーを押せば数表を出力します。
[エンター]キー
数表を画面に出力しますか、ファイルに出力しますか?
画面なら1を、ファイルなら2を入力してください: 2
続けますか?(y/n) n
0.200000 12.084868
0.396000 6.220902
0.592000 4.291498
0.788000 3.360715
0.984000 2.831815
1.180000 2.504538
1.376000 2.292725
1.572000 2.153188
1.768000 2.061888
1.964000 2.004385
2.160000 1.971478
2.356000 1.957028
2.552000 1.956784
2.748000 1.967704
2.944000 1.987560
3.140000 2.014678
3.336000 2.047777
3.532000 2.085863
3.728000 2.128149
3.924000 2.174005
4.120000 2.222922
4.316000 2.274483
4.512000 2.328343
4.708000 2.384215
4.904000 2.441859
5.100000 2.501068
5.296000 2.561671
5.492000 2.623517
5.688000 2.686478
5.884000 2.750443
6.080000 2.815315
6.276000 2.881008
6.472000 2.947448
6.668000 3.014570
6.864000 3.082314
7.060000 3.150629
7.256000 3.219468
7.452000 3.288791
7.648000 3.358560
7.844000 3.428742
8.040000 3.499306
8.236000 3.570225
8.432000 3.641475
8.628000 3.713032
8.824000 3.784877
9.020000 3.856991
9.216000 3.929356
9.412000 4.001956
9.608000 4.074779
9.804000 4.147809
10.000000 4.221035
0.898000 -291.022841
0.931860 -289.829277
0.965720 -288.635712
0.999580 -287.442147
1.033440 -286.248583
1.067300 -285.055018
1.101160 -283.861453
1.135020 -282.667888
1.168880 -281.474324
1.202740 -280.280759
1.236600 -279.087194
1.270460 -277.893630
1.304320 -276.700065
1.338180 -275.506500
1.372040 -274.312935
1.405900 -273.119371
1.439760 -271.925806
1.473620 -270.732241
1.507480 -269.538677
1.541340 -268.345112
1.575200 -267.151547
1.609060 -265.957982
1.642920 -264.764418
1.676780 -263.570853
1.710640 -262.377288
1.744500 -261.183724
1.778360 -259.990159
1.812220 -258.796594
1.846080 -257.603029
1.879940 -256.409465
1.913800 -255.215900
1.947660 -254.022335
1.981520 -252.828771
2.015380 -251.635206
2.049240 -250.441641
2.083100 -249.248077
2.116960 -248.054512
2.150820 -246.860947
2.184680 -245.667382
2.218540 -244.473818
2.252400 -243.280253
2.286260 -242.086688
2.320120 -240.893124
2.353980 -239.699559
2.387840 -238.505994
2.421700 -237.312429
2.455560 -236.118865
2.489420 -234.925300
2.523280 -233.731735
2.557140 -232.538171
2.591000 -231.344606