カメヲラボ

主にプログラミングとお勉強全般について書いてます

何が何でもTopを取りたかった話(1)

はじめに

f:id:Ozy:20220405134555p:plain
POJ 1000 Status
この画像は,AIZU ONLINE JUDGE(AOJ)AtCoderが誕生する前から稼働しているオンラインジャッジシステムのPKU Judge Online(POJ)にある,一番最初の問題「A+B Problem」のStatus画面です.

この問題は非常に単純なもので,2つの整数値を読み込んでそれらの和を出力するだけの問題です.2022年4月5日14時現在では提出数545364・そのうち正解数307448と,これまでにどれだけのユーザが利用したか想像できると思います.この画像の一番上にあるozy4dmというidは私のものです.2006年1月から,少なくともこの記事を公開するまでずっと一番上にあります.

問題の内容からして小学生でも解けるレベルの問題で一番上を目指すなんて,ほとんど無意味でバカみたいなことかもしれませんが,「じゃあやってみろ」と言われて実際にできるのかというと結構難しい話です.わかる人はバカらしくてやる気にならないだろうし,わからない人にとってはものとても難しいでしょうから.

この記事は,そんなしょうもなさそうな目標を達成した,2005~2006年ごろ(今から16~17年くらい前)のお話です.これがどれくらい昔かというと,目指せ!プログラミング世界一―大学対抗プログラミングコンテストICPCへの挑戦 という,本格的な競技プログラミング書籍が発行されたのが2009年プログラミングコンテストチャレンジブック [第2版] ~問題解決のアルゴリズム活用力とコーディングテクニックを鍛える~ (通称「蟻本」)と呼ばれる競技プログラミングのバイブル(初版)の発行が2010年ですので,それよりもっと前の話ということですね.(ちなみに最強最速アルゴリズマー養成講座 プログラミングコンテストTopCoder攻略ガイド の発行が2012年プログラミングコンテスト攻略のためのアルゴリズムとデータ構造2015年の発行です.)

背景

背景と言っても,競技プログラミングには全く縁のなかった私ですので,一般的な背景ではなく単なる私の背景です.当時ACM-ICPCのようなプログラミングコンテストの存在を全く知らず,オンラインジャッジのサイトも単なる遊び場でした.今でこそいろいろと知識や技術を習得しましたが,当時はBFSとかDFSのような基本的なアルゴリズムも知りませんでしたし,解くことのできる問題が限られていました.

一応勉強のために,ある程度難しい問題にも挑戦していましたが,大半は比較的簡単な問題を解いて,その後ソースコードを短くする,いわゆるコードゴルフと呼ばれる遊びをしていました.これが本当に楽しくて一生遊べます.どれくらい楽しかったかというと,それだけで本を1冊書くくらいです.

もちろんPOJはコードゴルフをするためのシステムではないので,勝手に遊んで運営の方にも結構迷惑をかけていましたが….

Topに立つってカッコええやん?

さて最初の画像の話に戻りますが,オンラインジャッジのサイトにアクセスして,おそらくほとんどすべてのユーザが最初に開く問題は,一番最初に設置された問題だと思います.POJでは問題のレベルやタイプの分類はされていませんでしたから,とりあえず問題一覧を開くと最初の問題が一番上に表示されました.

このとき私は思ったのです.「ここのStatusで一番上に表示されてたらカッコ良くね?」と.

一番上に表示されるために必要なこと

POJの(問題毎の)Status表示では,表示する際の順序として

  1. プログラムの実行時間
  2. 消費メモリ
  3. ソースコードのサイズ
  4. 提出の日時

の優先順位でソート昇順にされています.プログラムの実行時間はかなり大雑把(たぶんclock関数とか使ってる?)で,この問題は基本的に0msで通ります.つまり実行時間の面では先に提出した人が有利で,ここで差をつけることはできません.

消費メモリ

POJの消費メモリ測定は(も)かなり大雑把です.詳細は知らないのでこれから書くことはシステムの挙動から推測した内容です.概ね正しいと思いますが,このセクションはすべて「おそらく」を補って読んでください.

POJのシステムは内部でWindows APIを使って消費メモリを取得していましたが,その値をそのまま使用していませんでした.これは言語間での差異を無くすための処置だと思います.事前に各言語で空のプログラムを実行して消費メモリを測定しておき,提出されたプログラムを実行したときの消費メモリから差し引く処理を行っていました.

APIから取得できる消費メモリのサイズは割とアバウトで,大きな配列のような,ある程度大きなサイズのメモリを消費する際はそれらしい値になりますが,ほとんどメモリを消費しないプログラムの場合,誤差の大きい記録になっていました.

空プログラムの消費メモリは毎回測定しているわけではなく,システムのリブート時とか特定のタイミングでしか行われていないようなので,消費メモリのサイズが大きい日が続いたり,小さい日が続いたりということがあり,ひどいときは負の値になっていることもありました.

