2017 年度「計算数学a」 2017-10-20

§3.1 if ... else による分岐

(3.1.1) if ... else を使った自明なプログラム。

#include <stdio.h>

int main() {
    int x;

    printf("数を入れてください.\n");
    scanf("%d", &x);                        /* 数の入力 */

    if (x == 0) {
        printf("その数は零です.\n");
    } else if (x > 0) {
        printf("その数は正です.\n");
    } else {
        printf("その数は負です.\n");
    }
}

(3.1.2) if の行以降については次を見よ。


    if (条件1) {
        文1
    } else if (条件2) {    
        文2
    } else if (条件3) {
        文3
    } else {
        文4
    }
  • 条件1が真ならば文1だけを実行する。
  • 条件1が偽で条件2が真なら文2だけを実行する。
  • 条件1も条件2も偽で条件3が真ならば文3だけを実行する。
  • 条件1も条件2も条件3も偽なら文4だけを実行する。
   (文のうち一つだけが実行されることに注意。)

else if は何個あってもかまわない。

(3.1.3) if が一つしかなく,かつ,else がないとき。


    if (条件1) {           
        文1
    }
  • 条件1が真ならば文1を実行する。
  • 条件1が偽ならば何も行なわない。

「条件」の書き方は,(2.5.7) の for ループの「継続条件」と同じである。

(3.1.4) K&R2 では §3.2, §3.3 を参照。 そこでは省略可能な中カッコを省略しているが, 私のやり方のように全てつけておくほうが間違いが少ない。

(3.1.5) インデントについても上の例でしっかり覚えよ。 中カッコの中の文はタブ一つ分だけ余計に字下げするのだった。

(3.1.6) (3.1.1) のプログラムで、 if (x = 0) と書いたらどうなるか。 0 をほかの数に変えての実験もしてみよ。

(3.1.7) アラレ数とは,次の漸化式で決まる数列である。 「ある項が偶数のときは次の項はその 1/2, 奇数の時はその項の 3 倍 + 1」。 どんな数から始めても 4, 2, 1, 4, 2, 1 ... の無限ループにおちいると予想されている。 このプログラムは,1 になったらそこで終わる。

/* アラレ数 */

#include <stdio.h>

int main() {
    int n;

    printf("自然数を入れてください.\n");
    scanf("%d", &n);            /* キーボードから数を入力 */

    for (     ; n != 1;      ) {
        if (n % 2 == 0) {       /* n が偶数のとき */
            n = n / 2;
        } else {                /* n が奇数のとき */
            n = 3 * n + 1;
        }
        printf("%d ", n);
    }
    printf("\n");
}

※ 初期値が正の数以外とみなされると無限ループにおちいる可能性がある。 そのときは Ctrl+C で止める。

(3.1.8) 二重の forif ... else を組み合わせたプログラム。

/* 2 から 99 までの整数の最小の素因数を出力 */

#include <stdio.h>
 
int main() {
    int n, p;
 
    for (n = 2; n < 100; n++) {
        for (p = 2; n % p != 0; p++) {
            ;                              /* 空文。なにもしない */
        }
        if (n == p) {
            printf("%d は素数です.\n", n);
        } else {
            printf("%d の最小の素因数は %d です.\n", n, p);
        }
    }
}

(3.1.9) 練習問題:上のプログラムを,素数のみを出力するよう改変してみよ。 素数を単に並べて出力すればよい。 2 3 5 7 11 13 ... のように。

もっと大きな自然数まで調べるようにしてみよ。

§3.2 double

(3.2.1) C言語には小数点以下のある数を扱える型がいくつかある。 この授業では double 型のみを紹介する。

(3.2.2)

#include <stdio.h>

int main() {
    double x;

    scanf("%lf", &x);
    printf("%f\n", x);
}

double 型の入出力の際には上のように書く。 %lf(パーセント,エル,エフ)と %f の違いに注意せよ。 出力は小数点以下 6 桁となる。 20 桁ぐらいの大きな数や, 0.00000001 のような小さな数を入力してみよ。

    printf("%15.8f\n", x);

とすると「全体で少なくとも 15 桁の幅,小数点以下 8 桁」で表示される。

    printf("%15f\n", x);

なら「全体で少なくとも 15 桁の幅」。

    printf("%.8f\n", x);

なら「小数点以下 8 桁」。

(3.2.3) 以下のプログラムは,コピペして動かし, 「こんなものか」と思えばそれでよい。

(3.2.4) 自然対数の底 e の値を,e = 1 + 1/1! + 1/2! + 1/3! + ... として計算する。

#include <stdio.h>

int main() {
    int n;
    double x, sum;

    x = 1;
    sum = 1;
    for (n = 1; n <= 25; n++) {
        x = x/n;
        sum = sum + x;
        printf("%2d %.100f\n", n, sum);
    }
    printf("   2.718281828459045235360287471352...\n");
}

