2016 年度「計算数学」 2016-10-21

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

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

#include <stdio.h>

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.6) の for ループの「継続条件」と同じである。

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

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

(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>

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>
 
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>

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) 自然対数の底 e の値を,e = 1 + 1/1! + 1/2! + 1/3! + ... として計算する。

#include <stdio.h>

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.4) 同様。階乗の計算も行なうバージョン。

#include <stdio.h>

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.5) 階乗を計算するプログラム。出力の「inf」は無限大の意。 負の無限大なら「-inf」である。

#include <stdio.h>

main() {
    int n;
    double factorial;

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

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

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

#include <stdio.h>

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.7) 1 + 1/2 + 1/3 + ... を計算するプログラム。

#include <stdio.h>

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.8) このあとは,double 型の性質を知るためのプログラムである。 以下のプログラムをコピペして動かして,「こんなものか」と思っておけばよい。

ここで,端末の「編集」「プロファイルの設定」「スクロール」で, 512 行を 1024 行にしておこう。 (これは,端末の巻き戻せる行数を 1024 に増やしたのである。)

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

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

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

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

#include <stdio.h>

main() {
    int n;
    double x;

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

(3.2.13)

#include <stdio.h>

main() {
    int n;
    double x;

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

(3.2.14)

#include <stdio.h>

main() {
    int n;
    double x;

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

(3.2.15)

#include <stdio.h>

main() {
    int n;
    double x;

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

(3.2.16)

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

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

(3.2.17)

#include <stdio.h>

main() {
    int n;
    double x;

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

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

§3.3 数学関数

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

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

(3.3.3)

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

main() {
    double x;

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

(3.3.4) 数学関数を使ったプログラムをコンパイルする際は, 「cc ファイル名.c -lm」のように, 「マイナス,エル,エム」をつける。

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


岩瀬順一