反復

反復(Loop)

ループとは繰り返し文のことである.俗に反復処理(iteration)と呼ばれていて構造化プログラミングの根幹をなしている.反復処理にはwhile文,do-while文とfor文がある.

その昔,コンピュータの性能が悪かった時代,また,しっかりした制御構造および反復処理がプログラミング言語に用意されたいなっかた時代,多くの人がgoto文と呼ばれるジャンプを使ってプログラムを書いていた.しかし,1960年代後半goto文によって書かれたプログラムは,他人にとってわけのわからないプログラム(スパゲッティプログラム)になり,諸悪の根源であると言われ始めた.このことより,ソフトウェアも通常の工業製品と同じように生産管理が必要であるという観点から「ソフトウェア工学」が生まれた.このような流れの中から,分かりやすいプログラムを書くための方法論がダイクストラによって提案され,その中の1つに「構造化プログラミング」がある.

構造化プログラミング(Structured Programming)

構造化プログラミングは,基本的に3つの構造から成り立っている.

連接(sequence):プログラムは上から下に流れる
選択構造(Selection)
反復処理(Iteration)

構造化定理によると,すべてのアルゴリズムはこの3つの構造だけで記述できることが証明されている.

現在,C++言語を学んでいる学生は,上記の1,2,3だけでアルゴリズムを書く練習をして欲しい.なお,今やプロジェクトの規模が大きくなり,構造化プログラミングを用いても,ある時点からは複雑さがプログラマの管理できる限界を超えてしまっている.そこで生まれたのが,オブジェクト指向という考え方である.C++言語はオブジェクト指向言語としての顔も持つ.このテキストで,徐々にオブジェクト指向言語としてのC++についても学んで行く.

while文(while Statement):〜の間〜する

while文の構文は
    while(条件式){
        式文;
    }

の形をとる.while文は条件式が成立しなければ0を返し,次の式文は実行されないでwhile文の次に続く文に飛ぶ.条件式が成立すると0以外を返し,式文が条件が0になるまで実行される.したがって,while文は式文を一つも実行せず,while文を終えることもある.

例題 4..1   数列の和
$1^2 + 2^2 + 3^2 + 4^2 + 5^2 + \cdots + 100^2$を求めるプログラムを
whileループを用いて作成せよ.

解答 求める和をsとおくと, $s = \sum_{k=1}^{100}k^{2}$と表せる.$k=1$のとき,$s = 1^{2}$. $k=2$のとき, $s = 1^{2} + 2^{2}$となるが,$k=1$のとき,$s = 1^{2}$としておくと,$k=2$のとき, $s = s+2^{2}$と表せることに気づいただろうか.これが数列の和のコーディングの方法である.

この問題では$j$番目が$j^2$より和は$s=s+j^2$と表せる.これを$j$が1から100まで行えばよいので,擬似コードで書くと,
j $\leftarrow$ 1, s $\leftarrow$ 0;
while (j $<=$ 100) do
s $\leftarrow$ s + $j^{2}$;
j $\leftarrow$ j+1;
となる.これをC++言語に書き直すと,

#include <iostream>
using namespace std;
int main()
{
    int j=1,s=0;
    while(j <=100){
        s = s + j*j;
        j+=1;
    }
    cout << s << endl;
}

実行結果

\begin{figure}% latex2html id marker 1168
\centering
\includegraphics[width=8.3cm]{CPPPIC/reidai4-1.eps}
\end{figure}

一般に,j番目がある関数f(j)で表せるならば,上の式文s = s + j*j;をs=s+f(j)と書き換えればよい.

練習問題 4..1   数列の和
$100^2 + 99^2 + 98^2 + 97^2 + 96^2 + \cdots + 1^2$をこの順で求めるプログラムを
whileループを用いて作成せよ.

例題 4..2   逆数の和
s = 1 + 1/2 + 1/3 + $\cdots$ 1/nを求めるプログラムを作成せよ.ただし,nは逆数の和sが入力された整数より大きくなる最初の整数とする.

