2007 年度「計算数学1」 2007-04-12

§35 はじめに

(35.1) この授業は、内容としては前学期の「計算機基礎論3B」の続きである。

(35.2) センターのコンピュータの更新に伴い、使い方が少々変わった。 アカウントが二種類になった。 「コンテンツ ID」と「ネットワーク ID」である。 この授業では前者を配布する。 そのアカウントは今学期はこの授業専用であるが、 来学期の「総合演習」に引き継がれる。 後者は、学生証を用いてセンターのロビーなどで自分で取得するものであるが、 なくてもこの授業にはさしつかえない。 ログイン・ログアウトなどの基本的な操作については下の表を参照のこと。 この授業では Linux を用いる。

(35.3) 画面の上の端にある横に細長い棒は「GNOME パネル」という(らしい)。 この棒の上には、アイコンをドラッグしてくることができる(ようだ)。

(35.4) GNOME パネルの左上のほうの、テレビの受信機のようなアイコンをクリックすると 「GNOME 端末」が開く。これが、先学期の「ターミナル(Terminal)」に相当する。 文字の大きさは「表示」の中の「拡大」(あるいは「縮小」、および「通常サイズ」)で変えられる。 (これだと端末を開くたびに文字の大きさを設定し直さなければならない。 それがいやな人は (6.5) を見てまねすること。)

(35.5) その右のアイコンはメールソフト Sylpheed であるが、これは使わないのでクリックしないこと。 (してしまった場合は、ひたすら「キャンセル」して閉じる。)

(35.6) (いわゆる)ホームページを見るためのブラウザは 「アプリケーション」→「インターネット」→「Mozilla ウェブブラウザ」である。 この授業のページは http://wwwedu.ipe.kanazawa-u.ac.jp/%7Ez07eat01/07cma1/ である。 見て、ブックマークに追加しておくこと。

(35.7) この授業では使わないが、 今度のシステムでは、インターネットで学外のページを見るためには認証が必要となった。 その際に用いる ID は「ネットワーク ID」である。 認証のページは http://www.gipc.kanazawa-u.ac.jp/stu/ である。ここもブックマークに追加しておくとよい。 左上の「認証画面へ」をクリックすると 「不明な認証局により認証された Web サイト」というウィンドウが出るが、 気にしないことにして「OK」を押す。 「Input:」に ID を入力して「Submit」を押し、 「Password:」にパスワードを入力して「Submit」を押す。 そして、「Standard Sign-on」の左の白丸をクリックして中に点をいれ、 それから「Submit」を押す。 ここまでがうまくいったら、認証のページは閉じてしまってよい。 あとは従来通りに学外のページが見られる。 終わって席をたつ前にも同様の手続きが必要である。 ただし、「Standard Sign-on」の代わりに「Sign-off」を選ぶ。

(35.8) (いわゆる)日本語入力の On/Off は Shift+Space または「半角/全角 漢字」または「変換」。

(35.9) テキストエディタは emacs を用いる。§9 を参照のこと。 ただし、(9.3) にあった、一度だけ行なう設定はそこに書かれているものとは異なり、 「cp ~z07eat01/.em* . &> /dev/null」である。 なお、前には emacs の中でも Linux の日本語入力機能が使えたが、 今回は使えないようである。(9.10) にある、emacs の日本語入力機能を使うこと。 ただし、未変換文字が下線つきで表現される、 変換中の候補は地の色が違って表現される、などの点で先学期とは異なっている。 ほかにも異なる点があるので、実例で説明しよう。 たとえば「たなかみさこ」を変換して「田中美佐子」にしようとしてスペースバーを押すと 「他中味さこ」となるので、 Ctrl+I で切れ目を左に移動し「田中みさこ」とする。 次に Ctrl+F を押すと「田中」が確定され、 「みさこ」は変換されて「美佐子」となる。 この操作の際に「→」キーを押してはいけない。 なお、切れ目を右に移動するのは Ctrl+O である。

