平方三角数コードゴルフ その後
2ヶ月ほどほったらかしにしててゴメンナサイ(>_<)
平方三角数のコードゴルフ問題解答募集。
Twitter で解答をいただいていたので、それを紹介します。
あと、私の見付けている、最短解ではないけれど結構短い解も紹介します。
【2013/11/17 10:56 追記あり】
解答チェックスクリプト
解答を公表する前に、今回の解答募集で私が用意した解答確認スクリプトを紹介。
シェルスクリプトです。
#!/bin/sh #answer_chk.sh n=1 while [ $n -lt 100 ] do echo $n: $@ < ${n}_in.txt | diff -b ${n}_out.txt - n=`expr $n + 1` done
このほかに 1_in.txt〜99_in.txt と 1_out.txt〜99_out.txt を用意。
n_in.txt は数値 n のみからなるテキストファイル、n_out.txt は n 番目の平方三角数が記述されているテキストファイルです。
使い方は、例えば寄せられた回答が Ruby ならば
prompt$ ./answer_chk.sh ruby answer.rb
として、「1:」〜「99:」以外の出力がなければ(つまり diff が何も出力しなければ)すべて合っているということで OK、というワケ。目視で(^-^;*1
なお、設問は「標準入力から整数 N(>0)を与えて、N 番目の平方三角数を標準出力に出力する」だったのですが、入力ではなく「コマンドライン引数」で受け取る、という形式の解答もあったので、それも対応できるように引数バージョンも用意しました:
#!/bin/sh #answer_chk_a.sh n=1 while [ $n -lt 100 ] do echo $n: $@ $n | diff -b ${n}_out.txt - n=`expr $n + 1` done
例:
prompt$ ./answer_chk_a.sh perl answer.pl
@cielavenir さんの解答
まず @cielavenir さんによる Ruby の解答。
49bytes のものと、それを改良した 43bytes のもの。
a=b=0;c=1;gets.to_i.times{a,b,c=b,c,34*c-b+2};p b (Ruby 49bytes)平方三角数コードゴルフ 解答募集! - 名古屋で数学するプログラマ(仮) http://t.co/F0RVzOGcnx @antimon2
— しえる (@cielavenir) 2013, 9月 17
@antimon2 そうですね。恥ずかしい(汗) b=0;c=1;gets.to_i.times{b,c=c,34*c-b+2};p b (43bytes)
— しえる (@cielavenir) 2013, 9月 25
もちろん正解です(^-^)きちんとこちらが意図した「標準入力→標準出力」になっていますし。
Ruby だと、少し考えればここまで短くなりますね。
これ以上短くしようとすると、マニアックな小手先テクニックが要求されます(^-^;
@Hemus_ さんの解答
続いて @Hemus_ さんの解答。
2つあったのですが、1つめは N=1 のときに正しい解答でなかったためにご本人が取り下げられて、2つめの解答のみここに紹介します。
Perl のワンライナー。
再度、平方三角数コードゴルフのPerlワンライナー
% perl -Mbigint -e '($\,$b)=(34*$\-$b+2,$\)for++$\..~-pop;print' 3
@antimon2
— 古びたヘムス (@Hemus_) 2013, 9月 23
はい、これが標準入力ではなく、整数 N をコマンドライン引数で受け取るパターンだったんですね。
想定してなかったのですが、そもそも Perl の場合、標準入力を処理するよりコマンドライン引数を処理するようにする方が記述が短くなるようですし、採用することにしました。
(標準入力の場合 1byte 減らせるようです(^-^; 後述参照)
なお、-e 引数の引用符の中は 43bytes ですが、-Mbigint でモジュール読み込みしているので、それも含めて1つのスクリプトとなるように書き直すと、
use bigint;($\,$b)=(34*$\-$b+2,$\)for++$\..~-pop;print
と、54bytes になります。
Perl で、これより短く出来るのでしょうか?
どなたか挑戦してみませんか?
【2011/11/17 10:56 追記】ご本人による改良解
この記事に対して @Hemus_ さんご本人からフォローツイートがありました。
@antimon2 ちなみにですが、ワンライナーでない場合の読み込みはpopではなく<>にすることで1byte減ります
— 古びたヘムス (@Hemus_) 2013, 11月 16
そうかそれでよかったのか(^-^;
ということで試してみました:
use bigint;($\,$b)=(34*$\-$b+2,$\)for++$\..~-<>;print
Perl で 53bytes。もちろんきちんと動作することも確認しました。
さらにその後、2bytes 短くした改良解もいただきました。
@antimon2 より短くしました!
use bigint;$\=34*$\-$'+$\=~//+1for++$\..~-<>;print
— 古びたヘムス (@Hemus_) 2013, 11月 16
Perl で、51bytes。確認 OK。
下記に示した(時系列的には追記前の)私の Ruby の解答と同様なアプローチですね。そうか Perl でもこの書き方出来るのか。
なお、Perl のバージョン(?Math::BigInt のバージョン?)によっては Can't call method "is_zero" on an undefined value at 〜《パス略》/Math/BigInt.pm line 1200.
の様なエラーが出ることがあるようです。
原因は追えていませんが、たぶん bigint(Math::BigInt)のバグだろうと思いますし、少し古いバージョンで試したらきちんと動作したのでとりあえず OK とします。
【追記ここまで】
私の最短でない解答
私の用意していた、最短でない解答を公開します。
Ruby で、41bytes です。
a,b=1,0;gets.to_i.times{b=2-a+34*a=b};p b
はい。
@cielavenir さんの寄せてくださった解答とほとんど同じです。
times メソッドのブロック内を、ちょっとマニアックに書き換えただけです。
この書き方は Ruby 特有ですね。他の言語だとコンパイルエラーになるのではないでしょうか?
ちなみに、2ヶ月前の記事にも書いたように、私の見付けている最短解は、40bytes 未満です。
というか告白すると、39bytes です。
この 41bytes 解を、全く別のアプローチでさらに書き換え、ほんの少しの無駄を省くことによって 2bytes 削れました(^-^)
せっかくなので、その解答はまだ内緒にしておきます。
一言だけヒント。times メソッド使いません(^-^)
ということで、まだまだ無期限で解答募集しています!
特に、他言語での解答を絶賛募集!