私はここに目をつけて,消費メモリが負の日に提出することを試みましたが,データベースには絶対値を記録しているようで,ちょうど0にならなければならないことがわかりました.また,ほとんどの言語では消費メモリのサイズが0になることはめったになく,Pascalだけがその確率が大きいことも発見しました.

この時点で残された選択肢は,「消費メモリがちょうど0になる日に,Pascalで最短のコードを通す」ことになりました.2005年の12月から2006年の1月4日まで,毎朝起きるとすぐに他のユーザの提出状況をチェックして消費メモリの補正値を確認していました.

最短のPascalコード

よくよく考えると,このコードを公開していなかった気がしてきたので,ここで晒しておきます.どうぞ皆さん幻滅なさってください笑

f:id:Ozy:20220405140219p:plain
POJ 1000 Pascal 37B
これは酷いですね.解が1桁の整数にしかならないことと,テストケースが3つくらいしかないこともあって,繰り返し提出すると通ります.酷いコードではありますが,これでランキングの一番上に名前を表示することに成功しました.この記録を破るには,より短いコードを書いて消費メモリのサイズが0になる日を待ち続ける以外にありません.最近は消費メモリの補正がされないようになっていますので,現在は実質的に不可能ですが,いつかまた補正がかかる日が来るかもしれませんので,何が何でもTopを獲ってやりたい人はその日を待ってぜひ挑戦してみてください.

ここからおまけの話

この記事を書こうと思ったきっかけは,Twitterで「他人のコードをコピペするなんて!」みたいなツイートをちょいちょい見かけたからです.良いとか悪いとかは正直言えません.でも,当時POJがソースコードを原則公開するようなものだったら自分はどうしていただろうなぁと思ってしまったので,思いついたことをたらたらと書いておきます.

コードの公開

最近では,競技プログラミング的なコードは積極的に公開されるようになりました.そのおかげでプログラミングの学習効率は飛躍的に上がった気がします.当時は基本的に非公開だったので,提出したコードを自分のブログなどで公開したり解説するだけで,ある程度価値のある記事にすることができました.しかしコードの公開は,それなりにリスクを負うことになります.

それは自分の公開したコードをちょちょいと改変して,トップを獲られてしまうことです.実際,「最短コードやで!どや!!」みたいな記事を書いた直後に記録を抜かれることなんていくらでもありました.

でも,ほとんどのケースでは,自分のコードをちょっと弄ったというわけではなくて,新しいテクニックとか私では思いつかないような賢い方法で抜かれただけなので,気にする程のことは無かったように思います.たとえば余計なスーペースとか改行,使わない変数の消し忘れとか,そんな初歩的なミスでトップを獲られるなら,それは詰めの甘い自分が悪いだけですしね.

もちろん,自分が苦労して書いたコードを丸パクリとか,ちょっと変えただけで自分より良い成績を取られるのはとても悔しいことです.理想はさらに良いコードを書くことですが,うまくいかないことの方が多いと思います.おそらく自分が当事者になったら,パクリ合戦でもなんでも,相手が逃げ出すまでやると思いますが.

そこまでの執着心がなければ,さっさと次のお題に移って自分のスキルアップを目指した方が良いでしょうね.今競技プログラミングをしている人たちは,本当に高い能力を具えているように見えます.1つの問題に固執してチマチマチマチマやるのは私に任せて,どんどん上を目指して欲しいですね.

コードゴルフの文化

最近は,純粋にコードゴルフをする人はAnarchy Golfを利用しています.ここではユーザ登録の必要がなく,単に投稿者を識別するためだけの名前と,あとはソースコードのみを投稿します.また,ほとんどの問題では一定の期間を過ぎればソースコードが公開されます.

ソースコードが非公開の間は,ソースコードのサイズが小さい順,同サイズの場合は提出の早い順で順位が決まります(特殊なルールの問題を除く).ソースコードが公開された後,よりサイズの小さいコードを提出したとしても,それは後日の記録ということでランキングの下に追加されるようになっていて,よく考えられています.

しかしそれだけではなく,他人のコードから着想を得た場合に自分の名前+誰のアイデアを利用したかのような情報を付与するコードゴルファーは少なくありません.ルールではなく,暗黙のルールとかマナーというほどのものでもなく,なんとなく自然に行われていることです.

POJで遊んでいた頃でも,誰かが公開したコードがさらに短くなるとわかったとき,コメントで指摘するとか,自分で提出する場合でも余計に改行を入れて記録を抜いてしまわないように配慮する人だけが残るようになっていきました(もちろん新しいアイデアが閃いたときは容赦なく記録を更新しますが).

これって皆が真剣に遊んで,自然にそうなっただけなんですよね.別に誰かから強制されたこともないですし,長い年月を経てそうなっただけ.

もちろん今後,システムとか他の利用者に迷惑をかけたり,それまでとは異質な人間が入り込んでくることは何度もあると思います.でも,全く心配はしていません.最終的にはちゃんとイイ感じになります.

最後に

まあつべこべ言わずにコードを書けっちゅうことやな(`ω´) 第2回はものすごくモチベーションが高まったら書きます.N年後くらいかな.