1999 年度「計算機基礎論3B」 1999-12-10

はじめに

きょうも課題があります。 先週の課題2は「くり返し」がテーマでしたが、 今週の課題3は「関数の自作」です。 やっぱりこれをやらないとC言語をやったという感じがしないので。 わかってしまえば簡単なものを選んだつもりですが、 あまり進んでいない人はちょっと大変かもしれません。 課題3の締め切りは来年に設定しましたので、 ゆっくり取り組んでください。

2000 年を前にファイルのバックアップをとろうという人へ

いわゆる 2000 年問題がありますので、 今年の冬休みはみなさんのファイルなどが消えてしまう可能性がいつもの年より少しは高くなっています。 心配な人はフロッピーにバックアップをとるといいでしょう。 MS-Windows 用のフロッピーディスクを1枚もってきてください。 生協などで売っています。

年が明けたら TeX をやります

年が明けたら、TeX をやる予定です。 TeX はテフまたはテックと読みます。 TeX は、ひとことでいってしまえば数式を得意としたワープロです。 数学の本を写してみる練習をしますので、 来年のこの時間は適当な数学の本をもってきてください。 午前中の授業の教科書でもいいです。 数学教室の図書室で借りるも可ですが、 初めて借りるときは手続きにちょっと時間がかかりますから、 前もって手続きをしておくことをすすめます。 手続きの際は学生証を持参してください。

一月の最初の授業は 21 日(金)だと思いますが、 学部の掲示に従いますので、よく注意しておいてください。

来週も授業はあります!

きょうの課題3が終わってしまった人向きの何かを用意します。

Cプログラムのレポートを提出する際の注意

プログラムや出力結果をメールに貼り付ける際は、 左のほうを空けないでください。 私はこのページに出力結果の例を書くとき左を空けることがありますが、 メールは別です。 特にプログラムは切り抜いてコンパイルしてチェックしますので、 行頭に全角スペースがはいっていたりすると消すのに手間がかかります。

mod N の四則

N をひとつの自然数とするとき、 数学での mod N の四則については知っているものとします。 Z/NZの元を格納するための特別な型はC言語にはないので、 (さしあたっては)int で代用し、 簡単なプログラムを書いてみましょう。 次のプログラム例は ~iwase/prog に置いてあります。

== modadd.c ==========================================================
#include <stdio.h>

main() {
    int i, j, n;

    printf("mod N での足し算の表を出力するプログラムです.\n");
    printf("1 以上 25 以下の正の数 N を入力してください.\n");
    scanf("%d", &n);

    printf("   |");                     /* 罫線を引く */
    for (j=0; j<n; j++) {               /* 上の「見出し」*/
        printf("%3d", j);
    }
    printf("\n");                       /* putchar('\n'); でも同じ */
    printf("---+");                     /* 罫線を引く */
    for (j=0; j<n; j++) {
        printf("---");
    }
    printf("-\n");                      /* ここまで罫線 */

    for (i=0; i<n; i++) {               /* ここから計算 */
        printf("%2d |", i);             /* 左の「見出し」と罫線 */
        for (j=0; j<n; j++) {
            printf("%3d", (i+j) % n);
        }
        printf("\n");
    }
}
======================================================================
ユーザが 1 から 25 までの正の数 N を入力すると mod N の足し算の表を出力します。

※ プログラムの中では N を格納する変数は n にした。 変数名は小文字にするのが普通だから。

実際の計算をしているのは二重になった最後の for ループだけで、 それ以外の部分は罫線をひくことに費やされています。

実行すると次のようになります。

    mod N での足し算の表を出力するプログラムです.
    1 以上 25 以下の正の数 N を入力してください.
    5
       |  0  1  2  3  4
    ---+----------------
     0 |  0  1  2  3  4
     1 |  1  2  3  4  0
     2 |  2  3  4  0  1
     3 |  3  4  0  1  2
     4 |  4  0  1  2  3

※ 25 までなのはそれ以上だと一行が 80 字以内に収まらないから、 というだけの理由。

上の出力結果では各行各列に数が重複なく並んでいますが、 「(i+j) % n」を「(i*j) % n」と変えて掛け算の表にしてみるとどうでしょうか?

※ というのは数学の問題として聞いている質問。

引き算の表がどうなるかは足し算と同じぐらい自明かもしれませんが、 プログラムを書く上では問題があります。 JIS のC言語の規格では / や % について