解答 逆数の和をsumとし,入力された値をboundとすると,
i $\leftarrow$ 0;
while(sum $<$ bound) do
sum $\leftarrow$ sum + 1.0/++i;
で逆数の和が入力された値より大きくなる最初の整数iを求めることができる.C++言語に書き直すと,

#include <iostream>
using namespace std;
int main()
{
    int bound;
    cout << "正の整数を入力して下さい: " ;
    cin >> bound;
    double sum = 0.0;
    int i = 0;
    while(sum < bound){
        sum += 1.0/++i;
    }
    cout << "最初の" << i << "項の逆数の和は"
    << sum << endl;
}

実行結果

\begin{figure}% latex2html id marker 1190
\centering
\includegraphics[width=8.0cm]{CPPPIC/reidai4-2.eps}
\end{figure}

練習問題 4..2   逆数の和
sum = 1 + 1/2 + 1/3 + $\cdots$ + 1/1000とrsum = 1/1000 + 1/999 + $\cdots$ + 1をfloatで宣言し求めるプログラムを作成せよ.sumとrsumの値を比較せよ.また,どちらが正確か考えよ.さらに,このとき発生した誤差はなんと言われるか.

例題 4..3   素因数分解
2以上の整数を読み取って,素因数分解するプログラムを作成せよ.

解答 素因数とは,与えられた和nの約数でかつ素数のことである.素数とは,それ自身と1でしか割れない数である.ただし,1は素数には入れない.例えば,nを120とすると,nの約数は,2,3,5であり,120の素因数分解を行なうと, $120=2 {3}\cdot 3 \cdot 5$となる.

2 120
2 60
2 30
3 15
  5

では,素因数分解をおこなうアルゴリズムを考えてみる.データnを読み取って,2から順に割っていき,ある数mで割れたら商n/mを新たなnとおき,さらにmで割り切れなくなるまで続ける.mで割り切れなくなったらmを1増加し,nを割っていくことを繰り返し,商nが1になるまで続ける.このとき発生した,mが素因数となる.表示するときに,最後の数字のあとには「,」を付けないようにする. では,プログラムを作成しよう.

#include <iostream>
using namespace std;
int main ()
{
    int m=2,n;
    char c = ' ';
    cout << "整数を入力してください.";
    cin >> n;
    while(n!= 1){
        while(n%m == 0){
            n = n/m;
            cout << c << m;
            c = ',';
        }
        m++;
    }
}

実行結果

\begin{figure}% latex2html id marker 1215
\centering
\includegraphics[width=7.9cm]{CPPPIC/reidai4-3.eps}
\end{figure}

最後の約数の後にはカンマをつけないようにするには,カンマを約数の前に出力させる.そこで,問題になる最初のカンマを空白文字にしておけばよい.

例題 4..4   素数判定
与えられた数が素数かどうか判定するプログラムを作成せよ.

解答 素数とは1とその数自身でしか割ることができない数である.そこで,与えられた数をnumとし,numを2から順にnum-1までの数で割っていき,割り切れなければ素数であることが示せる.numを割る数をdivisorとし,divisorが2から順に割っていくということをプログラムで書くと,divisorがnより小さい間は実行すると表せるのでwhile(divisor $<$ num)と書ける.次に,whileループを実行中あまり(remainder)の最小値(min_remainder)を求める.やがて,divisorがnumに到達しwhileループが終了する.その後,あまりの最小値(min_remainder)の値が0か0以外かを調べれば,numが素数かどうかの判定ができる.プログラムに書くと次のようになる.

#include <iostream>
using namespace std;
int main ()
{
    int num,min_remainder=1,remainder,divisor=2;
    cout << "整数を入力してください" << endl;
    cin >> num;
    if(num == 1){
        min_remainder=0;
    }
    while(index < num){
        remainder = num%divisor;
        if(remainder < min_remainder){
            min_remainder=0;
        }
        divisor++;
    }
    if(min_remainder !=0){
        cout << num << "は素数です" << endl;
    }
    else{
        cout << num << "は素数ではありません" << endl;
    }
}