(35.9) メールには Active! mail を用いる。 サーバは「mailedu.ipe」を選ぶ。 メールアドレスは z07ea0??@mailedu.ipe.kanazawa-u.ac.jp となる。 §23 にならって設定を行なえ。 (今学期は「オプション」の中の「メール転送」が効くようになっている。 担当者(岩瀬)のアドレスは z07eat01@... である。)

【電源オフ】

本体(机の下)の電源ランプがついていなければこの状態。 本体の電源スイッチを入れれば次へ進む。 (電源ランプはついているがディスプレイの画面が真っ黒のときは、 ディスプレイのスイッチ(画面右下)がはいっているか確かめよ。 はいっている場合は省エネ状態と思われるので Shift キーを押してみよ。)

【OS選択メニュー】

キーボードの矢印キーでどちらかを選んで Enter キーを押す。 なにもしなければ自動的に Windows に進む。 (実習室によって違うかもしれないので注意。)

【Windows のログイン画面】

Ctrl キーと Alt キーを押したまま Delete キーを押す。

  • 「ユーザー名」にユーザ ID を、
  • 「パスワード」にパスワードを
打ち込み、「OK」をクリックする。 (パスワードは画面では「*」で表示される。)

(この状態から電源オフの状態にしたいときなどは「シャットダウン」をクリック。 そのあとの操作法は下の枠内の説明を参考にせよ。 また、この授業では行わないが、 ネットワーク ID で Windows にログインすることも可能である。 その場合は「ログオン先」を「IPE」から「INS」に変更せよ。)

途中、「Input "1" or "2"」では、 コンテンツ ID でログインするのでキーボード左上の「1」を打って Enter キーを押す。 (この授業では行わないが、 ネットワーク ID でログインするなら「1」の代わりに「2」を打つ。 また、何もしなければ、60 秒後、自動的に、直前に起動したほうへ進む。)

【Linux のログイン画面】

  1. 「ユーザ名」にユーザ ID を打ち込んで Enter キーを押し、
  2. 「パスワード」にパスワードを打ち込んで Enter キーを押す。 (パスワードは画面では「*」で表示される。)

(この状態から電源オフの状態にしたいときなどは「システム」をクリックし、指示に従う。 下の枠内の説明を参考にせよ。 「停止」が「シャットダウン」にあたる。)

【Windows の画面】

アイコンがたくさん出て、マウスカーソル近くの砂時計の絵が消えたらログイン完了。

ここでいろいろな仕事をさせる。

終わる前には自分で起動したアプリケーションをすべて終了させておくこと。

左下の「スタート」をクリックし、出てきた「シャットダウン」をクリックする。 すると「実行する操作を選んでください」が出てくるので青色の帯状の部分かその右の 「▼」をクリックし、出てきた中から次の一つを(クリックして)選び、 「OK」をクリックする。

  • 「……のログオフ」... Windows のログイン画面に戻る。
  • 「シャットダウン」... 電源オフの状態に戻る。
  • 「再起動」... OS選択メニューに至る。

【Linux の画面】

アイコンがいくつか出てきたらログイン完了。

ここでいろいろな仕事をさせる。

終わる前には自分で起動したアプリケーションをすべて終了させておくこと。

左上の「アクション」をクリックし、出てきた「ログアウト」をクリックする。 そして次の一つを(クリックして)選び、それから「OK」をクリックする。

  • 「ログアウト」... Linux のログイン画面に戻る。
  • 「シャットダウン」... 電源オフの状態に戻る。
  • 「コンピュータの再起動」... OS選択メニューに至る。

§36 整列(ソート)とは

(36.1) この授業のテーマは「整列」、すなわち、

 88 98 20 82 31 53 22 10 12 91 92 79 95 50 19 58
のような有限数列が与えられたとき、 これを小さい順に並べかえることである。 「ソート (sort)」とも言う。 ただし、 「整列」「sort」の日常用語としての意味はこれとは少し違う。

