C言語の課題を
(エクストリームに)解こう

    techno@Hirotaka Kawata
筑波大学 情報学群 情報科学類 1年
プログラミング入門1の課題

ここからみれます。
今回は、課題2 をやります。
  http://www.ialab.is.tsukuba.ac.jp/~maeda/class/prog1/
 
多分、みなさんなら、きっと簡単な課題でしょう!
僕には、むずかしすぎて...(笑

ちょっと、情報科学類1年生の内輪向けですが...
 
ここでやること。

プログラミング入門 1 の課題を、究極の形で、皆さんと一緒に解いて
きましょう。
 
条件:
  課題の本来の趣旨を守る
  無理しすぎない
  なるべく効率の良いコードを(?)
やってみよう。

そうしよう。
課題1

整数を入力し,入力された値が偶数か奇数かを判定して出力するプ
ログラムを作成しなさい.

よくありそうな、課題ですねー。
きっと頭にうかんだ演算子は...
%
    
 ですよね....
念のため、聞いておきます。

多分いちばん代表的なやり方は
 
if(i % 2) { 奇数 }
else { 偶数 }
 
% (あまりを求める演算子) 以外を使おうとした人。
いてほしい...
もし % が使えなかったら...

どうするおつもりですか?
 
偶数であるか奇数であるかを見分ける方法...
思いつきますか?

決して、ずっと / (割り算) していくなんてことはしませんよ。
例えばこんな例も...

0000 0001 -> 1
1111 1111 -> -1 (2の補数表現)

0000 0010 -> 2
1111 1110 -> -2 (2の補数表現)

0000 0011 -> 3
1111 1101 -> -3

なんだか気付きませんか?
デジタル人間なら、

2進数で考えよう。
 
偶数だと、必ず最下位ビットが 0 になる。
奇数だと、必ず最下位ビットが 1 になる。
 
という法則がある。
これを利用しよう。
マスクしましょう。

たとえば、5 が偶数か奇数か調べたいとき。

0000 0101 == 5

00000101 & 00000001
= 00000001

if(i & 0x01) {} じゃだめですか?
実際どっちがいいのよ...

んー。
% を、CPU がどんな計算してるんだかわからん。(知識不足)
たぶん、そんな命令もなかったはずだし。

マスクしたほうが、オールマイティーに早い。
とおもう。(どんなCPUでもってこと)
だけど、コード読みづらくなるかも...
コメント重要。
課題 2

100点満点の点数を入力し、 50点以上であれば、A、50点未満であ
れば、B、0から100の数字でなければ、errorと出力するプログラムを
作成しなさい.

おー。
これもなかなかおもしろそう。
一般的なやり方

if(a < 0 && a > 100) {
    if(a >= 50) { puts("A"); }
    else           { puts("B"); }
}
else { puts("error"); }
                              こんな感じだと思うのですが
                              いかがでしょう?
                              やり方は、いっぱいありますが。
だけど...

先程の例は、問題の文章通りに設計書を書いただけ。
 
ここから、人力最適化のお時間です。
コンパイラの最適化なんかあてにしちゃーいけません。
そもそも、コンパイラの最適化は、設計書に書いてあることに忠実
に、ちまちまやってるだけだし。
人力最適化。

if(a >= 0 && a <= 100) {
    if(a >= 50) { puts("A"); }
    else           { puts("B"); }
}
else { puts("error"); }             error は最大 2 回の比較。
                                    A, B は 3 回の比較。

                                    という感じになります。
人力最適化。

たとえば、違う実装。
if(i >= 50 && i <= 100)   { puts("A"); }
else if(i < 50 && i >= 0) { puts("B"); }
else                             { puts("error"); }

error は 3 回の比較。
A, B は最大 3 回の比較。
コードを短くすればいいというものではない。
人力最適化

if(i >= 50) {
  if(i <= 100) { puts("A"); }
  else { puts("error"); }
}
else {                          error は 2 回の比較
  if(i >= 0) { puts("B"); }     A, B も 2 回の比較
  else { puts("error"); }
}                               完璧じゃない?
                                ご想像にお任せします。
ただ、よくみると....

なんだか、行数が多い?
 
そう、コード量が若干多くなりました。
error を 2 回出力させているんです。
 
ということは、バイナリのサイズもでかくなる。
(goto でジャンプさせるってのもありだけど、たぶん else で JMP したあとに、
goto でもう一度 JMP するような気もするので、実行時間的には JMP 命令だからといえど、
最適なコードとは言えない...)
(そうだ、アセンブラだ!!!)
人力最適化はほどほどに。

もうちょっと、語ると...

なんだか、あとのコードになるほど、理解しづらくなったとは思いませ
んか?
 
変に最適化しすぎると、コードが読みづらくなって、コードも長くなっ
て... という状況になることが。
まあ、ほどほどに。
課題 3

二つの整数値A、Bを読み込んで,BがAの約数かどうかを判定する
プログラムを作りなさい.表示は以下のようにしなさい.
 
つまんねーな。
これが一番単純じゃん?
いきなりだけど、解答例。

if(a % b) {
  puts("n");
}
else {
  puts("y");
}
 
これ以上、どうしろっていうの...
ちょっと思いつきませんでした。
課題3について議論?

なんかありますかね?
最後に...

普通に解いて終わりでは、つまらない!
とおもった、techno のお遊びでした。

これからも、また不定期にやるかもしれません。(LT とかで)
プログラミングの面白さが少しでも分かってくれたら...

1年生いっぱいよんできてください。
以上。
ご清聴ありがとうございました。

C言語の課題を(エクストリームに)解こう #1