実行結果

\begin{figure}% latex2html id marker 1225
\centering
\includegraphics[width=8.0cm]{CPPPIC/reidai4-4.eps}
\end{figure}

もう少しよく考える.2で割り切れる数(偶数)は2以外素数ではない.また,素数でない数(合成数)の約数は対になっているので,nの平方根までの数をチェックすればよいことが分かる.この考えを用いて,もう一度プログラムを作成しよう.

//素数判定改良版
#include <iostream>
using namespace std;
int main ()
{
    int num,min_remainder=1;
    cout << "整数を入力してください" << endl;
    cin >> num;
    if(num == 1 || num > 2 && num%2 == 0){
        min_remainder=0;
    }
    int divisor = 2;
    while(divisor*divisor < num){
        int remainder = num%divisor;
        if(remainder < min_remainder){
            min_remainder=0;
        }
        index++;
    }
    if(min_remainder !=0){
        cout << num << "は素数です" << endl;
    }
    else{
    cout << num << "は素数ではありません" << endl;
    }
}

ループによる実行回数のコントロール(Control of Execution by Loop)
こういうプログラムを実行させるとき,その都度エグゼファイル(実行ファイル)を立ち上げなければならず,面倒であると感じた人は少なくないだろう.そこで,好きなだけ実行し,止めたくなったら止めることができる方法を考えてみる.

whileループは条件式が満たされている間,whileブロックを実行する.そこで,例えば条件式を,入力されたデータが正の値の間は正しいとすれば,入力データが負になるまでは実行することができる.例えば,

int choice=0;
while (choice >= 0) {
    cout << "続ける場合は正の数字をやめるときは負の数字を入力してください";
    cin >> choice;
}
と上のプログラムに入れておく.これにより正の数を入力している間は続行できるし,止めたければ負の数を入力すればよい.

ループからの脱出(Terminating a Loop)

while,for,do-while文を用いるとき,気をつけなければならないのは,無限ループにおちいりやすいことである.このようなことを避けるために,ループの条件は点ではなく,区間とするべきである.特に,while(1),for(;;)などの無限ループではじめ,Ctrl+Cで脱出するようなプログラムは初心者は可能なかぎり書くべきではない.

さて,ループからどうしても脱出する必要があるときには,if文とbreak文を用いる.例えば,キーボードからデータを入力して,0を入力したら終了したい場合を考えよう.入力されたデータを変数dataに取り込むとすると,データがある限り読み込むとすると, whileループの条件はcin $>>$ dataと書ける.ここで,cinは呼び込みに成功すると1を返し,失敗すると0を返すので,このループは入力がある限り続行される.次に,入力が0のとき,if文の条件が真となるので,次のbreak文に到達し,ここで,whileループから脱出する.

while(cin >> data)
{
    if (data == 0){
        break;
    }
}

ループから脱出するもう一つの方法は,breakの代わりに関数exit (0)を用いる方法である.この方法はループから脱出するばかりでなく,プログラムを終了してしまう.

練習問題 4..3   次の条件を満たす正の整数を小さい方から3個見つけるプログラムを作成せよ.

正の整数nは3で割ると1余り,5で割ると2余り,7で割ると3あまる数である.つまり,n%3 == 1, n%5 == 2, n%7 == 3を同時に満たす数である.

例題 4..5   平均,最大・最小値
データをキーボードから入力し,入力したデータの平均,最大値,最小値を求めるプログラムを作成せよ.ただし,0を入力した時点で終了するとする.

