プログラミングパズル
高専プロコンや歯みがきロボコンと、自分の関連するイベントもひとまず終了。 ただ、高専プロコンで課題自由部門の本戦参加ができなかったり、 歯みがきロボコンでもzeroからライントレースを作ったりという、 実践で頼りがいのある学生さんが少ない。
難しいことをああだこうだと指導するよりも、 興味を持って実際にプログラムを書く事が先決だろうと考える。 ゲームを作ってみるのも1つだけど、数値をうまく操る技術がないと、 キャラクターの動きも単調でつまらないはず。 ということで、プログラミングパズルにチャレンジしてもらうのがいいのではないかと考える。問題も極めてシンプルに….
プログラミングパズル
- x*yの計算をするプログラムを作成せよ。
ただし、乗算演算子*や除算演算子/は使わない事。 1000*1000の実行にかかる時間を、他の人のプログラムと比べてみること。 - 上記のプログラムを、for,while,do-while,goto文を使わずに記述せよ。
- 99の階乗(99!)を全ての桁を計算せよ。
ただし、言語付属のBIGNUM系パッケージは使わない事。
99の階乗は、Common Lispであれば、勝手にBIGNUMを使ってくれるので、 → (defun fact(n) (if (= n 0) 1 (* n (fact (- n 1))))) → (fact 99) 933262154439441526816992388562667004907 159682643816214685929638952175999932299 156089414639761565182862536979208272237 582511852109168640000000000000000000000 でも、BIGNUM相当を自分ですらすら書けて、普通のプログラマー。
ワード境界とビットフィールド
構造体の説明の後半として、メモリの使用量と関係深いワード境界とビットフィールドの説明を行う。 最初に、主記憶の不足とプログラムの関係として、仮想メモリとメモリ不足時にハードディスクアクセスが多発し、スピード低下について述べる。
ワード境界
struct A { char name[ 3 ] ; // イニシャルと点数の構造体? int point ; } a[ 4 ] ; 境界無視 境界配置 |NNNP| |NNNx| N:name |PPPN| |PPPP| P:point |NNPP| |NNNx| |PPNN| |PPPP| |NPPP| |NNNx| |PNNN| |PPPP| |PPPP| |NNNx| |PPPP|
CPUに比べて、主記憶の速度は遅いため、メモリアクセスは必要最小限にしたい。 しかしながらワード境界を無視すると、a[0].point の取得には、2回のメモリアクセスが 発生するため、処理速度が低下する場合がある。 このため、name 3文字の後ろに1byteの空きを設けて、ワード境界をまたがらない様に 構造体の要素を配置するのが一般的。
この話の前に、char=8bit=1byte=0..255(-128..0..127),32bitCPUなら、 int=32bit=4byte=-2^31..0..2^31-1といった復習を簡単に行った。
メモリインタリーブとよばれる方式を使うと、 ワード境界があっても最小限のメモリ参照で済むが、ハードウェアが複雑化する。 情報処理技術者試験を受けるのであれば、インターリーブも理解しておくこと。
ビットフィールド
struct YMD { int year ; int month ; int day ; } ;
といった構造体では、12byte のメモリを使用するけど、メモリ使用を減らすには、 year=0..2047,month=0..15,day=0..31と考えれば、20bitで十分。 メモリの使用量を減らすために、year , month , day を1つのint型で覚えるには?
ymd = year *10000 + month*100 + day ; printf( "月=%d" , (ymd % 10000) / 100 ) ;
という方法もあるけど、2進数として年月日を覚えるのであれば、
// YYYYYYYYYYY000000000 // or MMMM00000 // or DDDDD ymd = (year << 9) | (month << 5) | day ; printf( "年=%d" , ymd >> 9 ) ; printf( "月=%d" , (ymd >> 5) & 0x0F ) ; printf( "日=%d" , ymd & 0x1F ) ;
とすれば、int 型で、年月日を必要最小限のbit数で保存できる。 しかしながら、2進数演算は分かりにくいので、以下のようなビットフィールドを使えば簡単。
struct YMD { unsigned int year : 11 ; unsigned int month : 4 ; unsigned int day : 5 ; } ;