計算数学演習第5回

多重くりかえし文、while文

今日の演習

今日は以下の内容で演習を行います。

  • 繰り返し文(復習、応用)
  • 多重繰り返し文(多重ループ)
  • 扱える数値の大きさ

多重繰り返し文(多重ループ)

for 文、while 文は入れ子にすることができます。
例えば、次のように for 文の中にさらに for 文があるような繰り返し文を、2重ループと呼びます。
もちろん、3重、4重ループも作ることができます。
次のプログラムを forfor-1.c として打ち込み実行してください。
ただし、実行する前にどのような出力が出るか予想してから実行してください。
予想通りであれば、繰り返し文の理解は十分できているといえるでしょう。

forfor-1.c

#include <stdio.h>
 
int main() {
    int i, j;
    
    for (i = 1; i <= 3; i++) {
        for (j = 1; j <= 5; j++) {
            printf("i=%d, j=%d\n", i, j);
        }
    }
    
    return 0;
}

実行すると分かりますが、多重ループでは、もっとも内側のループ(繰り返し文)が先に実行されます。

課題1

整数 i, j が 0 < i < 50, 0 < j < 50 を満たすとする。
正の整数 n を入力すると、n = i + j を満たす全ての i, j を表示するプログラムを作成せよ。
(ヒント:2重ループと if 文)

課題2

整数 i, j, k が 0 < i < 50, 0 < j < 50, 0 < k < 50 を満たす。
正の整数 n を入力すると、n = i + j + k を満たす全ての i, j, k を表示するプログラムを作成せよ.

課題3

(2以上の)整数 n を入力すると、n 以下の全ての素数を表示するプログラムを作成せよ。
(前回の素数判定プログラムを利用、 繰り返し.)

while 文

while 文は次のように使われます。

while (条件) {
    ブロック文
}

条件部分に書かれた条件式が真である間ブロック文が繰り返し実行されます。
先の for 文を使った例も while 文を使って書き換えることができて、

#include <stdio.h>
 
int main() {
    int i;
    for (i = 1; i <= 10; i++) {
        printf("Hello!\n");
    }
    
    return 0;
}

#include <stdio.h>
 
int main() {
    int i;
    i = 1;
    
    while (i <= 10) {
        printf("Hello!\n");
        i = i+1;
    }
    
    return 0;
}

と同等です。
このように、for 文と while 文は実質的に全く同じものです。
プログラムが見やすくなる方を選んで使えばよいでしょう。
上記2つの例では先の for 文を用いたプログラムの方が意味を理解しやすいかと思います。
例えば、次のような例では while 文が適しています。
while-1.c として打ち込んで実行してみてください.

while-1.c

#include <stdio.h>
 
int main() {
   /* 変数の型の宣言 */
    
    int n, sum;
   /* 変数の初期化 */
    n = -1;
    sum = 0;
    
    /* n が 0 でなければ、以下の処理を繰り返す n が 0 ならば以下の処理をやめる */
    while (n != 0) {
       /* 変数入力 */
        printf("整数を入力してください ");
        scanf("%d", &n);
        
       /* 入力された数を足す */
        sum = sum + n;
    }
    
    /* 結果表示 */
    printf("合計=%d\n", sum);
    
    return 0;
}

このプログラムはキーボードから整数値を入力していき、その数字を加算します。
入力された数値が0であれば繰り返し処理を終了して合計値を表示するものです。
はじめに n = 0 とせずに n = -1 としているのは n = 0 としてしまうと、 while (n != 0) の条件式がはじめから偽となってしまうからです。
この場合 -1 という数値にはあまり意味がありません。0 でなければよいのです。
もちろん同様の動作をするプログラムを for 文を用いて作成することも可能です。

課題4

自然数Nの入力を促し、n! > Nを満たす最小の自然数nを求めるプログラムをwhile文を用いて作成せよ。
(n!はnの階乗)

扱える数値の大きさ

コンピューターは無限に大きな数値を扱うことはできません。
以下に変数が扱えるおおよその範囲を示しておきます。
(こういった話は実は機種に依存するので、注意が必要。)

データ型(変数型) 表現 範囲
int 整数型 -2147483648~2147483647
long 倍長整数型 -2147483648~2147483647
float 単精度実数型 およそ 10e-38~10e38 (有効桁7桁)
double 倍精度実数型 およそ 10e-308~10e308 (有効桁15桁)

ここで、10eN は 10Nをあらわすとします。整数型は以外と小さな数値までしか扱えないことがわかります(20億程度)。
それでは困るので、最近のコンピューターはより大きな整数値を扱えるようになってきています。
(整数計算をする部分を自分でプログラミングすれば、いくらでも大きな整数を扱えるようになります。
例えば Mathematica などはより巨大な整数値を扱えます。もちろん、それにも限界はありますが・・。)

課題5(すこし難しめ)

    \[\frac{\pi^2}{6} - \sum_{k=1}^{N} \frac{1}{k^2} < 0.01\]

を満たす、最小のNを求めるプログラムをwhile文を用いて作成せよ。
ただし、π=3.14159265とおくこと。
注意:kをintで宣言した場合、1/(k*k)の演算結果は整数となってしまう。これを回避するためには、
・整数型の変数と実数型の変数を演算(+, -, *, /)した結果は、実数型となることを利用して、1.0/(k*k)とおく
・ 1.0/( (double)(k)*(double(k)) )とおく(「定積分の近似値」のコメント参照)
などの方法がある。

レポート課題

4択問題を表示し、解答が正解であれば終了、正解でなければ正解するまで再解答させるプログラムを作成せよ。
また、プログラムには適宜コメント文を入れ、第3者が見ても分かりやすいプログラムにすること。
※ プログラムの中身はもとより、問題のユニークさも採点対象となる。
提出締め切り:2023年12月22日(金) 午後18時


プログラムの停止の仕方とファイルの消し方(前回の復習)

プログラムは正しく動作し、正しく停止することが重要ですが、停止しないプログラムを書くのは大変簡単です。停止しないプログラムはたいていの場合、プログラミングミスが原因です。例えば、次のプログラムは停止しません。。

#include <stdio.h>
 
int main() {
    int i;
    
    for (i = 1; i >= 1; i++) {
        printf("Hello!\n");
    }
    
    return 0;
}

条件式 i >= 1 が真でありつづけるため停止しないわけです。(後の述べる扱える数値の大きさを超えるとおかしなことになるので、そこで停止する可能性はありますが・・。)
今後、色々なプログラムを作ってもらいますが、何らかのプログラミングミスで停止しないプログラムになっていて、それを実行してしまった場合は、
Ctrl + c
(Ctrl キーを押しながら c のキーを押す)
によって強制的に終了させることができます。覚えておいてください。
また、エラーのあるプログラムによっては core というファイル名のファイルを作成してしまう場合があります。このファイルは、当面不要なので、消してしまいましょう(ファイルサイズが大きいので)。以前、0 での割り算を行うプログラムを実行した人は、多分この core ファイルができているはずです。


今日学んだ事

  • 繰り返し文を使えるようになりました
  • より複雑なプログラムを理解し、つくることができるようになりました

Department of Mathematical and Life Sciences, Graduate School of Integrated Sciences for Life, Hiroshima University