解答

  1. 最大値と最小値を見つけるには,まず,1個データを読んで,その値を最大値と最小値にしておく.これを行うプログラムは次のようになる

    int max,min,ini;

    cin $>>$ ini;

    max = min = ini;

  2. 0を入力したら終了しなくてはならないので,while文の中にif文とbreakを 使ってwhileからの脱出をはかる.関数cinはうまくいけば1を返し,失敗する と0を返す関数なので,次のようになる.
    int data;
    while(cin $>>$ data){
    if(data == 0){
    break;
    }
    }
  3. 0以外のときは,新しく読み込んだデータと1個目のデータとを比較し,新し いデータが1個目より大きければ,それをmaxとおき,小さければminとおく.次に データを読み込んだときに,読み込んだデータとmax,minを比較する.次のようにな る.
    if(data $>$ max) {
    max = data;
    }
    if(data $<$ min) {
    min = data;
    }
  4. 平均は読み込んだデータの和sumを読み込んだ回数nで割ってやればよい.
    double sum;
    sum += data;
    n++;
    cout $<<$ "平均は" $<<$ sum/n $<<$ endl;

以上をまとめると次のようなプログラムになる.

#include <iostream>
using namespace std;
int main ()
{
    int data,ini;
    cout << "数学の成績を入力してください." << endl;
    cin >> ini;
    double sum = ini;
    int max = min = ini;
    int n = 1;

    while(cin >> data){
        if(data == 0){
            break;
        }
        else {
            sum += data;
            n++;
            if(data > max){
                max = data;
            }
            if(data < min) {
                min = data;
            }
        }
    }
    cout << "平均点は" << float(sum/n) << endl;
    cout << "最高点は" << max << endl;
    cout << "最低点は" << min << endl;
    cout << "回数は" << n << "回" << endl;
}

実行結果

\begin{figure}% latex2html id marker 1263
\centering
\includegraphics[width=8.0cm]{CPPPIC/reidai4-5.eps}
\end{figure}

練習問題 4..4   平均,最大・最小値
データをキーボードから入力し,入力したデータの平均を求めるプログラムを作成せよ.ただし,負の値を入力した時点で終了するとする.

do ... while文(do ... while Statement)

do ... while文の構文は
    do {
        式文;
    }while(条件式);
最低一回は必ず実行される.

例題 4..6   数列の和
$1^2 + 2^2 + 3^2 + 4^2 + 5^2 + … + 100^2$を求めるプログラムをdo...whileループを用いて作成せよ.

解答

==============================================

#include $<$iostream$>$
using namespace std;
int main()
{
int j=1,s=0;
do {
s = s + j*j;
j+=1;
  } while(j$<$=100);
cout $<<$ s $<<$ endl;
}
==============================================

実行結果

\begin{figure}% latex2html id marker 1290
\centering
\includegraphics[width=8.0cm]{CPPPIC/reidai4-6.eps}
\end{figure}

練習問題 4..5   数列の和
$100^2 + 99^2 + 98^2 + 97^2 + … + 1^2$を求めるプログラムをdo...whileループを用いて作成せよ.1190

階乗0!,1!,2!,3!,...は次の漸化式により定義されている.