(36.2) 次のプログラムを元に、 順次、書き足したり改良したりしてゆくが、 ソースファイル名は指定しないので、適当に決めよ。 ファイル名をプリントにメモしておくと、 あとで探すときに楽かも知れない。

(36.3) ※ 実際の仕事でコンピュータを使う際には、 数だけを並べかえたのでは意味がないのが普通である。 たとえば、 受験者一人一人に対応するデータ 「受験番号、国語の得点、数学の得点、英語の得点、合計点」 の組が受験者の数だけあり、それらを合計点の順に並べかえる、 というような操作をすることが多いであろう。 これから考えるのは、(いわば)合計点だけを並べかえるもので、 実地にはあまり役立たない。 また、Excel (エクセル)などの表計算ソフトはソート機能を持っているので、 応用上はそれらを使えばよい。

(36.4) ※ 同じ値があった場合にどちらを先にもってくるかは、 この授業では「どちらでもよい」としておく。 (実用上は、それも問題になることがある。 たとえば、昔は、試験のあと、 成績優秀者の氏名と得点を掲示したことがあった。 それには、 元々は名簿の順に並んでいるデータを得点順に並べかえて上から何人かを選べばよいのだが、 もしも同点の学生がいた場合は

    100 点 伊藤*、田中**、藤村**
     95 点 ...
のように名簿の順にしておくのが普通である。 これを
    100 点 藤村**、伊藤*、田中**
     95 点 ...
とすると 「同じ 100 点だけど藤村君が先頭にきているのは何か理由があるのかな」 と思われるのでうまくないかもしれない。)

§37 ソートプログラムの準備

(37.1) 次のプログラム例を見ながら、 以下の説明を読むこと。 また、このプログラムは完全ではないので、 指示に従って自分で書き加える必要がある。

#include <stdio.h>
#include <stdlib.h> /* srand() */
#include <time.h>   /* time() */

#define N 20

void init(void);
void print(void);

int a[N+1];     /* これで a[1] ... a[N] が使える。a[0] は使わない */

main() {
    init();     /* 初期化 */
    print();    /* 画面表示 */
}


void init(void) {
    int i;

    {   /* この中カッコの中(乱数の種の設定)はわからなくてもよい */
        unsigned seed = (unsigned)time(NULL);   /* 現在時刻を取得して */
        printf("乱数の種は %u です.\n", seed);
        srand(seed);                            /* それを乱数の種に */
    }

    a[0] = 0;                       /* 念のためこうしておく */
    for (i = 1; i <= N; i++) {
        a[i] = rand() % 99 + 1;     /* N が小さいとき */
/*      a[i] = rand();              /* N を大きくして実験するとき */
    }
    return;                         /* この return は普通は書かない */
}


void print(void) {
    /* ここに関数 print() の本体を書き加える */
}

(37.2) 数列は、int 型の配列 a[ ] に入れることにした。 その大きさ N は、 容易に変えられるよう、 #define を使って書いた。

(37.3) C言語の配列は添字が 0 から始まるので、普通は a[0] から a[N-1] までを使うことになるが、 ここではわかりやすくするため a[1] から a[N] までを使いたい。 それには、 「int a[N+1];」と宣言することで a[0] から a[N] までを用意し、 最初の一つ、a[0] は使わなければよい。

(37.4) 初めに、この配列を「初期化」する。 「初期化」とは、一般には初めのデータを代入することである。 ここでは乱数で初期化する。 この部分は関数 init() として独立させた。 新しく習うことがらもあるので、以下でくわしく説明する。

(37.5) void init(void) の最初の void は、 この関数には返り値がないことを示す。 つまり、この関数はこれが行なう機能に意味があるのであって、 power(2, 3) のように計算した結果の値を使うものではない。 (いままで出てきた関数では、printf() の使い方がそれに似ていた。 実は printf() にも返り値があるのだが、 授業では「出力する」という機能だけを使っていた。)

(37.6) 小カッコの中の void は、引数がないことを示す。 すなわち、使うときは「init();」のように使う。 小カッコの中には何も書かない。

