宿題4-1の解答
要求された機能を満たす2分法のプログラムを示す。
(1) 精度、初期区間の入力機能追加
(1-1) 初期区間 [x1, x2] の値と
精度 eps を入力できるプログラム
(bisec1_1.c)。
/*
bisec1_1.c: 初期区間、精度入力バージョン
*/
#include <stdio.h>
double f(double x)
// f(x) = x^2 - 2
{
return x * x - 2.0;
}
int main(void)
{
int n=0;
double x1=1, x2=2, x3, err;
double eps = 1.0e-6;
char s[128];
fprintf(stderr, "# x1, x2 = "); fgets(s, 128, stdin);
sscanf(s, "%lf%lf", &x1, &x2);
fprintf(stderr, "# eps = "); fgets(s, 128, stdin);
sscanf(s, "%lf", &eps);
printf("# n, x, err\n");
do {
n++;
x3 = (x1 + x2) / 2.0;
if (f(x3) > 0.0) x2 = x3;
else x1 = x3;
err = x2 - x1;
printf("%4d, % .15e, % .15e\n", n, x3, err);
} while (err >= eps);
printf("\n# sqrt(2) = % .15e\n", (x1 + x2) / 2.0);
return 0;
}
(1-2) 負の領域の解も求められるプログラム
(bisec1_2.c)。
/*
bisec1_2.c: 負領域可能バージョン
*/
#include <stdio.h>
double f(double x)
// f(x) = x^2 - 2
{
return x * x - 2.0;
}
int main(void)
{
int n=0;
double x1=1, x2=2, x3, err;
double eps = 1.0e-6;
char s[128];
fprintf(stderr, "# x1, x2 = "); fgets(s, 128, stdin);
sscanf(s, "%lf%lf", &x1, &x2);
fprintf(stderr, "# eps = "); fgets(s, 128, stdin);
sscanf(s, "%lf", &eps);
printf("# n, x, err\n");
do {
n++;
x3 = (x1 + x2) / 2.0;
if (f(x1)*f(x3) < 0.0) x2 = x3;
else x1 = x3;
err = x2 - x1;
printf("%4d, % .15e, % .15e\n", n, x3, err);
} while (err >= eps);
printf("\n# sqrt(2) = % .15e\n", (x1 + x2) / 2.0);
return 0;
}
(2) エラー処理の追加
(2-1) 反復が止まらない場合にエラーメッセージ
“*** 反復回数が100回を超えました。***”を表示して
有限回(N = 100)で止まるプログラム
(bisec2_1.c)。
/*
bisec2_1.c: 反復回数制限バージョン
*/
#include <stdio.h>
#define N 100
double f(double x)
// f(x) = x^2 - 2
{
return x * x - 2.0;
}
int main(void)
{
int n=0;
double x1=1, x2=2, x3, err;
double eps = 1.0e-6;
char s[128];
fprintf(stderr, "# x1, x2 = "); fgets(s, 128, stdin);
sscanf(s, "%lf%lf", &x1, &x2);
fprintf(stderr, "# eps = "); fgets(s, 128, stdin);
sscanf(s, "%lf", &eps);
printf("# n, x, err\n");
do {
n++;
if (n > N) {
fprintf(stderr, "*** 反復回数が100回を超えました。***");
break; // →[付録1]
}
x3 = (x1 + x2) / 2.0;
if (f(x1)*f(x3) < 0.0) x2 = x3;
else x1 = x3;
err = x2 - x1;
printf("%4d, % .15e, % .15e\n", n, x3, err);
} while (err >= eps);
printf("\n# sqrt(2) = % .15e\n", (x1 + x2) / 2.0);
return 0;
}
(2-2) 入力値が x1 > x2
でも解が求められるプログラム
(bisec2_2.c)。
/*
bisec2_2.c: 初期区間逆入力可能バージョン
*/
#include <stdio.h>
#include <math.h>
#define N 100
double f(double x)
// f(x) = x^2 - 2
{
return x * x - 2.0;
}
int main(void)
{
int n=0;
double x1=1, x2=2, x3, err;
double eps = 1.0e-6;
char s[128];
fprintf(stderr, "# x1, x2 = "); fgets(s, 128, stdin);
sscanf(s, "%lf%lf", &x1, &x2);
fprintf(stderr, "# eps = "); fgets(s, 128, stdin);
sscanf(s, "%lf", &eps);
printf("# n, x, err\n");
do {
n++;
if (n > N) {
fprintf(stderr, "*** 反復回数が100回を超えました。***");
break;
}
x3 = (x1 + x2) / 2.0;
if (f(x1)*f(x3) < 0.0) x2 = x3;
else x1 = x3;
err = fabs(x2 - x1);
printf("%4d, % .15e, % .15e\n", n, x3, err);
} while (err >= eps);
printf("\n# sqrt(2) = % .15e\n", (x1 + x2) / 2.0);
return 0;
}
(2-3) 初期区間の数値が異常な場合
(例えばf (x3)とf (x1),
f (x2)の符号がすべて同じ場合)
は入力エラーメッセージ“*** 初期区間の数値が異常です。***”
を表示して終了するプログラム
(bisec2_3.c)。
/*
bisec2_3.c: 異常入力検出バージョン
*/
#include <stdio.h>
#include <math.h>
#define N 100
double f(double x)
// f(x) = x^2 - 2
{
return x * x - 2.0;
}
int main(void)
{
int n=0;
double x1=1, x2=2, x3, err;
double eps = 1.0e-6;
char s[128];
fprintf(stderr, "# x1, x2 = "); fgets(s, 128, stdin);
sscanf(s, "%lf%lf", &x1, &x2);
fprintf(stderr, "# eps = "); fgets(s, 128, stdin);
sscanf(s, "%lf", &eps);
printf("# n, x, err\n");
do {
n++;
if (n > N) {
fprintf(stderr, "*** 反復回数が100回を超えました。***");
break;
}
x3 = (x1 + x2) / 2.0;
if (f(x1)*f(x3) < 0.0) x2 = x3;
else if (f(x3)*f(x2) < 0.0) x1 = x3;
else {
fprintf(stderr, "*** 初期区間が異常です。***\n");
return 1; // →[付録1]
}
err = fabs(x2 - x1);
printf("%4d, % .15e, % .15e\n", n, x3, err);
} while (err >= eps);
printf("\n# sqrt(2) = % .15e\n", (x1 + x2) / 2.0);
return 0;
}
(3) ソースのさらなる改良
(3-1) 二分法のアルゴリズムの部分(bisec.cの20行め〜28行めに相当する部分) を
bisection() と名づけ、 main関数から独立させたプログラム
(bisec3_1.c)。
/*
bisec3_1.c: サブルーチン独立バージョン
*/
#include <stdio.h>
#include <math.h>
#define N 100
double f(double x)
// f(x) = x^2 - 2
{
return x * x - 2.0;
}
double bisection(double x1, double x2, double eps)
{
int n=0;
double x3, err;
printf("# n, x, err\n");
do {
n++;
if (n > N) {
fprintf(stderr, "*** 反復回数が100回を超えました。***");
break;
}
x3 = (x1 + x2) / 2.0;
if (f(x1)*f(x3) < 0.0) x2 = x3;
else if (f(x3)*f(x2) < 0.0) x1 = x3;
else {
fprintf(stderr, "# 初期区間が異常です。\n");
exit(1); // →[付録1]
}
err = fabs(x2 - x1);
printf("%4d, % .15e, % .15e\n", n, x3, err);
} while (err >= eps);
return (x1 + x2) / 2.0;
}
int main(void)
{
double x1=1, x2=2;
double eps = 1.0e-6;
char s[128];
fprintf(stderr, "# x1, x2 = "); fgets(s, 128, stdin);
sscanf(s, "%lf%lf", &x1, &x2);
fprintf(stderr, "# eps = "); fgets(s, 128, stdin);
sscanf(s, "%lf", &eps);
printf("\n# sqrt(2) = % .15e\n", bisection(x1, x2, eps));
return 0;
}
(3-2) 方程式の左辺の関数 f (x) の名前をmain関数側で自由に
指定できるように bisection の引数に関数引数を用いた定義を追加したプログラム
(bisec3_2.c)。
/*
bisec3_2.c: サブルーチン関数引数バージョン
*/
#include <stdio.h>
#include <math.h>
#define N 100
double f(double x)
// f(x) = x^2 - 2
{
return x * x - 2.0;
}
double bisection(double (*f)(double), double x1, double x2, double eps)
{
int n=0;
double x3, err;
printf("# n, x, err\n");
do {
n++;
if (n > N) {
fprintf(stderr, "*** 反復回数が100回を超えました。***");
break;
}
x3 = (x1 + x2) / 2.0;
if (f(x1)*f(x3) < 0.0) x2 = x3;
else if (f(x3)*f(x2) < 0.0) x1 = x3;
else {
fprintf(stderr, "# 初期区間が異常です。\n");
exit(1);
}
err = fabs(x2 - x1);
printf("%4d, % .15e, % .15e\n", n, x3, err);
} while (err >= eps);
return (x1 + x2) / 2.0;
}
int main(void)
{
double x1=1, x2=2;
double eps = 1.0e-6;
char s[128];
fprintf(stderr, "# x1, x2 = "); fgets(s, 128, stdin);
sscanf(s, "%lf%lf", &x1, &x2);
fprintf(stderr, "# eps = "); fgets(s, 128, stdin);
sscanf(s, "%lf", &eps);
printf("\n# sqrt(2) = % .15e\n", bisection(f, x1, x2, eps));
return 0;
}
【目次】 |
【1.】 |
【2.】 |
【3.】 |
【付録1】
【付録2】