$\displaystyle \left\{\begin{array}{l}
0! = 1\\
n! = n(n-1)!
\end{array}\right.$

例えば,$n = 3$のとき,

$\displaystyle 3! = 3(3-1)! = 3(2!) = 3(2)(2-1)! = 3(2) = 6$

となるので,6は3の階乗である.

例題 4..7   階乗
入力された整数までの階乗を全て求めるプログラムを作成せよ.

解答 階乗$n!$$f(n)$とすると, $f(n) = nf(n-1)$で与えられる.そこで, $f = f(n-1) = (n-1)!$とおくと,階乗を求める構文は,繰り返しを用いて
f = f * n
となる.この$n$を1から順に増やしていけば,全ての階乗を求めることができる.よって,擬似コードで書くと
入力 bound;
i $\leftarrow$ 1;
f $\leftarrow$ 1;
do
f $\leftarrow$ f $\ast$ (++i);
while(i $<$ 入力);
となる.例題5.11では再帰(recursion)を用いてn!を求めている.

#include <iostream>
using namespace std;
int main()
{
    long bound;
    cout << "正の整数を入力して下さい: ";
    cin >> bound;
    cout << bound << "までの階乗は \n 1";
    long f= 1, i = 1;
    do {
        f *= ++i;
        cout << ", " << f;
    }while(f < bound);
}

実行結果

\begin{figure}% latex2html id marker 1318
\centering
\includegraphics[width=11.7cm]{CPPPIC/reidai4-7.eps}
\end{figure}

練習問題 4..6   階乗
${}_{n}C_{r}$を求めるプログラムを作成せよ.ここで,1198

$\displaystyle {}_{n}C_{r} = 1\cdot\frac{n-1+1}{1} \cdot \frac{n-2+1}{2} \cdot \frac{n-3+1}{3} \cdots \frac{n-r+1}{r}$

for文(for Statement)

for文の構文は
    for (初期値;条件式;更新){
        式文;
    }

の形をとる.

  1. 初期値はコントロール変数の初期化を行う.2つ以上の初期値を書く場合はカンマで区切る.
  2. 条件が成立すると式文は実行される.条件が成立しなければループを終了.
  3. コントロール変数が更新される.
  4. 2,3を繰り返す.

例題 4..8   数列の和
$1^2 + 2^2 + 3^2 + 4^2 + 5^2 + … + 100^2$を求めるプログラムをforループを用いて作成せよ.

解答

#include <iostream>
using namespace std;
int main()
{
    int s=0;
    for(int j=1; j<=100;j++){
        s = s + j*j;
    }
    cout << s << endl;
}

実行結果

\begin{figure}% latex2html id marker 1355
\centering
\includegraphics[width=8.5cm]{CPPPIC/reidai4-8.eps}
\end{figure}

練習問題 4..7   forループを用いた数列の和
$1/1^2 + 1/3^2 + 1/5^2 + … + 1/101^2$を求めるプログラムをforループを用いて作成せよ.1201

例題 4..9   ループコントロール変数の再利用
$1^2 + 2^2 + 3^2 + 4^2 + 5^2 + ... + 100^2$$50^{2}$までとそれ以降に分けて求めるプログラムをforループを用いて作成せよ.

解答

#include <iostream>
using namespace std;
int main()
{
    int s=0;
    for(int j=1; j<50;j++){
        s = s + j*j;
    }
    for(int j=50; j<=100;j++){
        s = s + j*j;
    }
    cout << s << endl;
}

実行結果

\begin{figure}% latex2html id marker 1375
\centering
\includegraphics[width=8.6cm]{CPPPIC/reidai4-9.eps}
\end{figure}

この例題ではコントロール変数iが2つのforループで用いられている.ANSIのC++以前のコンパイラではエラーになる.

例題 4..10   階乗再び
入力された整数までの階乗を全て求めるプログラムをforループを用いて作成せよ.

実行結果

\begin{figure}% latex2html id marker 1384
\centering
\includegraphics[width=11.7cm]{CPPPIC/reidai4-10.eps}
\end{figure}

解答

#include <iostream>
using namespace std;
int main()
{
    long bound;
    cout << "正の整数を入力して下さい: ";
    cin >> bound;
    cout << bound << "までの階乗は \n 1";
    long f= 1;
    for(int i=2; f < bound; i++) {
        f *= i;
        cout << ", " << f;
    }while(f < bound);
}

forループはなかなかしなやかで,ステップ幅が1より大きいforループも可能である.

例題 4..11   素数判定再び
与えられた数が素数かどうか判定するプログラムをforループを用いて作成せよ.

解答 4以下の整数で1以外は素数です.5以上の素数は全て奇数.そこで,まず,偶数か調べ,偶数なら素数でないことが分かる.次に,奇数で割って余りが0になれば素数ではない.

#include <iostream>
using namespace std;
int main()
{
    int n;
    cout << "整数を入力してください" << endl;
    cin >> n;
    if(n < 2){
        cout << n << "は素数ではありません" << endl;
    }
    else if(n < 4) {
        cout <<< n << "は素数です" << endl;
    }
    else if(n%2 == 0){
        cout << n << " = 2*" << n/2 << endl;
    }
    else {
        for(int d=3; d < n/2; d+=2){
            if(n%d == 0) {
                cout << n << " = " << d << "*" << n/d << endl;
                exit(0);
            }
        }
        cout << n << "は素数です" << endl;
    }
}

実行結果

\begin{figure}% latex2html id marker 1395
\centering
\includegraphics[width=11.7cm]{CPPPIC/reidai4-11.eps}
\end{figure}

例題 4..12   反復練習
次のような形を表示するプログラムを作成せよ.

実行結果

\begin{figure}% latex2html id marker 1404
\centering
\includegraphics[width=3.3c...
...dai4-12.eps}
\includegraphics[width=3.4cm]{CPPPIC/reidai4-12-1.eps}
\end{figure}

解答

実行結果1>
cout $<<$ "*";
で1つのアスタリスクを表示することができる.では*****と表示するにはどうすればよいだろうか.確かにcout $<<$ "*****";でもできるが,これではアスタリスクを任意の数だけ表示せよとなると,不可能である.そこで,cout $<<$ "*";を5回実行すると*****が表示できることに気づけばよい.5回実行させるには,例えばforループを用いるとfor(i=1;i$<$=5;i++)と書けばよいことを知っている.次に,第1回目には,1つのアスタリスク,第2回目は2つのアスタリスクと増やすにはどうすればよいだろうか.i$<=$5の5の代わりに,jを用いて,

for(int j=1;j$<$=5;j++){

for(int i=1;i$<$=j;i++){

cout $<<$ "*";

}

}

と書けば,外側のループが1回終了する間に内側のループがj回実行されることになる.内側のループが終了したら,endl;で改行する.ではプログラムを作成しよう.

#include <iostream>
using namespace std;
int main()
{
    for(int j=1; j <= 5; j++){
        for(int i=1; i <= j; i++){
            cout << "*";
        }
        cout << endl;
    }
}

実行結果2>

実行結果1との違いはアスタリスクを右詰めで表示しているところである.そこで,アスタリスクの前にブランクを挿入するコマンドを追加すればよい.

#include <iostream> using namespace std;
int main()
{
    int k;
    for(k = 4; k >= 0; k$--$){
        for(int j=1; j <= k; j++){
            cout << " ";
        }
        for(int i=1; i <= 5-k; i++){
            cout << "*";
        }
        cout << endl;
    }
}

練習問題 4..8   反復練習
次のような形を表示するプログラムを作成せよ.

\begin{figure}% latex2html id marker 1433
\centering
\includegraphics[width=3.0cm]{CPPPIC/renshu4-7.eps}
\end{figure}

continue文(continue Statement)

break文はループブロックのbreak文以下をスキップし,ループの外の次の文にジャンプする.continue文もbreak文のように,ループブロックをスキップするが,ループを終了する代わりに,ループの次の反復にうつる.

例題 4..13   continueとbreak文
次のプログラムを実行するとどんな結果が得られるか考えよ.

#include <iostream>
using namespace std;
int main()
{
    int n;
    for(; ;){
        cout << "整数を入力して下さい:";
        cin >> n;
        if(n%2 == 0) continue;
        if(n%3 == 0) break;
        cout << "\tループの最後" << endl;
    }
    cout << "\tループの外" << endl;
}

解答 2でも3でも割り切れない数を入力すると,2つのif文の条件を満たさないので,ループの最後が出力される.2で割り切れる数を入力すると,continueで残りの式文をスキップし,ループに戻る.3で割り切れる数を入力すると,break文でループの外に出る.,

実行結果

\begin{figure}% latex2html id marker 1443
\centering
\includegraphics[width=8.0cm]{CPPPIC/reidai4-13.eps}
\end{figure}

擬似乱数(Pseudo-Random Number)

コンピュータの応用で最も重要なものの1つに現実世界のシミュレーションがある.ハイテクリサーチや開発の多くがシミュレーションを用いて,どのようにシステムが動いているかを実際に実験を行わずに研究している.

シミュレーションはコンピュータにより発生させられた乱数(random number)を用いて,現実世界の確かでない部分をモデル化する.当然,コンピュータは本当の乱数を発生することはできない.なぜならば,コンピュータは決定論的であるからである.しかし,乱数のようにみえる数を発生させることは可能である.つまり,ある区間で,一様分布していて,認識可能なパターンを含まない数の列(擬似乱数)を発生させることは可能である.

標準C++ライブラリ$<$cstdlib$>$では,擬似乱数を発生させる関数rand()を定義している.rand()関数は0からRAND_MAXまでの数をランダムに発生させる.RAND_MAXも$<$cstdlib$>$で定義されている定数である.

例題 4..14   乱数の発生
rand()関数を用いて8個の乱数を発生させ,また,RAND_MAXの値を表示せよ.

解答 擬似コードで書くと
for i=0,1,2,...,7 do
rand();
で8個の乱数を発生することができる.

#include <iostream>
#include <cstdlib>
using namespace std;
int main()
{
    for(int i=0; i < 8;i++){
        cout << rand() << endl;
    }
    cout << "RAND_MAX = " << RAND_MAX << endl;
}

実行結果

\begin{figure}% latex2html id marker 1460
\centering
\includegraphics[width=8.6cm]{CPPPIC/reidai4-14.eps}
\end{figure}

rand()関数により発生される乱数は,この例でも分かるように同じものである.なぜならば,同じ種(seed)を用いているからである.これでは,擬似乱数と呼ぶことができない.そこで,毎回違う乱数を発生させるために,srand()関数を用いて自分自身でseedを変更することができる.

例題 4..15   seedを用いた乱数の発生
srand()関数を用いて乱数を発生させよう.

解答

#include <iostream>
#include <cstdlib>
using namespace std;
int main()
{
    unsigned seed;
    cout << "seedを入力して下さい:" ;
    cin >> seed;
    srand(seed);
    for(int i=0; i < 8;i++){
        cout << rand() << endl;
    }
}

実行結果

\begin{figure}% latex2html id marker 1470
\centering\includegraphics[width=8.6cm]{CPPPIC/reidai4-15.eps}
\end{figure}

\begin{figure}% latex2html id marker 1474
\centering
\includegraphics[width=8.6cm]{CPPPIC/reidai4-15-1.eps}
\end{figure}

例題 4..16   システム時計を用いた乱数の発生
システム時計からseedを設定し乱数を発生させよう.

解答

#include <iostream>
#include <cstdlib>
#include <ctime>
using namespace std;
int main()
{
    unsigned seed = time(NULL);
    cout << "seed = " << seed << endl ;
    srand(seed);
    for(int i=0; i < 8;i++){
        cout << rand() << endl;
    }
}

実行結果

\begin{figure}% latex2html id marker 1484
\centering
\includegraphics[width=8.6cm]{CPPPIC/reidai4-16.eps}
\end{figure}

例題 4..17   決められた範囲での乱数の発生
システム時計からseedを設定し,乱数を発生範囲をその範囲の最小値と最大値を入力することにより求め,その範囲で乱数を発生するプログラムを作成せよ.

解答 指定された範囲の最小値と最大値より,範囲rangeはrange = 最大値 - 最小値 + 1で求まる.次にrand()で発生させた乱数を0からrange-1までに変換するには,rand()%rangeで余りを調べればよい.

#include <iostream>
#include <cstdlib>
#include <ctime>
using namespace std;
int main()
{
    unsigned seed = time(NULL);
    cout << "seed = " << seed << endl ;
    srand(seed);
    int min,max;
    cout << "乱数の範囲の最小値と最大値を入力して下さい: ";
    cin >> min >> max;
    int range = max - min + 1;
    for(int i=0; i < 20;i++){
        int r = rand()%range + min;
        cout << r << " ";
    }
    cout << endl;
}

実行結果

\begin{figure}% latex2html id marker 1494
\centering
\includegraphics[width=13.8cm]{CPPPIC/reidai4-17.eps}
\end{figure}

練習問題 4..9   モンテカルロ法
モンテカルロ法を用いて$\pi$の値を求めよ.ただし,乱数の個数はRAND_MAXまでの数なら利用者が選べるようにする.0から1の間の乱数はrand()/(RAND_MAX+0.1)で発生させることができる.

モンテカルロ法(Monte Carlo Method)とは,ある問題を確率(乱数)を用いて解くことをいう.

1辺が1の正方形の中に半径1の1/4円を描く.ここで,点$(x,y)$$x$$y$の値を0〜1の一様乱数を発生させて生成すると, $x^{2} + y^{2} \leq 1$ならば,1/4円の内部にあり,それ以外は正方形の内部で,1/4円の外部にあることになる.n個の点を発生させ,1/4円の内部にばらまかれた点の数を$r$,1/4円の外部にばらまかれた点の数を$s$とすると,確率的に次の関係が成り立つ.

$\displaystyle \frac{r}{r+s} = \frac{r}{n} = \frac{1/4円の面積}{正方形の面積} = \frac{\pi}{4}$

これより,円周率$\pi$

$\displaystyle \pi = \frac{4r}{n}$

で求まる.

確認問題 4..1  

1. while文の条件式が最初から正しくない(つまり零)場合どうなるか述べよ.

2. 次のプログラムコードはどこが間違っているか.

while (n <= 100)
sum += n*n;

3. 次のプログラムコードはどこが間違っているか.

int main()
{
    const double PI;
    int n;
    PI = 3.141592653;
    n = 22;
}

4. break文はどのようにしてループにコントロールをもたらすか述べよ.

演習問題 4..1  

1. eを式,sを文とするとき,次のforループをwhileループを用いて書き直しなさい.

a. for(;e;)s
b. for ( ; ; e) s

2. 次のforループをwhileループを用いて書き直しなさい.

for (int i=1; i <= n; i++)
    cout << i*i << " ";

3. 次のコードをトレースしなさい.
float x = 4.15;
for (int i = 0; i $<$ 3; i++)
x *= 2;

4. 入力した10進数の数字を反転して出力するプログラムを書きなさい.

5. Euclidの互助法は2つの整数の最大公約数を求めるのに用いることができる.そのアルゴリズムは例えば,210と56の最大公約数ならば,

$\displaystyle 210$ $\displaystyle =$ $\displaystyle 56*3 + 42 \cdots (1)$  
$\displaystyle 56$ $\displaystyle =$ $\displaystyle 42*1 + 14 \cdots (2)$  
$\displaystyle 42$ $\displaystyle =$ $\displaystyle 14*3 + 0 \cdots (3)$  

より,14は(3)の式の両辺の約数である.次に14は(2)の式の両辺の約数である.なぜなら,14は42の約数より,14は56の約数となるからである.最後に14は(1)の式の両辺の約数である.つまり,14は210と56の約数である.また,14より大きな数で,210と56の約数があるとすると,その数は(1)の式より42の約数となる.ということは(2)の式より,14の約数となる.このことから,14は210と56の最大公約数である.

次に,210と56の最大公約数を$(210,56)$と表すと,

$\displaystyle (210, 56) \rightarrow (56,42) \rightarrow (42,14) \rightarrow (14,0)$

となる.このアルゴリズムを実装せよ.