(37.7) この関数の本体は §24 で示したものに似ている。 「a[i] = ...」という行が二つ並んでいる理由について、 以下で説明する。

(37.8) プログラムを書いている間は、N の値を小さくとり、 ソートの過程の途中で、 何度か配列の値を全て出力してソートが進んでいることを確認する。 この段階では、 配列を初期化する値は 100 未満で十分だし、 そのほうが大小の比較が容易である。 また、あとで説明するが、 これらの値は 1 以上としておくほうが、 プログラムの間違いを見つけやすい。 以上から、値の範囲は 1 以上 99 以下としよう。

(37.9) プログラムが完成したら、 N の値を何百万、何千万と大きくしての実験も行なう。 その際には、 同じ値が代入されている項がたくさんあるとうまくないので、 値の範囲は広ければ広いほどよい。 よって、配列には乱数の値をそのまま代入する。 そこで、値の範囲は 0 以上 RAND_MAX 以下となる。 (RAND_MAX については §24 を参照。)

(37.10) そのため、init() の中には「a[i] = ...」 となっている行が二つあるのであった。 いまどの段階の作業をしているかに応じて、 一方を選び、他方はコメントとする。 すなわち、/**/ とで囲む。

(37.11) プログラムの末尾にある関数 void print(void) の本体を書け。 この関数は、配列に格納されている値を (36.1) に示したようなスタイルで、 すなわち、スペースで区切って一行に、出力するものとする。 一桁のときも二桁分のスペースで出力されるようにせよ。 そのとき、十の位を 0 とするかスペースにするかは各自の趣味にまかせる。 (printf() の中で、 %d の代わりに %02d または %2d と書けばよい。)

(37.12) これで上のプログラムはコンパイル可能になった。 main() では init()print() を続けて呼んでいるだけなので、 起動するたびに異なる乱数列が表示されるプログラムができたはずである。 (非常に短い間隔で起動すると、同じ乱数列になることもある。)

(37.13) ※ init() の 「a[i] = ...」 のもう一方を使うと出力はどうなるか?

§38 互換を行なう関数 swap()

(38.1) ソートプログラムの多くは、 互換、 すなわち配列の二つの要素の交換からできている。 そこで、まず、互換を行なう関数を書こう。

(38.2) §37 で完成させたプログラムに、 関数 void swap(int i, int j) を書き加えよ。 この関数は、 要素 a[i]a[j] の値を入れかえるものとする。 ij が範囲内にあるかどうかはチェックしなくてよろしい。 プロトタイプ宣言もつけ加えること。

(38.3) ヒント:

    a[i] = a[j];
    a[j] = a[i];
ではだめである。(31.4) の、 うまく動作しない swap() が参考になるかもしれない。 最初は ij とは等しくないと仮定して書いてみて、 書けたものが ij が等しいときも正しく動くかどうか考えよ。

(38.4) もちろん、 この関数を呼び出してみなければ、正しく書けたかどうかはわからない。 それには、main() の最後に

    swap(3, 8);
    print();
のように付け加えればよいだろう。 これで a[3]a[8] が入れかわっていれば OK である。 ほかに swap(8, 3)swap(3, 3) も試すこと。

(38.5) ※ swap(-1, 8)swap(3, N+1) を実行させるプログラムは、 配列の添字が範囲外になるので誤ったプログラムである。 そのことを理解したうえで、そういうプログラムを書いて実行してみよ。 おそらく、ここのコンピュータでは、エラーメッセージなどは出ないであろう。 前者では a[8] に、後者では a[3] に、 配列の“外”にあった値がはいってくることになるが、 それは 0 であることが多い。確かめよ。

(38.6) ※ これから書くプログラムは、少しずつ複雑になってゆく。 その際、間違って swap(-1, 8)swap(3, N+1) を実行するようなプログラムを書いてしまうことはよくある。 配列を初期化する際、値の範囲を 1 から 99 までとしておいたのは、 print() が 0 を出力したら間違いだとすぐにわかるためであった。


岩瀬順一