1999 年度「計算機基礎論3B」 1999-11-19

はじめに

きょうの分はどちらかといえばオプション的な内容になります。 前のが終わっていない人は前のに取り組んでください。 ただし、最初の部分は目を通しておくことを勧めます。

プログラム例に「当たり前」のものが多い理由について、 説明し忘れていました。 プログラムを書くとき、普通は間違えます。 「こんなに自明な内容なんだから間違うはずがない」 と思うような簡単なプログラムでも間違えるものです。 ですから、最初にプログラミングを学ぶときは 「当たり前」のプログラムで、 自分で暗算で求めた答えと同じ答えが出て 「このプログラムは合っている」と安心するようなものを取りあげるべきだ、 というのが私の考えです。 みなさんも、まずはそういうプログラムを書いてください。 電卓で検算するのもよいことです。

自分で実験してわかったことでひとに伝えたいことがある人は、 ぜひ自分のホームページに書いてください。 私(岩瀬)の書いたプログラム例をこうなおしたらこううまくいった、 あるいはうまくいかなくなったなど。

授業時間以外に自習していて質問がある場合は、 メールをください。できる限り答えます。

課題1、見ました

いくつか感想を述べておきます。

ホームページについて。 手の込んだものを作った人、あっさり作った人、 いろいろでしたが、 出した人は全員、私が思っていた基準は満足していたので、 まったく同じ点数です。 遅れた人は少し減点しておきましたが。 今後もこの方針でやりますので、 そのつもりで取り組んでください。 うんとやってみたい人は自分の満足のためにがんばる、 よい点をとるために無理して時間をかける必要はない、というわけです。

index.html の上での一行が長い人が多かったように思います。 エディタでものを書くときはなるべく画面の横幅以内で改行しましょう。

メールについてですが、Subject が指定と違う人が何人かいました。 今回は“手動”で処理したので問題ありませんでしたが、 Subject を見て自動で処理を行なう相手に出した場合は正しく読んでもらえない可能性があります。 また、学籍番号・名前を書いてない人もいました。 それから、「名前」を書くよう求められた場合は、 姓(氏)と名を両方書くべきだと私は考えます。 口頭で「お名前は?」と聞かれたら姓だけを言うこともありますけどね。 今度からは「氏名」と明記するように、私も気をつけます。

フロッピーの使い方

何度か質問のあった、 第四実習室の WS でのフロッピーの使い方を、 計算科学科の後藤先生から教えてもらいました。 ディスクは、 MS-Windows 用のを用意すればよいようです。 この実習の最後には自分の書いたものなどをフロッピーに入れてもって帰るといいかも知れません。 私の書いたこの(いわゆる)ホームページも、 もちろん持っていっていいですよ。

※ ただし、その際、ウィルスには各自で注意。 実行ファイルをやり取りしない限り感染しないらしいが、 私はちょっと不安……。

※ いわゆる 2000 年問題が心配な人は、 年末に一度ファイルのバックアップをとるほうがいいかもしれない。

フロッピーをドライブにいれ、 ファイルマネージャの「ファイル」「フロッピーのチェック」を順に選びます。 するとファイルマネージャの新しいウィンドウが開き、 そこにフロッピーの内容が表示されます。 マウスを使って操作するもよし、 ウィンドウの枠に表示されるように、 フロッピーはディレクトリ /floppy の下(正確なディレクトリ名は忘れた ^^;) にマウントされますから、 そこに移って操作してもよいでしょう。

※ 「マウント」とは、 フロッピーなどがしかるべきディレクトリの下にあるように見えるようにすることをいう。 よって、 マウント後「cd /floppy」などとしたあとで既出のファイル操作コマンドを使えば、 フロッピーディスク内のファイルが読み書きできる。

※ この操作中、フロッピーがパーミッション 777 になるようなのでちょっと恐い。 chmod も効かないようだ。 フロッピーからデータを読み出すだけの場合、 書き込み禁止にすればよさそうだが、なにやらエラーメッセージがでる。 無視するか?

フロッピーをフォーマットするときはファイルマネージャから 「ファイル」「フロッピーのフォーマット...」 とします。 確認メッセージのほかに、 「フォーマットを選択してください」 と出ますので、 「UNIX」「DOS 高密度」「NEC-DOS 中密度」 から選んでください。 最近のフロッピーはフォーマットして売っているので、 自分でフォーマットする必要はないでしょう。

最後に、「ディスクの取り出し」をクリックして、 そのウィンドウが閉じてからフロッピーを取り出すようにしてください。 そうでないと、/floppy の下に自分のディレクトリができて消えなくなります!

※ 経験者は語る。:-)

メールを自分のファイルにコピー・移動する方法について

前回、Mail というコマンドについて(telnet のところで)説明しましたが、 実際にやってみると、 メールをファイルにセーブすることが不可欠でした。 そうでないと nkf で漢字コードを変換することができないからです。 くわしいやり方は昨年度のページを見てください。 (先週の分は、ftp のところにも間違いがありました。すみません。)

そう気づいたら、 メールツールでもメールをファイルに移しておく方法を知っておくほうがいいと思えてきましたので、 説明します。 大事なメールを失わないよう、十分注意して実験してください。