(3.2.5) 同様。階乗の計算も行なうバージョン。

#include <stdio.h>

int main() {
    int n;
    double factorial, sum;

    factorial = 1;
    sum = 1;
    for (n = 1; n <= 25; n++) {
        factorial = factorial * n;
        sum = sum + 1/factorial;
        printf("%2d %33f %.100f\n", n, factorial, sum);
    }
}

(3.2.6) 階乗を計算するプログラム。あるところから先は“無限大”になる。

#include <stdio.h>

int main() {
    int n;
    double factorial;

    factorial = 1;
    for (n = 1; n <= 200; n++) {
        factorial = factorial * n;
        printf("%2d %f\n", n, factorial);
    }
}

(3.2.7) int 型と double 型を混ぜて使う際の注意。 int 型を double 型に代入すると,(たぶん)そのまま格納される。 double 型を int 型に代入すると,(確か)小数点以下が切り捨てられる。

やや間違えやすいケースをプログラムで示す。

#include <stdio.h>

int main() {
    double x;

    x = 1/3;
    printf("%f\n", x);

    x = 1.0/3;
    printf("%f\n", x);

    x = 1;
    x = x/3;
    printf("%f\n", x);
}

(3.2.8) 1 + 1/2 + 1/3 + ... を計算するプログラム。

#include <stdio.h>

int main() {
    int n;
    double sum, x;

    sum = 0;
    x = 1;
    for (n = 0;    ; n++) {     /* 無限ループ */
        sum = sum + 1/x;
        x++;
        printf("%d: %.200f\n", n+1, sum);
    }
}

∞に発散するはずだが,なかなか大きくならない。 (この級数をコンピュータで調べようとするのは無謀である。)

(3.2.9) double 型は二進で数値を保存しており, 十進に変換して出力される。 十進の 0.1 は二進では 1/1010 なので循環小数となり, 十進の 1.333 のような近似値として扱われる。 (以下のプログラムでそのことがわかる。)

(3.2.10) (注意)二進の有限小数は十進でも有限小数である。

(3.2.11) double 型には, 十進で言えば 1.234567×1089 のような形で値が格納されているので, 桁数が大きい整数も代入できる。 しかし,近似値になってしまうこともある。

(3.2.12) 次のプログラムは,0.9 までが出力されるように思えるが,そうはならない。

#include <stdio.h>

int main() {
    double x;

    for (x = 0; x < 1; x = x + 0.1) {
        printf("%f\n", x);
    }
}

(3.2.13) 以下のプログラムを動かしてみよ。 (諸数値はここの端末を最大化して使うことを想定して設定してある。)

#include <stdio.h>

int main() {
    int n;
    double x;

    x = 1;
    for (n = 0; n < 230; n++) {
        printf("%f\n", x);
        x = x * 10;
    }
}

(3.2.14)

#include <stdio.h>

int main() {
    int n;
    double x;

    x = 1;
    for (n = 0; n < 236; n++) {
        printf("%.234f\n", x);
        x = x / 10;
    }
}

(3.2.15)

#include <stdio.h>

int main() {
    int n;
    double x;

    x = 1;
    for (n = 0; n < 761; n++) {
        printf("%236f\n", x);
        x = x * 2;
    }
}

(3.2.16)

#include <stdio.h>

int main() {
    int n;
    double x;

    x = 1;
    for (n = 0; n < 780; n++) {
        printf("%.234f\n", x);
        x = x / 2;
    }
}

(3.2.17)

    printf("%g\n", x);

とすると,1.234567×1089 のように表示される。 (実際の出力がどうなるかは下のプログラムでわかる。)

(3.2.18)

#include <stdio.h>

int main() {
    int n;
    double x;

    x = 1;
    for (n = 0; n < 340; n++) {
        printf("%.207g\n", x);
        x = x / 10;
    }
}

どこかから先は 0 になる。

§3.3 数学関数

(3.3.1) 妙な名前だと思うかもしれないが, Cコンパイラに備え付けの関数で数学に関連するものを「数学関数」と呼ぶ。 数学関数の一部をあげる。 K&R2 では §B4 を参照。 数学関数を使うには #include <math.h> が必要である。 つまり,プログラムの冒頭は次のようになる。

#include <stdio.h>
#include <math.h>

int main() {

(3.3.2) x, ydouble 型, 返り値はすべて double 型である。

(3.3.3) 三角関数表の一部を出力するプログラム。

#include <stdio.h>
#include <math.h>

int main() {
    double x;

    for (x = 0; x <= 90; x = x + 5) {
        printf("%9f %6f\n", x, sin(x / 180 * 3.14159265358979));
    }
}

(3.3.4) 練習問題:二次方程式を解くプログラムを書け。 次に,解が虚数の場合も扱えるようにせよ。


岩瀬順一