読者です 読者をやめる 読者になる 読者になる

『第6回デスマコロシアム』に参加しました

デスマコロシアムとは(テンプレート)

大雑把に説明すると、

  • 問題文で与えられたある文字列を出力するコード(プログラム)を提出する。
  • 言語は ideone で利用可能なものから選ぶ。
  • コードのサイズが短いほどよい。(1文字につき1点減点)
  • 同じ言語を選択した人が少ないほどよい。(1人につき10点減点)
  • その他、コーディングとは無関係な運の要素も加えて、トーナメントで競う。

といった感じです。要するに、コードゴルフに運の要素を加えたトーナメントです。

第6回の問題

今回の出力は

2:3:5:7:11:13:17:19:23:29:31:37:41:43:47:53:59:61:67:71:73:79:83:89:97:101:103:107:109:113:127:131:137:139:149:151:157:163:167:173:179:181:191:193:197:199:211:223:227:229:233:239:241:251:257:263:269:271:277:281:283:293:307:311:313:317:331:337:347:349:353:359:367:373:379:383:389:397:401:409:419:421:431:433:439:443:449:457:461:463:467:479:487:491:499:503:509:521:523:541:547:557:563:569:571:577:587:593:599:601:607:613:617:619:631:641:643:647:653:659:661:673:677:683:691:701:709:719:727:733:739:743:751:757:761:769:773:787:797:809:811:821:823:827:829:839:853:857:859:863:877:881:883:887:907:911:919:929:937:941:947:953:967:971:977:983:991:997

という文字列です。997以下の素数を ":" で区切ったものになっています。ちなみに、私の出題予想はフィボナッチ数列でした。

素数列も一瞬頭をよぎったのですが、言語によっては素数が組み込みで用意されていて単に出力するだけになってしまうので、まあ無いだろうと思って脳内却下したのですが…

というか、むしろ Perl を除け者にするための問題としか思えないのですが!

私が提出した解答

1. Perl (45)

期待されては、本気を見せざるを得ません。本気の Perl (45) を提出しました。

$,=":";print grep+(1x$_)!~/^(11+)\1+$/,2..999

正規表現素数判定するコードです。具体的には、2~999の範囲の整数 $_素数かどうかを判定するために、 1x$_ で 1 を n 個並べた文字列を作り、その文字列が 2 文字以上の文字列の 2 回以上の繰り返しになっているかどうかを判定しています。ちなみに、私が最初にこの方法を知ったのは、 tybalt89 さんのこのコードです。感動したのを覚えています。
http://golf.shinh.org/reveal.rb?Gaussian+Prime/tybalt89_1323996492&pl

2. Perl (44)

Perl (45) は本気のコードのはずだったのですが、翌日になって見直してみたらあっさり縮んでしまい、 Perl (44) を提出しなおしました。素数判定の方法は同じです。

print 2,map":$_"x(1x$_)!~/^(11+)\1+$/,3..999

なお、同日に Octave (26) を提出したため、この Perl (44) は集計に反映されませんでした。

3. Octave (26)

この時点での集計では Octave (27) が最短でした。最短賞狙いの私としては、早速試してみたのですが、リファレンスとにらめっこしつつどう書いても (34) より短くなりません。

printf("%d:",primes(991));disp 997

ヒントを求めていろいろやっている中、 Google で「octave delimiter」で検索してみたところ、このようなページが見つかりました。
GNU Octave: Simple File I/O
これぞ求めていた情報です!

dlmwrite(1,primes(999),58)

dlmwrite という関数は、指定した区切り文字を用いて出力するという、まさにこの問題のために存在するような関数です。短くならないわけがありません。ていうか、このコードは (26) なので、最短更新です。喜び勇んで提出しました。

この後しばらく、最短は Octave (26) で動きがなく、今回の最短コードはこれで決まりかと思われたのですが…

4. Perl (5)

と、トップが変わるですと!?

本当だ! しかも、よりによって Ruby ですか! Ruby は触ったことがないので、さっぱりわからりません(ゴルフで RubyPerl より有利になることなんて滅多にないのでスルーし続けてきました)。困った…。

ところで、細則を利用して賢く文字数を稼ぐって、どういうことでしょうね? 短縮に使えそうな細則といえば、モジュールのインポートくらいでしょうか。モジュールを賢くインポート… って、もしかして、 Perl で書けば、こういうこと!?

use 5.01;
use constant a,"2:3:5:7:11:13:17:……中略……:991:997";
say a

1行目はひとまず置いておくとして、2行目は constant.pm というモジュールをインポートしていますが、(当時の)細則によればこれは文字数にカウントされません。結局、このコードの長さは、3行目だけがカウント対象なので (5) になってしまいます。

まあ、当然のごとく、この解答は却下され、モジュールのインポートの際にパラメータを渡した場合には文字数にカウントするというルールが整備されることとなりました。

5. Ruby (22)

どうやら最短を目指すためには Ruby を書かなければならないようです。 Ruby には手を出さないというアイデンティティと、デスマコロシアムは最短賞・最小賞を獲りに行くというアイデンティティとが、激しくコンフリクトしています。まあ、少しやってみて駄目だったら諦めるということで…

Ruby での短縮ということで、まず思ったのが goruby です(こんなことだけは知っている)。 goruby 自体はコマンドですが、モジュールとしては golf_prelude.rb というものになるようです。しかし、このモジュールは Ideone にはインストールされていないようです。

ということは、別のモジュールでしょうか。とりあえず Ruby の標準モジュールを片っ端から見て回ることにします。何せ Ruby を触るのはほぼ初めてなので、どのようなモジュールがあるのかさっぱり知らないのです。 CSV モジュールとかは Octave の dlmwrite に似ていて怪しい気もしますが、どうもコンマ区切り専用のようで、使えそうもありません。むむ…

まあ、とりあえずモジュールのことは置いておいて、普通に書いてみましょうか。ええとなに、 Prime は Enumerable で Object なのか。さっぱりわからんが、こんな感じ?

require 'prime'
$><<Prime.take(168)*?:

おや!? 普通に (22) で書けてしまいました! うわ、モジュールのドキュメントをひたすら読みまくったあれは一体何だったんだ…

結果

Ruby (22) でなんとか無事に最短賞を獲得できました。デスマコロシアムでの最短賞・最小賞は、これで5回連続5回目です。おめでとうございます。ありがとうございます。せっかくなので、これからもトーナメントそっちのけで最短賞・最小賞を狙っていきたいと思います。