メールツールの「ファイル名」のところに、 メールをコピー・移動したいファイル名を書きます。 (既存のときは「ファイル名」の右の▼をクリックして選ぶことも可能。) メール一覧からひとつのメールを選び (選ぶと枠で囲まれる)、 そこで「コピー」を押し、コピーしたいファイル名で離すと、 そのファイルにメールがコピーされます。 新規作成の場合、 どうやらそのファイルのパーミッションは 600 になるようです。 親切ですね。 「移動」はメールが一覧から消えるところだけが違います。 「ロード」を押しファイルを選ぶと、 そのファイルの中に含まれるメール一覧が出ます。

メールをたくさんやりとりする人は、 こうやってファイルに移しておくほうが整理しやすいと思います。 また、そうしておけば、実習の最後にメールをフロッピーにいれてもってゆくことができます。

乱数で遊ぼう

きょうのプログラム例も ~iwase/prog にあります。

「乱数」とは、 サイコロを繰り返し振って出た数を並べたような無規則な数の列のことです。 C言語には rand() という、乱数を発生させる関数が備わっています。 rand() は int 型を返す関数で、 0 から RAND_MAX までの整数をランダムに返します。 引数はありません。

※ 定数 RAND_MAX をプログラムの中で使うには stdlib.h を #include する必要がある。 しかし、rand() が stdlib.h を必要とするので、 よほどアマノジャクなプログラムでなければ RAND_MAX だけのために stdlib.h を #include するようなことはしないだろう。

rand() は決まった種(たね)から計算を繰り返して数を発生させるので、 プログラムを動かすたびに毎回同じ乱数になってしまいます。 それでは乱数にならないので、 下のプログラム例の 「現在時刻で乱数の種をセット」 とある行のようにすればよいでしょう。 その行をコメントアウト (/* ... */ で囲んでコメントにし、 実行されないようにしてしまうこと) したりして、実験してみてください。

※ コメントを入れ子にすることはできないので、下の例では行頭に「/*」をつけるだけでよい。 つまり、

/*    srand((unsigned int) time(NULL));   /* 現在時刻で乱数の種をセット */ */
はダメ、
/*    srand((unsigned int) time(NULL));   /* 現在時刻で乱数の種をセット */
は OK.

なお、その行にはまだ説明していないことが使われていますが、 今は説明しないことにします。

== random.c ===================================================================
#include <stdio.h>
#include <stdlib.h>     /* rand(), etc */
#include <time.h>       /* time() */

main() {
    int i;

    printf("RAND_MAX は %d です.\n", RAND_MAX);

    srand((unsigned int)time(NULL));    /* 現在時刻で乱数の種をセット */
    for (i=0; i<20; i++) {              /* 20 個の乱数を発生させ印字 */
        printf("%d ", rand());
    }
    printf("\n");
                                        /* 以下は失敗。なぜだ? */
    for (i=0; i<20; i++) {
        srand((unsigned int) time(NULL));
        printf("%d ", rand());
    }
    printf("\n");
}
===============================================================================

次のプログラムは、 0 以上 1 以下の乱数を二つ続けて発生させ、 それを平面上の点の座標とみなして、 原点を中心とする半径 1 の四分の一円内にはいる確率から円周率の近似値を出しています。

== pi.c =======================================================================
#include <stdio.h>
#include <stdlib.h>     /* rand() */
#include <time.h>       /* time() */

#define N 30000

main() {
    int i, count;
    double x, y;

    srand((unsigned int)time(NULL));    /* 現在時刻で乱数の種をセット */
    count = 0;
    for (i=0; i<N; i++) {
        x = (double)rand()/RAND_MAX;    /* [0,1] の乱数に変換 */
        y = (double)rand()/RAND_MAX;
        if (x*x + y*y <= 1) {           /* 四分の一円内にあればカウント */
            count++;
        }
    }
    printf("円周率は約 %f です.\n", (double)4*count/N);
}
===============================================================================

1 から 6 までの乱数を発生させるには 「rand() % 6 + 1」とすればよいでしょう。 RAND_MAX はたいてい 6 で割り切れないので、 厳密には 1 から 6 までが同じ確率で出ないのですが、 ゲームなどに使う分には構わないでしょう。

みなさんが小学生を教えていると仮定して、 乱数を利用して 「3桁×3桁」の掛け算の問題を出すプログラムを書いてみませんか? (課題ではありません。)

     325
  × 283
 --------
のように出題し、次にリターンを押すと
     325
  × 283
 --------
     975
   2600
   650
 --------
   91975
のように筆算の結果が出力される、というものです。

※ ……と思ったのだが、 いままで出てきたことだけでは 「リターンを押すと」が書けないかも。 だったら答えのほうだけ出力するプログラムでもよい。

※ トランプゲームを作る際には、 カードは int 型の変数一つで表わすと簡単だ。 値は 0 から 51 までとし、 4 で割った商に一を加えたものをカードの数(Aなら 1 とか)、 4 で割った余りを種類(0 がスペードとか、適当に決める) とすればよい。 これはメモリが足りなかった時代の古典的なテクニックらしい。 (まだ教えてないけど、char 型にすればさらにメモリが節約できる。)

※ スペードやハートの記号は文字としてはないので、 「●」「▲」で代用するなどしてほしい。

※ ポーカーゲームの 「ワンペア」「ツーペア」「スリーカード」「フルハウス」「フォアカード」 の判定は、 「5枚のカードのうち、同じ数をもつ2枚の組」 の数で判定できる。 これも昔からあるテクニックらしい。

※ 一組のトランプを“切る”方法については、 各自で考えて実験してみてほしい。


岩瀬順一 <iwase@kappa.s.kanazawa-u.ac.jp>