AOJ―Volume0:0000をショートコーディング

皆さん大丈夫でしたでしょうか。
とりあえず私は無事です。



今回はAOJ(AIZU ONLINE JUDGE)から、簡単な問題のコード短縮に挑んでみました。

問題はVolume0の問題0000

九九表を以下の形式で出力せよ
1x1=1
1x2=2



9x9=81

です。言語は短くしやすいCにします。

とりあえず短いかつ安直なコードを書いてみる。
111Byte.全然長いですね。

#include<stdio.h>
int n,m;int main(){for(n=1;n<=9;n++)for(m=1;m<=9;m++)printf("%dx%d=%d\n",n,m,n*m);return 0;}

さてまず、要らないものは削ります。
まず、型宣言を省略するとint型と解釈されるので、mainとn,mの型を省略します。(n,mの方を省略するとBorlandコンパイラだとエラーを吐きますが、AOJのシステムテストに使われているコンパイラなら問題なく通ります)

さらに、どうやら#includeも不要なようです。C++だと怒られますが、Cだと関数を使うだけならインクルードは不要です。警告は出ますが……

さらに、return 0;も省略可能です。
「関数は値を返すべき」とは警告されますが、あくまで「返すべき」であって必須ではないようです。(そもそもmainの返り値でどうこうするプログラムは少ないですよね。)

とりあえずこれらを削ると75Byteに。

n,m;main(){for(n=1;n<=9;n++)for(m=1;m<=9;m++)printf("%dx%d=%d\n",n,m,n*m);}

ここから先は、このサイトあたりを参考にしながら進めます。

まず、int型変数の一つはmain()の引数として宣言できるようです。つまりmain(n)とできます。このとき、変数nはコマンドライン引数の個数という意味合いを持たされます。そのためAOJのサーバではコマンドラインオプションなどをつけないらしく、n=1で初期化されます。つまり、nのfor文での初期化が要らなくなるので、これも削れますね。

次に、mのfor文for(m=1;m<=9;m++)の再初期化式m++は、継続条件式Bにまとめてm++<9と書くことで2バイト削れます。

ここまでやったのが次の69Byteコード。随分短くなりました。

m;main(n){for(;n<=9;n++)for(m=0;m++<9;)printf("%dx%d=%d\n",n,m,n*m);}

さて、これ以上短くするとなると、もうプログラムの構造から何とかしないといけないような気がします。

で、思いついたのは、2重ループ

for(;n<=9;n++)for(m=0;m++<9;)

を単一のループ

for(;n<=9;(m%=9)||n++)

で置き換えるアイデア

下のループは、


「mを9で待った余りをmとする」のbool値
または
「nをインクリメントする」のbool値

という処理を再初期化式においてますが、

m%=9はbool値としては9で割った後のmを返す
n++はbool値としてはインクリメント後のnを返す
A||BはAがtrue(=非0)ならBを評価(&実行)しない

ということを利用して、


mが9で割り切れたらmを0に初期化し、
nをインクリメントする

という処理を一挙にさせてます。
但しこれだと(n,m)=(1,0),(1,1), .... ,(9,8)のループになるので、どうしてもprintf部分が少し伸びてしまいますが、それでも3Byteの短縮に成功し、最終的に以下の66Byteのコードが出来上がりました。

m;main(n){for(;n<=9;(m%=9)||n++)printf("%dx%d=%d\n",n,++m,n*m+n);}

とりあえず、サーバ上の"xeno"の記録は66Byteという結果になりました(笑)

もうすこし縮みそうですが、もうこれ以上工夫しても短縮はできませんでした。記録を見るとどうやら62Byteまで短縮できるようです。


こういう所謂ショートコーディングの探求は、あまり生産的でないようにも思われますが、意外とショートコーディングをすることでしか気づけない発見があったので、個人的には思ったより有意義でした(`・ω・)b


ではまた(・ω・)ノシ