整数同士の除算で割り切れない場合、(中略) 一方のオペランドが負の値をもつ場合、 / 演算子の結果が代数的な商以下の最大の整数とするか、 又は代数的な商以上の最小の整数とするかは、処理系定義とし、 % 演算子の結果の符号も処理系定義とする
としているからです。

※「オペランド」とは a/b の a や b のこと、 「処理系定義」とは(簡単にいえば)コンピュータごとに決めてください、 ということ。

つまり、(-2) % 3 は -2 かも知れないし 1 かも知れない、 ということです。

実習室の WS では (-2) % 3 は -2 になるので、 答えを 0, 1, ..., N-1 の範囲にしようとすると一工夫必要です。 tmp を int 型の変数として

            tmp = (i-j) % n;
            if (tmp < 0) {
                tmp = tmp + n;
            }
            printf("%3d", tmp);   
とするか
            printf("%3d", ((i-j) % n + n) % n);  
とするか。 ほかにうまい方法があるかもしれません。 考えてみてください。

課題3:mod N での割り算をする関数を書き、メールで送る

mod n で a を b で割った“商”を返す関数 int moddiv(int a, int b, int n) を書いてください。 言い方を変えれば、 この関数の返す値 x は 0 から n-1 までの整数であり b*x と a は mod n で等しい、 ということです。 n は自然数、 a は任意の整数、 b は 0 以外の任意の整数とします。 n が正であることおよび b が 0 でないことはこの関数を呼び出すほうが注意する事項と考えることにします。 関数の中でチェックするには及びません。 x に二つ以上の候補がある場合は最も小さいものを返してください。 また、もしもそのような x が存在しなければ -1 を返してください。

この問題は不定方程式 b*x + n*y = a を解くことにあたります。 うまい方法もありますが、 ここでは「x = 0 から x = n-1 まで順番に試す」 というやり方で構いません。

必ずお試し用 main をつけて、 コンパイルして動作を確かめてから提出してください。 また、 main がどんなことをするプログラムかは各自で違いますから、 私にわかるように

などしてください。どれか一つで結構です。 なお、 関数 moddiv の仕様は上に書いた通りなので説明をつけなくてもいいですが、 興味のある人は簡単にコメントをつける練習もしてみてください。 上で説明した「x = 0 から x = n-1 まで順番に試す」 以外の方法を使った人はそのやり方を書いてください。

できあがったら、プログラムをメールで送ってください。 今回は出力例はいりません。

※ 感想などがあれば添えてもよいが、 無理に書かなくてよろしい。

宛て先は私(岩瀬)の実習用アカウント cf7175 です。 実習用 WS からは To: のところに cf7175 と書くだけで届きます。 Subject は「kadai 3」 (すべて小文字、スペースにも注意!)にしてください。 自分の学籍番号、氏名(フルネーム)も忘れずに。

※ こういう場合の氏名は大学に届けてある通りの文字で書く。 たとえば漢字に変換するのがめんどうだからとカナで書いたりすると受け取った側は名簿との照合に余計な時間がかかる。 ただし名前に JIS コード以外の文字が使われている人は別。

私はメールを受けとったら数日以内にプログラムを読み、 コンパイルし、チェックして、 「OK」なり「やり直し!」なりの返事をメールで送ります。 「やり直し!」だった人は「OK」 がもらえるまでやり直して再提出しないと課題をこなしたことになりません。

提出期間は二つに分かれています。

です。 年末年始をはずしてあるのは、 今年はいわゆる「2000 年問題」 があるので万が一のメールの紛失を恐れているからです。 第一の提出期間中にメールをくれた人には 12 月 27 日(月)12 時までに返事を送ります。 締め切り間際にメールを送ってくれた場合は、 私からの返事は締め切り後になるかもしれません。

※ それで「やり直し!」だとまずいので、できれば早めに提出するように!

総合情報処理センターは年末年始は休みになるはずです。 センターの掲示などに注意してください。 私が上に書いた提出期間はセンターの休みの情報とは無関係に決めたものですので、 たとえば 1 月 5 日にセンターが開いていることを保証するものではありません。。

おまけ

mod N で a, a*a, a*a*a, ... を出力するプログラムもおもしろいです。 「原始根」と関係がありますが、 「原始根」については適当な初等整数論の教科書を見てください。


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