【第4回】競技プログラミングはITエンジニアをどう鍛えるか
プログラミングのスキルは、ウェブをはじめシステム開発の業務に欠かせないものですが、それ自体を「競技」として楽しみ、練習を通じて上位を目指すという世界もあります。そんな競技プログラミングにおいて「強くなる」ことは業務におけるプログラミングスキルの向上に関係があるのか、そもそも人間にとって「学び」とは何なのか、日本語で参加できる競技プログラミングのコンテストを定期的に開催するAtCoder株式会社の高橋直大さんと青木謙尚さんが、株式会社一休でウェブシステム開発に携わる伊藤直也さん、所澤友大さんと語ります。
目次
・伊藤 直也さん / 株式会社 一休 執行役員 CTO
新卒入社したニフティ株式会社でブログサービス「ココログ」を立ち上げ、CTOを務めた株式会社はてなでは「はてなブックマーク」などの開発を主導。グリー株式会社では統括部長としてSNSを担当した。2016年4月、一休に入社し執行役員CTOに就任。
・所澤 友大さん / 株式会社 一休 エンジニアリングマネージャー
2018年に一休に入社。一休.com / 一休.com レストラン のフロントエンド開発チームを経て、2021年に新規事業開発チームへ異動。リードエンジニアと開発組織全体のEMを兼務。
・高橋 直大さん(chokudai) / AtCoder株式会社 代表取締役社長
慶應義塾大学大学院政策・メディア研究科修士課程修了。2012年にAtCoderを創業。
2008年に「Imagine Cup 2008」Algorithm部門で3位入賞。
世界的なプログラミングコンテストで活躍中。
Twitter : @chokudai
・青木 謙尚さん(akensho)/ AtCoder株式会社 取締役副社長
AtCoder株式会社 取締役副社長。
和歌山大学大学院システム工学研究科コミュニケーション科学専攻中退。2012年にAtCoderを創業。
Twitter ID : @akensho
AtCoderと競技プログラミングの入口
伊藤さん:一休にも、最近有志のソフトウェアエンジニアによる「AtCoder部」という部活動みたいなものができて、AtCoder株式会社が開催するプログラミングコンテストに挑戦しています。今では5、6人が参加しています。CTOがいるとやりづらいと思うので、ぼくは部活には参加していないんですが、最近になって個人的に競技プログラミングに入門しました。それで、「システム開発をずっとやってきてプログラミング自体はできるエンジニアにとっての競技プログラミング」みたいなことをいろいろ考えることがあり、競技プログラミング界の最前線にいる高橋直大さん、青木謙尚さんのお二人にぜひお話を伺いたいと思います。
chokudaiさん:ありがとうございます。AtCoder株式会社の代表をしている高橋です。名前を音読みして「chokudai」と呼ばれることが多く、「高橋」と呼ばれてもすぐに反応できないので、chokudaiと呼んでもらってかまいません。いちおう現役の競技プログラマーです。
伊藤さん:いちおうと言いつつ、去年もかなり活躍されていましたね。
chokudaiさん:はい。2022年はかなり良い成績でした。Googleが主催する「Google Hash Code」というプログラミングコンテストで優勝したのと、関数プログラミングの学会に併設されているICFP Contest(ICFP Contest 2022 Homepage)で優勝しました。1年にこれら2つのコンテストで優勝しているのは、けっこう珍しいと思います。
akenshoさん:AtCoderでバックオフィス業務などを担当している副社長の青木(@akensho)です。縁があって10年くらい前にAtCoderの創業にかかわってから、当社の社員として仕事をしています。
競技プログラミングに関しては、chokudaiのように強いわけではありませんが、*AtCoderのレーティング(上から順に赤・橙・黄・青・水・緑・茶・灰・黒)でいうと「緑」になります。
ただ、「緑」になった時点で止めてしまったので、いまは会社として必要な仕事のうち「chokudaiのような競技プログラミングが強い人」にはできないことをひたすらやっている感じです。
* AtCoderでは競技プログラミングのコンテストを随時開催しており、参加すると成績に応じたレーティングが色で示される。上から順番に赤・橙・黄・青・水・緑・茶・灰・黒(参考)
伊藤さん:お二人とも、自己紹介ありがとうございます。
青木さんは、高橋さんをはじめとする才能あふれるAtCoder株式会社の皆さんをサポートするマネージャー的なお仕事をされているんですね。
今日は一休からも、私のほかにもう一人、所澤が同席してお話を伺えればと思います。
所澤さん:一休のエンジニアリングマネージャーの所澤です。ふだんはソフトウェアエンジニアとしてプロダクト開発を行っていますが、エンジニア採用などのマネジメント業務も担当しています。
伊藤さん:所澤さんは、冒頭で触れた「一休AtCoder部」の部長でもあり、AtCoderのコンテストにも参加しているんですよね。
所澤さん:はい、何度か参加しています。まだまだ競技プログラミングが得意というわけではなく、レーティングでいうと恥ずかしながら「茶色」です。
伊藤さん:最近は茶色になるのも以前より難しいみたいですからね。恥ずかしがらなくてもよいんじゃないかと。
chokudaiさん:所澤さんは、いつごろから競技プログラミングを始めたんですか?
所澤さん:AtCoderに参加するようになったのは去年くらいなので、まだ半年です。ただ、もっと前に「蟻本」の愛称で知られる『プログラミングコンテストチャレンジブック』を一休のエンジニアで集まって読んだりはしていました。
伊藤さん:あの本、最初は難しいですよね。
所澤さん:難しかったです。それでも毎週、章末で紹介されているオンラインジャッジの問題を3、4問やるくらいのペースで進めて、いちおうひととおりやったはずです。ただ、紹介されているオンラインジャッジの問題が今となってはけっこう古めだったり、システムの使い勝手もよくなかったんですよ。
chokudaiさん:北京大学のPOJの問題とかが多いのかな。
所澤さん:そうです。それで、しばらくしたらけんちょんさんがQiitaで「AtCoder版!蟻本」という記事を公開されたので、そちらもやっていこうと。そこからAtCoderを始めました。
伊藤さん:けんちょんさんは、ほかにもQiitaに「AtCoder に登録したら次にやること 過去問精選10問」っていう記事を書かれていて、今だとこの記事を入り口にAtCoderを始める人もけっこう多いようですね。ぼくも最初はここから着手しました。
所澤さん:そうかもしれません。結局、自分たちが蟻本を読み始めたのは、当時は初心者向けの入口がなかったからなんですよね。今のように「鹿本」(『アルゴリズム的思考力が身につく! プログラミングコンテストAtCoder入門』)がない時代の話なので。
伊藤さん:鹿本は昨年夏に発売された本で、入門者向けの問題集として適度な難易度の問題がセレクトされていますね。
chokudaiさん:最近はだんだん初心者にも入りやすい流れになってると思います。それでもまだ「競技プログラミングへの入口がない」っていう人は結構いて、何が欲しいのか正直わからないとこもあるんですけど。
実力は時間をかけた結果である
伊藤さん:それこそchokudaiさんが競技プログラミングを始めた10年以上前には、入門者向けの本や記事なんて、ほぼ何もありませんでしたよね。
chokudaiさん:日本語だと本当に何もなかったです。英語で書かれた解説はあったらしいんですが、ぼくの英語力って世の中の大学生の平均あるかないかくらいで、「archive」を「アチーブ」と読んでいたくらいだから、解説が読めない以前に解説が存在することにも気づけていませんでした。
伊藤さん:chokudaiさんが英語が苦手で数学や競技プログラミングにステータスを極振りしたという話は他のインタビューでも拝見しました。そうすると、一般的なアルゴリズムの教科書とかを読んでいたんですか?
chokudaiさん:アルゴリズムの本があるってこと自体を知りませんでした。当時、競技プログラミングのコンテストを定期的に開催しているのってTopcoderくらいしかなかったと思うんですが、当時のTopcoderだと基本的な問題が解ければレーティング(上から順に赤・黄・青・緑・灰)で「緑」には入れたんですよ。「なんとか法」みたいなアルゴリズムの知識が必要なわけでもなく、ちゃんとロジックを考えて必要な計算量の削減ができればいいというレベルでした。最初は、このコンテストの他の人の回答のコードを見て「ここは何をやってるんだろう?」と考えることでアルゴリズムを覚えていきました。
所澤さん:えっ、いきなりコードからアルゴリズムを勉強するって、すごくないですか…
伊藤さん:アルゴリズムのコードは、特に読んだだけでは何をやっているかすぐにはわからないし、普通はそれ以上のことを理解しようともしないですよね。やはりというか、かなり特徴的な学習のアプローチに思えます。そもそもchokudaiさんが競技プログラミングを始めたのはいつだったんでしょうか?
chokudaiさん:始めたのは高校時代です。ぼくは筑駒っていう、周囲にマジで賢い人しかいないような学校に通ってたんですが…
伊藤さん:筑駒出身の方には、本当に度肝を抜かれるようなすごい方も多いですよね。
chokudaiさん:なんですが、ぼくは学校の成績でいうと下から10番めくらいで、どちらかというと「運動ができるキャラ」だったんです。部活も野球部でした。
ところが肘を壊してしまって野球部を引退し、ふてくされてパソコン研究部に入り浸るようになったんです。そうしたらSuperConという高校生や高専生のためのプログラミングコンテストにチームで出させてもらい、そこで2006年に6位になれて、「自分にはこれしかない」と思うようになりました。
所澤さん:ということは、初めてのプログラミングが競技プログラミングだったわけですか。
chokudaiさん:学校の授業で4ビットマイコンを触っていたので、そういう意味ではプログラミング自体をやったことはあったんですが、それこそC言語を触ったのはSuperConが初めてでした。
伊藤さん:それで2008年には、今度はMicrosoftのImagine Cupというコンテストで世界3位になるわけですよね。
chokudaiさん:まあ、一応そうです。ただ、世界3位っていうとすごく見えると思うんですが、実はそこまですごくはないんです。あの当時、Topcoderのレーティングでいうと「青」になりたてではあったんですが、当時の「青」は今でいうと「緑」くらいなので。
そもそも当時のImagine Cupには10個くらいのサブ部門があって、ぼくが2008年に3位をとったのはそのうちのアルゴリズム部門でした。これ、参加者こそかなり多いんですが、それには理由があって、参加するだけでMS OfficeやWindows Serverがもらえたんです。
伊藤さん:なるほど、Microsoftが主催のコンテストだから、そういう特典があったのか。
chokudaiさん:そうなんですよ。それで1回戦はプログラミングと呼べるほどのものでもないパズルゲームみたいなやつで、これは楽しいのでみんな一応参加するんです。そこで200人くらい通過するけど、2回戦はプログラミングだから出ない人がけっこういたんです。ガチで参加していたのは、多分十数人くらい。そのうちの6人に入れば決勝に出場できたんです。
伊藤さん:とはいえ、その十数人は、みんな真剣に参加しているわけですよね。そこでいきなり3位はすごいですよ。
chokudaiさん:予選の期間が2ヶ月くらいあったので、ぼくはその間にがっつり時間をかけてコーディングだけをしていたんですよね。ほかの通過者もわりと似たような感じだったと思います。結果的には、本当にすごい人が1位をダントツで取り、時間だけはたくさんかけた人たちが2位から6位で、ぼくがその中で3位だったっていう感じなんですね。
akenshoさん:才能もあったのだろうけれど、どちらかというと時間を費やして訓練したことで実力がついたっていう認識なんですね。
chokudaiさん:やっぱりそこは、高校のときに「これしかない」と思ったのがありますよね。浪人時代もずっと競プロの問題を解いていましたし、Imagine Cupを経て大学でも授業中はずっと競プロをやってたりしましたから、そうやって2、3年かけて徐々に実力がついていったなと思っています。
所澤さん:そうなんですね。chokudaiさんにも下積み期間というか訓練期間があったんですね。少し勇気をもらいました。
時間をかけるだけで実力になるわけではない
伊藤さん:その話を受けて、最近Qiittaに投稿された「中卒の主婦が青コーダーになったおはなし」という記事があるんですが、この記事についてぜひ話をしたいです。もともとプログラミングをそれほどやっていなかった方がオンラインのエンジニア養成スクールでプログラミング問題に取り組んだのをきっかけに競技プログラミングを始めて、そこから1年半くらいでAtCoderで「青」になったという内容の記事で、界隈でもかなり話題になっていました。
akenshoさん:「青」だと、だいたいの会社のエンジニア採用でコーディング試験の問題を突破できますね。
chokudaiさん:それなら「緑」でもいけそう。「青」はその上なので、AtCoderの参加者の上位10%には入ると思います。
akenshoさん:もっと上かもしれません。
所澤さん:我々の感覚だと、「青」はとんでもなく優秀と言う印象です。
伊藤さん:すごいですよね。ウェブの反応を見ていると、青色コーダーになったことももちろんですが、1年半ぐらいの短期間でここまで辿り着いているということに感嘆している人が多かったです。
実際、「青」っていうと、AtCoderを知らない人には「真ん中」くらいに聞こえるかもしれないんですが、世間的なプログラマーの基準から見ると相当ですよね。青を持っていたらその会社で1番か2番の実力をもったエンジニアだとしてもおかしくないと思います。
chokudaiさん:普通のソフトウェア開発で必要になるアルゴリズムの範囲だと、「青」でもうカンストですよね。
伊藤さん:それで、この方の記事を読んで印象的だったのは、一つは毎日欠かさず続けていること。もう一つは、毎週開催しているAtCoderのコンテンストに出るのはもちろんのこと、非公式で誰でも開催できるバーチャルコンテストにも積極的に参加して実戦を通じて学習を続けていることでした。書籍をじっくり読んで座学でというよりは、実戦形式で問題を解いて、そのあと動画を見たり感想戦をやったりを中心にされているようなんですね。そして、それをとても楽しんでいらっしゃる様子でした。
所澤さん:毎朝開催されるバーチャルコンテストの「あさかつ」と、夜に開催される「くじかつ」に参加したとありましたね。その後は自分でもバーチャルコンテストを立てて続けた、と。
伊藤さん:そうですね。先ほどの話を聞いてchokudaiさんもそうだと思ったんですが、やはり毎日のように続けることが競技プログラミングで実力を付けるうえでの前提なのかもしれないと感じました。
chokudaiさんも記事について、Twitterで「毎日複数問やってる人は、もともとのスキルセットに拠らずよく伸びてる気がする」とコメントされていましたよね。
chokudaiさん:はい。どれだけ時間をかけることができたかが前提というのは、いろいろなコンテスト上位者を見ても思います。AtCoderで見ても、過去のコンテストに出た問題をどれくらい解いたかを管理できる「AtCoder Problems」というサイトがあるんですが、解いている問題の数が2000問とか3000問の人はレーティングでいうと「水色」くらいまではいっています。3000問くらい解いている人であれば、「青」や「黄色」までいっていることも少なくありません。
akenshoさん:アスリートと一緒ですよね。長時間どれだけ練習したかっていうのは、けっこう大切だと思います。
伊藤さん:量が実力にはっきりと結びつくもんなんだなあと。以前はぼくも誤解していたのですが、プログラミングだからといって知識や地頭だけで勝負するというものではないんですね。「競技」の名前を冠しているので、そう言われてみれば確かに当たり前なんですが。とはいえ、取り組んだ量によって実力が伸びるというのは、希望にも思えてきます。
chokudaiさん:AtCoderの社内でも、「初心者向けのコンテストであるAtCoder Beginner Contest(ABC)で伸び悩んでしまう人がいるのはどうしてか」という議論はあり、それに対する基本的な答えとして「練習量が足りない人が大半である」とは言えるだろうと考えています。
ただ、その中にもなかなか伸びていない人というのはいるんですよね。伸びていないのはなぜだろうと思って、いろいろ聞いてみたりはするんですが…。
akenshoさん:それについては、メタ認知みたいな側面が影響していそうかなと考えている感じです。自分に足りていないものを自分で認識する力を発揮できているかどうか、という観点です。たとえば、「自分にとって都合のいい問題」ばかりを毎日解いていても、なかなか実力には結び付きにくいですよね。
chokudaiさん:AtCoder Problemsでは、「自分が何日連続でオンラインジャッジを通したか」も見られるんですが、それを最優先のモチベーションにして謎の最適化に走ってしまっているケースとかもあります。簡単な問題をあえて残しておくとか、一日で解く量を抑えるとか。ひどい場合だと、解説と同じコードや他の強い人のコードをコピペしてオンラインジャッジを通すだけの人もいて、それでは強くならないよ、っていうケースもあります。
akenshoさん:謎の最適化に走ってしまうと、強くなることが目的ではなくなってしまうので、実力には結び付きにくいですよね。同じくらいの問題数でも成長速度の差が出るのは、そういう「自分が本当に解くべき問題」を認識できる能力がありそうです。
chokudaiさん:「解くべき問題」を認識するというよりは、「なぜ解けたか」あるいは「なぜ解けなかったか」を認識することのほうが大きいかな。要するに、「1問からどれだけ学べるか」が大切なんですよ。解けなくて、解説を読んで、「なんでこんなの思いつくんだ、わかんねーよ」といって流しちゃうと、同系統の別の問題が出たときにやっぱり解けないままなわけです。
akenshoさん:それは当然ですよね。
chokudaiさん:なので、練習量は前提としてあるんですが、「この解き方を思いつくために自分は何を考える必要があったんだろう」と考えて、次に同じジャンルの問題が出たときには自分で解けるようにするというのが、けっこう大切なんだろうなと思います。
伊藤さん:実践して、復習してよりたくさんを一度に吸収すると言う反復ですね。
akenshoさん:「この問題を解くために自分が何を発想しないといけなかったのか」を突き詰めていくと、問題をいくつかのパターンに分類できて、このジャンル分けが上手な人はやっぱり強くなる印象です。
AtCoderで作問をやっているような人間に言わせると、AtCoder Beginner Contestの問題のうち難易度が低いA問題からC問題くらいのものは、20通りくらいのパターンに分類できてしまうらしいんです。コンテストに参加してる普通に人からすると「そんなわけない」という感じなんですが、そこまで抽象化がうまい人であればどんな問題にもうまくアプローチできますよね。
所澤さん:突き詰めると、その20通りの解き方を知っていればどんな問題も解けるということですものね。
akenshoさん:はい。彼からすると、「こんなのいつもの繰り返しです」という感じなんです。
所澤さん:強すぎる。
練習には効率がある
伊藤さん:ここまでの話をまとめると、強くなるには練習量が必要という前提があり、さらに毎回の練習の質も大事という話になると思うんですが、一方でそれだけだと根性論に聞こえないこともないし、人によっては「そこまでの時間をつぎ込めないから、やらない」ともなりかねない。なぜ量が必要になるのか、それこそ業務の経験とかアルゴリズムの知識などでカバーできたりはしないのか、そのあたりも少し気になります。
chokudaiさん:「量をやるとどうして強くなれるのか」については、わりと当然だと思っていて、実際、「chokudaiと同じ量の問題を解けばchokudaiと同じことはできるようになるはず」とは言えますよね。
ただ、それには前提があって、「chokudaiが問題を解くときに得ているのと同じ量を1問から吸収できる」のが必要なんです。
去年発行された『競技プログラミングの鉄則』という本があるんですが、この本はまさにそういうアプローチで書かれたのだろうなと思っています。具体的には、「この問題からどんな知識を得ればいいのか」が伝わるように書かれていて、これはすごくいいなと。
伊藤さん:はい、私も半分ほどやりました。問題の解き方が載っているだけでなく、問題そのものの構造的な分析ですとか、そこからどういうふうに知識が派生していくとか、そういうことも書かれていますね。1問1問の説明が丁寧なので、それぞれから吸収できることは多いと思います。
chokudaiさん:どんな練習をすればいいかを意識するのは本当に大事だと思います。自分がよく知っている野球との比較でいうと、競技プログラミングって、野球よりも練習の効率化みたいなのが難しいんですよ。
所澤さん:練習の効率化?
chokudaiさん:たとえば野球の練習って、試合形式だとけっこう効率が悪いんです。理由は単純で、打席に立つチャンスがあるのはピッチャーが9人に対して投げるうちの1回だけだし、守備でも自分のところに球が飛んでくることばかりではないから。そもそも試合だとバッターがなかなか打ってくれないこともあるので、外野手だと試合中にぜんぜん球が飛んでこないこともあります。
伊藤さん:確かに、ぼくは球技が苦手だったので体育の授業のときは一番ボールが飛んでこない外野手にしてもらっていました。立ってるだけで良いことも多くて助かりましたが、全く上達しませんでした。
chokudaiさん:だから、うまくなるために、素振りとかノックのように「必要な部分を切り出して反復する」ことができる練習の効率化をやるわけです。
akenshoさん:自分はサッカーをやっていたんですが、サッカーの場合には試合形式の練習でもだいぶ強くなれるんですよね。むしろ、試合の一部を切り取って練習の効率を上げようとした結果、実際の試合ではあまり起きないシチュエーションばかりを長時間練習してしまって意味がない、みたいなこともあります。練習のための練習をする罠に陥ってしまう感じです。
競技プログラミングでも、本番で使える形を意識しないで効率化しようとすると、練習のための練習になってしまいやすい面があると思います。
chokudaiさん:野球でも似たような話はあるんですよ。たとえば試合で右打ちのバッターが打って外野のライトに飛んでくる球って、基本的に振り遅れた結果なので、切れて曲がるんです。でも、ライトの守備練習のノックをするときにライトの側を正面に見て打ってしまうと、まっすぐ飛んでくる球を取るだけの練習になってしまう。これだと練習のための練習になってしまうわけです。
競技プログラミングの例でいうと、一般的なアルゴリズムの知識だけを知っていても、それと実践には距離があって問題を解くことは難しいと思います。競技プログラミングの文脈においては、アルゴリズム本を読むだけでは練習のための練習の域を出ないでしょうね。
伊藤さん:ああー、それ、まさにかつての自分です。むかし初めて競技プログラミングに挑戦したときに、アルゴリズムの本は読んでいたので知識はあると思っていたのに、1問も解けなかったんです…。
『アルゴリズムイントロダクション』という大学の教科書になるような本も一通り読んでいたので、「よし、自分は基礎は抑えているぞ」という気持ちで挑戦したのにまったくダメで、ショックでした。
akenshoさん:『アルゴリズムイントロダクション』は、アルゴリズムそのものを解説している書籍としては名著ですよね。コンピュータサイエンス的な観点でアルゴリズムについて勉強する場合には、間違いなく読んだほうがいい本ではあります。でも競技プログラミングには向いていないんですよね。
chokudaiさん:あの本は、冒頭で計算量の話をしっかりしていて、そこでO記法とかΘ記法まで含めてみっちりやるから、確かに勉強にはなるんですよ。Twitterで間違ったことを書かないためにも読んだほうがいいかもしれない。でも競技プログラミングだと、ああいう本に出てくる有名なアルゴリズムを適用するだけで解ける問題が実はあんまり好まれなくて、ひねった問題が多く出るから、それを踏まえて効率的な練習をしないと強くはなれないんですよ。
所澤さん:競技プログラミングも、知識と実践の間には距離がありますよね。
伊藤さん:ところで、ある程度まで競技プログラミングの問題を解く練習を続けていると、問題が解けたときに自分自身でも「なんでこんなにすんなり解けたんだろう」と感じることがありますね。そう感じることが不思議に思えたので、『私たちはどう学んでいるのか ― 創発から見る認知の変化』という本を読んでみたんです。この本がなかなか面白くて、そこにバッチリ、この理由も書かれていました。
所澤さん:伊藤さんはこういう本が好きですよね。
伊藤さん:そうなんです。で、詳しくは書籍を読んでいただきたいのですが、ざっくりまとめると、「人間はさまざまな入力や出来事をきっかけににして自分の頭の中にあるいろんな過去の記憶同士を結びつけて知識を『創発』するのではないか」ということです。そして、その知識の創発に至るプロセスを自分自身で認識することは難しい、と。だから、量をこなして自分の脳に負荷をかけ続けていると、自分でも意識しないうちにどこかで記憶と記憶が結びついて、問題を解ける状態に達するんじゃないかと思いました。こういう「創発」を起こすためにも、継続して問題を解き続けるのが有効なのではないかと想像しています。先の話を踏まえると、創発を起こしやすい練習の仕方がある、とも言えそうです。
chokudaiさん:はい、やはりスポーツにも共通する話ですね。効率の良い練習を重ねているうちに、意識せずとも体が先に動くようになっていくのだと思います。
実際、先ほどの記事で青色コーダーになった方の話に戻ると、もう一つ象徴的なこととして「バーチャルコンテストを中心にやっていた」というのも大きいと思うんですよ。
伊藤さん:なるほど?
chokudaiさん:記憶という観点では、やっぱりコンテストに出たりして実戦でやるほうが、そのイベントや出来事と結びついて強く印象に残りやすいですよね。他の人と競争したり、感想戦でコミュニケーションしたりもあるわけですし。競技プログラミングも、ひとりで過去問を解き続けるだけよりも、定期的にコンテストに参加するほうが実力を伸ばしやすいと思います。
伊藤さん:確かに先の書籍にも、人間の知識はそういうイベントというか文脈と切り離せないと書かれていました。ぼくはまだ過去問ばかり解いてるのですが、やっぱり実戦にも入ったほうがよさそうですね。わかってはいたのですが…。
所澤さん:伊藤さんもそろそろコンテストに出ましょう!
認知コストがなくなるまで反復訓練すると面白くなる
伊藤さん:いましているのは、練習にあたっては「単純な量」を増やすだけでなく、「必要なことをうまく切り出して反復する」のが大事という話なのだと思います。実はこれって、野球やサッカーや競技プログラミングだけではなく、プログラミング全般にも言えますよね。競技プログラミングに限らない。というのも、プログラミングの勉強で文法とかライブラリの知識を増やす、つまりインプットすることが大事だと認識している人は少なくないと思うんですが、自分はむしろ反復訓練が重要ではないかと常々思っているんです。今日こうしてお話を聞いて改めて思うのは、プログラミングって案外とスポーツに近いなと。
chokudaiさん:そうです。スポーツですよ。
伊藤さん:ところが、プログラミングは知的労働という印象が強いので、知識が大事そうだと思っている方も多いと思うのですよね。でも我々プログラマーは、普段if文やfor文を書くときにまで頭を使っているわけではないわけです。プログラミング言語の基本的な文法なんかは、身体がそれを反射的に入力していて、認知エネルギーを使わずに記述できていると思います。そうできるようになるまで訓練しないと、生産的なプログラミングは難しいと言ってもいいでしょう。プログラマーであれば、経験的にそれがよくわかっていると思います。ただ、まさに自分がそうなんですが、そういうことができるようになったのは、実現したい仕組みがあったり「これを動かしたい」という強いモチベーションがあったから、それを目指す過程で自覚せずに反復訓練をしてきた結果だったりするんですよ。そうやってプログラミング言語の基本は使いこなせるようになったから、「プログラミングにも練習量が大事」という感覚を忘れていたことに気づきました。
所澤さん:実は、ぼくは伊藤さんとはちょっと違って、完全に反復練習をとおしてプログラミングを覚えたんですよ。
伊藤さん:そういえば所澤さんは、もともと営業職で、社会人になってしばらく経ってからプログラミングを覚えたんですよね。
所澤さん:そうなんです。今34才で、ソフトウェア・エンジニアになったのは28才のときでした。最初にプログラミングの学習に使ったのはpaizaさんのオンラインレッスンで、あれは「AとBのうち大きいほうを出力しなさい」といった問題を何十問も解くことでif文の書き方を覚えたりするので、まさに反復練習なんです。そうやって「if文の書き方」の認知コストを下げることでプログラミングをできるようにしていくんです。この学習パターンは大人でもありだなと思っていました。
伊藤さん:やる側が「これは反復練習なんだ」と自覚しているかどうかも重要そうですね。もちろん、自覚していなくても「そのパターンがハマって伸びる」は起きるんですが。
いずれにせよ、基本的な式や文を記述するのに認知エネルギーをかなり使ってしまうと、より大きな問題を考える余裕がなくなるのはあると思います。その意味で、プログラミング初心者が反復練習によって「あまり考えないで済む当たり前のこと」を増やしていくと、それで余った認知エネルギーをより根源的な部分に使えるようになるんだと思います。
akenshoさん:AtCoderでも、最近は営業として入社してもらった方へのプログラミング教室として、AtCoder Beginner Contestのいちばん簡単なA問題やB問題をPythonで一緒にやってもらっています。それで文法に慣れてくると、「頑張って文法を正しく書かないといけない」という負荷から開放されて、もう少し上の抽象的な側面を考えられるようになるみたいですね。
伊藤さん:それはとても象徴的な話ですね。より抽象的なことを考えられるようになるには、その手前にかけるコストを小さくできるまで繰り返せば良い。プログラミングに挫折した経験のある人の多くは、そういう基本的な部分に対する認知コストがなくなるところを越える前にやめてしまっているのかもしれないと思いました。そこを越えられると面白くなるんだけど、やはり難しいのかな。
chokudaiさん:我田引水かもしれませんが、その「認知コストがなくなるところ」を越えるのに競技プログラミングはすごくいいですよね。だって、たとえば「ゲームを作りたい」みたいなモチベーションでプログラミングを始めると、そもそも千行以内で完成させるのが難しいじゃないですか。「ゲームができた」という成功体験を得るまでにどれだけたくさんのステップが必要かを考えると、競技プログラミングって、認知コストが十分に低くなって「プログラミングが楽しくなる」までの反復練習としてかなりおすすすめだと思うんですよね。
所澤さん:そうですね。そもそも、プログラミングは覚えたいけど作りたいものが思いつかないっていう人もけっこういると思います。その点、競技プログラミングは問題が無限に降ってくるので、反復練習にはいいですよね。
競技プログラミングではじめるプログラミング
伊藤さん:先にも少し触れましたが、ぼくは15年前くらい前に一度、はてな社にいたときに競技プログラミングに一回挑戦したことがあったんです。社内で競技プログラミングをやっていたメンバーたちが、開発合宿か何かのイベントの余興で競技プログラミング大会を主催してくれて、そこで簡単な問題を何問か出してくれたんですが、ぼくを含めてその場にいた多くの人が1問も解けなかった。
chokudaiさん:それは出題側が難易度の見積もりでやらかしをした可能性がありますね。
伊藤さん:どんな問題だったのか、今となってはまったく覚えていませんが、たぶん彼らは「これぐらいだったら、この人たちなら解けるだろう」というレベルの問題を出題してくれたはずです。いちおうみんなシステム開発をしていてプログラミングができるので。
chokudaiさん:それはそうですよね。
伊藤さん:先にも述べた通り、アルゴリズムや計算量の教科書を読んではいたので、それらの基本はわかっているつもりでした。でも、まったく歯が立たなかったんですよね。いま思い返すと、できなかったのは当然で、効果的な訓練が何かを知りもせず、お二人のいう練習のための練習にしかなっていなかったからなんですが、それからずっと競技プログラミングには苦手意識のようなものがあったんです。
chokudaiさん:みんながTopcoderを盛んにやっていたころの話だとすると、AtCoderはもちろん、日本語のコンテスト自体がほとんどなくて、会津大学がやっているAizu Online Judgeがあったどうかという時期だと思うので、練習の場もあまりなかったかもしれません。
伊藤さん:そんな状況だったと思います。ちょうどそのころ、当時のPFIにいた田中英行さんや太田一樹さんらと仕事でご一緒する機会があったんですが、ご存じの通り彼らは競技プログラミングの世界大会に出ているような人たちなわけです。目の前にいる彼らのようなすごい人たちと、ごく簡単な問題すら解けない自分に彼我の差を感じてしまって、言いようのない無力感に苛まされました。しかし今思うと、落ち込む必要はなかったんだなあ、と。ドリブルの練習をしたこともないサッカーの素人がプロの選手を目の前に落ち込んでいるという状況だったわけですね。
chokudaiさん:田中さんはTopcoderでもかなり上位にいましたね。日本人の登録者数がアクティブで百数十人だったころ、田中さんは常時10位以内にいたんじゃないかな。「赤」にも一瞬いて、だいたいは「黄色」にいた感じだったと思います。Topcoderのハンドルネームがかっこよくて、「haskell-master」なんですよね。
伊藤さん:田中さんは当時からHaskellを使いこなしていて、本当にすごくて。ぼくが一番最初に目にしたすごい競技プログラマーが田中さんだったので、野球を始めて一番最初にイチローに出会ってしまった感じです。相手がイチローとも知らず、自分と比較してショックを受けていたわけで、笑い話ですよね。当時のぼくは、それを「才能やセンスの違い」ということにして、自分の怠慢さを誤魔化そうとしたんだなあと、今になっては思います。
でも、そういう経緯があったことでHaskellという言語に憧れを持てたのもあります。それでずっと「Haskellをちゃんと書けるようになりたい」と思っていたんですが、もう45歳になるし、今やらなかったら多分一生やらないなあと。でも作りたいものは別の言語で書けてしまうから使う機会がない。だったら苦手に感じていた競技プログラミングをHaskellでやってみようと思ったのが再挑戦のきっかけでした。
chokudaiさん:伊藤さんが自分の得意な言語ではなくHaskellを使ってくれたのは、すごくよかったなって思います。というのも、実務経験がある得意なプログラミング言語で競技プログラミングに挑戦する人がつまづくのって、当然ですが「競技プログラミング独特のところ」なわけです。でもそこって、がんばって訓練しても実務で役に立つとは限らない。だから、ふだん使っている言語で競技プログラミングを始めていたら、もしかするとまたネガティブな感想になっていたかもしれません。
伊藤さん:競技プログラミングに強くなるという動機がないと、そのための訓練にメリットは感じにくいですよね。
chokudaiさん:だから、できればあまりプログラミング経験がないうちから競技プログラミングに触れてほしいと思っているんです。それこそif文やfor文を書く感覚を競技プログラミングで培うことになるわけなので、これはメリットを感じやすい。
伊藤さん:確かに、ぼくがいま競技プログラミングをやっているのも、「競技プログラミングで勝ちたい」というよりは「Haskellを覚えたい」という動機のほうが強いです。
akenshoさん:私も、新しい言語を覚えるときにAtCoder Beginner Contestの簡単な問題を解き直すことがあります。chokudaiさんもけっこうプログラミング言語の再学習をやってますよね。
chokudaiさん:去年ICFP Contestに出たときはチームでRustを使ったんだけど、ふだん書いていない言語だったので3日前くらいから200問くらい解いて、それでだいたい書けるようにしました。
伊藤さん:3日で200問! すごいですね…。
ぼくも先日、B問題をひたすら解くのをやりましたが、そんなにたくさん解けませんでした。1日に2、30問ぐらいだったかな…。
競技プログラミングに強い言語はあるか
伊藤さん:競技プログラミングでプログラミング言語の基礎を訓練するのは、実際かなり効果的だと思います。ぼくもHaskellの基本文法については、ほぼ認知エネルギーを使わなくても出てくるくらいには覚えられました。一方で、競技プログラミングには向いている言語とそうでない言語があるようですね。
例えば、よく引き合いに出されるのが、田中英行さんが以前にTwitterに引用されていた「Tier」という概念だと思います。もう引用元のURLは消えてしまっているのですが、元々は2018年に取られたベンチマークをベースにした区分けだったようです。それによると、最速の「Tier1」はC/C++やRustで、次いで「Tier2」がJavaやC#やGo、HaskellはJavaScriptと同列で「Tier3」、「Tier4」はPythonなどのいわゆるスクリプト言語と区分されています。
chokudaiさん:それは主に計算速度によるプログラミング言語の区分ですね。競技プログラミングでは、必要なロジックを全部記述してそれを制限時間内に実行できる必要があるので、どうしても言語の実行速度の差が露骨に出るところがあります。これがウェブ開発だと、そもそもファイル入出力やネットワーク通信にけっこう時間がかかったり、重い処理をライブラリーに任せられるみたいな面があるので、そこまで言語自体の速度が重要ではないと思うんですが。
とはいえ、制限時間内に実行が終わらない、いわゆる「TLE」になる場合には、その理由がどこに存在するかを見極めるのも大切です。
伊藤さん:計算量からすれば問題なく実行が終了するはずだけど、その言語ならではの落とし穴みたいなところにはまってTLEするケースがある、ということですか?
chokudaiさん:たとえばC#は、多次元配列がすごく遅いんですよ。4次元配列を使うと、中でチェックが走っているからか、速度が遅くなってTLEになることがあります。なので、コンテストによってやり方を変えたり、いろいろこねくり回さないとダメな場合があります。
あと、Rustは言語の速さでいったらTier1に分類されると思いますが、実際に競技プログラミングで使うとしたら、そこまで有利とは限らないはずなんですよね。理由は単純で、型が厳しすぎるからです。もちろんソフトウェア開発という観点だといいことなんですが、i32とsize_tを行ったりきたりする必要があるような問題だと、そこで型を通すためにいろいろケアするのが時間的な足枷になることがあるかもしれないですね。所有権も、競技プログラミングをやる上では同様です。
伊藤さん:まあ、制限時間内に実行できれば、極端な話、メモリーリークしてもいいし型も緩くていいわけですからね。
chokudaiさん:それこそ認知コストの話ですよね。Rustでコンパイルエラーを出さないように書こくことに思考が取られると、アルゴリズムが大丈夫か、ランタイムエラーが出ないか、コーナーケースがないかを考える余裕が奪われます。競技プログラミングにおいてC++の需要が高いのは、やはり「何でもできて速い」のが理由ですよね。
伊藤さん:なるほど。C++のマニュアル運転的な部分が、競技プログラミングにはフィットしやすいわけですね。かつ、C++ではショートカット的な手法もたくさんあるみたいですよね。ぼくはふだんC++を書かないので、他の人の回答や解説のC++のコードを見ても意図が読み取れないことも多いんですが。
chokudaiさん:あと、Pythonはさっきの分類だとTier4で遅いことになっているんですが、今となってはユーザーがすごく多く、PyPyという選択肢があったり、NumPyを使って速く書くテクニックがいろいろあったりするので、すでにTier4から脱出して普通に競技プログラミングで使える言語になっていると思います。実際、AtCoderでもPythonがめっちゃ使われているんですよ。今の時点でPythonをおすすめするわけではないですが、これからおすすめ度は上がっていくと思います。
所澤さん:そうですね。周囲を見てもAtCoderをPythonでやっている人は多いですね。ぼくもPythonを使っています。先の青色コーダーの方も、はじめはPythonでやっていて、途中でC++に持ち替えたとありました。鹿本も、解答例はC++とPythonの両方で書かれています。処理系の改善によって速度が向上しているといえば、JavaScriptも、商業的に成功して世界中の天才が処理系をチューニングした結果か、Node.jsなどの実行エンジンがとても速くなっていますよね。
chokudaiさん:JavaScriptも本来は遅い言語ですが、速度を速くしようと命をかけている人がたくさんいるので、Pythonと同じようにすでに上にきている可能性はありますね。
akenshoさん:伊藤さんが使っているHaskellは速度的にはどうなんですか?
伊藤さん:Haskellは、いわゆるスクリプト言語よりは十分に速いのですが、Tier3ということで、GoやSwiftよりは後ろに置かれていますね。感覚的にも、そうかなと。その理由の一つは、遅延評価によるところが大きいんじゃないかと思っています。評価前の計算をメモリに溜め込むので、おそらくそのコストで遅くなってしまうことがありますね。あと、Stringなどの基本となるデータ構造があまり速くないんですよ。もちろん、いずれも速くする書き方はあります。ただ、いったん書いてから遅いことに気づいて書き直すとなると、どうしても手数が増えます。
とはいえ、Haskellを使っているせいで通らなかった問題があるかというと、自分はまだ当たったことはありません。あまり意識をしなくても速度が出るTier1やTier2の言語に対し、意識して速く書く必要があるのがHaskellがTier3に置かれている理由かなあ、と。
chokudaiさん:「関数型で書くのは難しいけど、手続き型で書くのが簡単」という問題もあるかもしれない。たとえば問題例を見ていると、AtCoder Beginner ContestのB問題とかは、手続き型で書く人を想定してBという難易度になっていると思います。
Haskellで楽しむ競技プログラミング
伊藤さん:それでも、Haskellみたいな関数型のプログラミング言語で競技プログラミングの問題を解くのは、やってみるとなかなか面白いんですよ。なので、今となっては勝つためというより楽しむためにHaskellを使っています。手続き型のときとは異なる計算モデルでコードを書くことが要求されるので、最初のうちは「Pythonだったらさっと書けるのに」ということもあるんですが、だんだん自分の中に別のメンタルモデルができてくるんです。
たとえば、AtCoderで最初にやってみようという問題のうち、Bレベルのものの代表として、「Shift only」という問題がありますよね。与えられたN個の整数すべてを2で割るのを最大で何回繰り返せるか求めよ、という。
chokudaiさん:2で割り切れない数が出たら終わりで、そこまでの繰り返しの回数を出すっていうやつですね。
伊藤さん:この問題を手続き型のプログラミング言語で解くなら、「N個の数を順番に2で割っていくループで、奇数が出たらリターンする」のを回数をカウントしながら繰り返す、という感じになると思うんです。ループの外側のスコープにカウンターを用意して、そのカウンタを書き換えることによって計算結果を得ますよね。
でもHaskellで解くときには、自分の場合、現在の「N個の整数の配列」の状態を考えて、それに対して「それぞれを2で割る」という1対1写像を繰り返して「奇数が含まれる」という状態へ収束するのは何回めか、のように考えると思うんです。頭の中で値が別の値に写っていく様子を描いていて、配列全体をxという値と見立てたときに、x1 -> f -> x2 -> f -> x3 ...
と関数によって写像されていくというか…。
chokudaiさん:そっちのアプローチなんだ。自分が関数型のプログラミング言語で書くとしたら、「整数を2で割れる回数」を返す関数を作ってマップしてあげて、その最小値を取ってあげる、という実装にするかな。
伊藤さん:はい、実際の実装としてはそれに近いものになります。メンタルモデルとしては、「forループのようなもので先頭から見ていく」とか「カウンターの値を書き換えていく」とは考えず、入力によって出力の値が変わっていくのを高階関数なり再帰なりで書くという認識になるんですよね。関数によって値を写していく。そういうメンタルモデルに自分の頭の中がだんだん変わっていくのがとても面白いです。
akenshoさん:関数型脳みたいなのは、そもそもこの問題を手続き型のプログラミング言語で解けるような人が関数型のプログラミング言語を書いていくことで生成されるものなんですかね。
伊藤さん:最初から関数型言語でプログラミングを始めた人で「forループがわからない」と言っている人を1人、2人は知っています。彼らに言わせると、「すべての基礎に再帰がある」という考え方の方がずっとわかりやすいって。最終的にやっている計算としては一緒なので、どちらもメンタルモデルが違うだけなんですが。
所澤さん:自分は、ループの前に再帰を思いつく段階には至れていないですね。どちらでも同じ問題が解けるというのは、言われたらわかるけれど、自然とは出てきません。
chokudaiさん:ぼくも、書き換えろって言われたら書き換えられるけど、先には出てこないかな。だから自分が関数型を使うときは、まず手続き的な書き方で考えて、それを関数型で書くにはどうしたらいいかなって変換作業が入りますね。
伊藤さん:実際、Haskellで競技プログラミングをやるまでは、ぼくも最初はこういうメンタルモデルではなかったんです。過去にElixirやErlangを触ったこともあるので、関数型プログラミング言語を使うこと自体は初めてではなかったんですが、やっぱりこれまでは頭の中にメモリのレジスタマシンがあって手続き型で考えていた。それが競技プログラミングで反復練習をしたことで、ようやく関数型脳みたいなものに近づいてきたのかもしれないと思っています。
chokudaiさん:伊藤さんから見て「これは関数型のほうが解きやすいな」と思うような問題もあるんでしょうか?
伊藤さん:普段、AtCoderの問題を手続き型で解いていないので比較はできないんですが、例えば連長圧縮を使う問題なんかはリスト処理が中心になることが多いので、そうするとHaskellの豊富な関数や遅延評価が活きてきますね。遅延評価は、速度のところで話した通り、デメリットになることもあるのですが。
一般に、計算が冪等で済むものだけで構成できる場合には、Haskellで書くと簡潔に書けて良いと思うことが多いです。バケットを作ったり累積和を取ったりした結果のデータ構造に対して畳み込み計算をする、といったケースですね。案外、グラフの問題なんかも、慣れてくると認知コストを抑えて解くことができます。
所澤さん:典型的な話として、Haskellだと動的計画法(DP)を使う問題をどうやるかという話はありますよね。再帰の定義を工夫するか、配列でメモ化するのか…。
伊藤さん:そうですね。DP表を更新していくような正攻法のDPだと、書き換え可能な配列を使いたくなりますがどうしても手数が増えてしまいます。できないわけではなくて、書き換え可能な正格評価の配列を使って手続き的に記述することで、メモリ使用量を抑えた高速な計算はできるんです。
ただし、やや記述量のあるAPIを使う必要があリます。上級者の方々は、DP表を使わずにHaskellらしいコードで計算を済ませる人もいますが、ぼくはああいう実装をさっと出せる感じがまだ全然ないです。
同様に、途中までイミュータブルでいいやと思って書いていたけれどやっぱり書き換え可能なデータ構造に変えたい、ということがあると、実装を関数型から手続き型に切り替える必要があったりして手戻りが多くなってしまいます。ミュータブルなデータ構造を使うときにはモナドを意識する必要があるんですが、先ほどのRustの型の話と同様に、認知コストがそちらにとられることはあると思います。
chokudaiさん:Haskellのメモ化については、それこそ田中英行さんが昔書いた記事を読んだことがあって、なんかめんどくさいなって思った記憶があります。
伊藤さん:田中さんが書かれている方法は、遅延評価を活かしたより高度な手法に見えますね。
ちなみに田中さんも、最近は競技プログラミングのメイン言語をRustに切り替えたというような話をツイートされていました。
競プロは実世界の業務に役立つか
伊藤さん:ここまで競技プログラミングについて話してきたことって、実は仕事のプログラミングにも通じる内容がほとんどだと思うんです。つまり「競技プログラミングは実務に役立つ」と言えるのではないかと思います。そこで、果たして本当に競技プログラミングは役に立つのか、ここで改めてお二人に聞いてみたいです。この話はFAQすぎるので、お二人からすると「また聞かれるのか」という感じかもしれませんが、やはりテーマとしては外せないと思うので…。
chokudaiさん:それはもう10年以上、何度となく言われていますね。そのときの言い分としては、「自分たちは実務に役立てるために競技プログラミングをやってるんじゃない」という観点と、「実務って、具体的には何の仕事のこと?」という観点と、2つあると思っています。
伊藤さん:仮に競技プログラミングやっている人で、「実務に役立たない」という人は、そのいずれかだろうと。
chokudaiさん:「完全に役立たない」と思ってる人は、ゼロではないけれど、そんなに多くないと思うんですよね。たとえば、「同じ会社のソフトウェアエンジニアがAtCoderで『緑』なら安心」みたいな言い方って、ある程度まで競技プログラミングをやっている人ならほとんど全員がするはずなんですよ。
伊藤さん:そうですね。一休のように比較的大規模なデータを扱うシステムだと、大量のデータを読み込むプログラムを何気なくデプロイしてしまってシステムをクラッシュさせるような事故もありうるわけですが、AtCoderで「緑色」に入っているくらいの人なら必ず気をつけて書いているはず、という安心感はあります。競技プログラミングをやっていると、その時々の自分の実装の計算量を考えずに記述することはまずないですからね。
chokudaiさん:適当にコピペを組み合わせるとか、なんとなくライブラリに任せるのとかではなく、自分で計算量まで含めてロジックが考えられるよね、という感じになりますよね。
akenshoさん:そもそも、「プログラミングが必要なすべての仕事で競技プログラミングが役立つ」という話をすることもないですしね。
chokudaiさん:世の中のスキルってだいたいそんなもんだと思うんですよ。たとえば「TOEICは仕事に役立ちます」みたいな話に近いと思っています。点数が高いほど役立つ仕事もあるだろうし、逆に点数が低くても多くの仕事はできるでしょう。それが競技プログラミングの話になると、そもそも英語を使わない業務の人が「業務で日本語しか使わないからTOEICの点数などいらない」と言っているのに近いような状況もありますからね。
「実務のすべての面において競技プログラミングが役立つ」なんて言えないのは当たり前なんですが、ではどこに役立つのかと言われると、だいぶ細かい議論になってしまう。
所澤さん:そうですね。実際、Web開発だとアルゴリズムの知識が要求されるような場面自体は少ないですからね。月に1回あるかないかぐらい。場合によってはほぼない。そういう側面だけから結論を導くと、「ほとんどの人はアルゴリズムを書けなくてもプログラムを書く仕事はできる」という話になってしまいます。でも、それはそれで少し安直すぎるかなあと思います。
chokudaiさん:それはよく言われます。面白いことに、それに対する考え方は会社とか人によってけっこう違うみたいなんですよね。
伊藤さん:いますぐアルゴリズムやデータ構造を勉強すべきかどうか、ぼくはそれ自体はわりとどっちでもいいと思っているというか、好きなようにすればいいと思っています。勉強を強制的にさせられても面白くないだろうから。一方で「Web開発者も知っていた方がいいかどうか」と聞かれたら、それは「知ったおいた方がいい」と答えます。
Webアプリケーションは20年くらい運用されることもあるわけですが、その間にみんなが計算量をまったく意識せずに書いたコードが積みあがっていくと、ボディーブローのように性能に影響してきます。結局、どんなアプリケーションであっても、計算量をちゃんと意識したコードを書けるほうがいいのは間違いない。でも、Webアプリケーションを書く実務で計算量がシビアになるコードを書く機会が発生することがあまりないから、ほとんどの人は業務経験だけでは計算量を意識したコードを書くために必要な反復練習をしていないことになるんですよね。
chokudaiさん:アルゴリズムの計算量を理解して自分で判断できるほうが、すっきりコードが書けて品質も高くなるとは思います。率直なところ、計算量を考えずに書いたコードで致命的なミスが起きないとしたら、それは「パターン」でなんとかしているケースがけっこうあると思っているんですよね。
伊藤さん:パターン、ですか?
chokudaiさん:「JOINは避けるべき」みたいなやつですね。やってはいけないことを理論的に説明できるわけでなく、そういうパターンだけでやっているという状況では、迷ったときにどうしても「やってはいけない」とされていることを避ける書き方になりますよね。理屈がわかっていれば、必要ないときにはそういう迂回するような実装をしなくて良い。つまり、パターンだけでどうにかしようとすると実装量が増えることになると思います。そこまで考えると、アルゴリズムと計算量から書き方を判断できるほうがいいとは思います。
伊藤さん:ああ、それはそうですね。おっしゃる通りで、昔の自分を思い返すと、確かにそうだったなっていう記憶があります。20年くらい前の自分は理屈を知らなかったから、こういうのは危なそうだなとか、根拠もなく実装をこねくり回して作っていました。そうして出来上がった無駄な実装は、運がよければ問題を回避できるけど、そうでないと当然遅いから、そこを回避しようとしてまたこねくり回して、必要以上に複雑になるという。
chokudaiさん:その点、競プロは時間制限があるから、そういうことを最初から考えざるをえなくなりますよね。しかも「とにかく高速化」ではなく、「2秒」といった制限をクリアすればいいので、必要な分だけ高速化しながら実装量が少ない手法を選ぶことになります。これができる人はなかなかいないし、その練習で培った感覚みたいなのって、たぶんWeb開発の実務でもかなり役立ちますよね。
所澤さん:Webエンジニア目線だと、AtCoderの「茶色」か「緑」になるあたりで、そういう感覚が一気に「地に足がついた」ものになる気がしますね。例えば、N=20程度しかないデータを扱っているようなときに、コードレビューで「O(N²)だからダメ」だって言われたときに違和感を感じる、みたいな。「パターン認識でO(N²)だからダメって言っているな」といった実感が湧くのは、競技プログラミングをやっていてよかったと感じているところです。
伊藤さん:繰り返し問題を解いていくと「これぐらいのデータ量なら数十ミリ秒ぐらいで終わるだろう」といった感覚がわかってきますよね。そして、その感覚がシステム開発でプログラミングをしていたときに「これは遅いだろう」と思っていた感覚とだいぶズレがあったりして。
所澤さん:わかります。処理すべきデータ量が1万とか10万という桁になったときに「さすがに万単位だと遅くなりそう」みたいな感覚だったのが、競技プログラミングをやった後では「このデータ量でも、この実装でOKなら遅くならない、大丈夫」と自信を持って判断できる。
chokudaiさん:線形時間でなくて対数時間の処理だから余裕だよね、みたいな感じかな。
伊藤さん:逆に線形時間の処理であっても、最大でも5000件程度にしかならないデータなら、わざわざ複雑なロジックを持ち込まずに力技で処理してしまって良い、といった判断ができるんですよね。オーダーの感覚がないと、「全件データを持ってきて処理するのは良くないんじゃないか」と思い込んで考えてしまったりする。でも競技プログラミングで反復練習をすると、「高々5000なら大したことない、全部読み込めばいい」という感覚が身につきます。将来5億件になる可能性のあるデータなら、やっちゃいけないんだけど、たとえばBtoBなんかだと「お客さんは最大でも5000社」みたいなケースはよくありますからね。
akenshoさん:その観点だと、実務でプログラムを書かない人こそ競技プログラミングでその感覚を身につけてほしいのはあるかもしれない。たとえば就活サービスをやっている会社で、それこそ経営者とか営業みたいな立場で新しい機能を提案するときに、「人材側が持っているスキルと企業側で欲しいスキルのマッチング数はこのオーダーだから、お勧め度を毎回計算して出しても大丈夫」みたいなレベルで提案ができるとめちゃめちゃ強いですよね。
伊藤さん:そうですね。そういう提案でオーダーを考えることができる人には、「その体験を現実的に作れるかどうか」がわかる。そこがわかるのは相当大きいと思います。
chokudaiさん:企画を考えるうちは、実装や設計までできる必要はなくて、できるできないの判断ができるところだけで十分なんですよね。そこができれば無茶な提案もしないし、それだけでエンジニアの信頼も得やすくなる。それだけが理由じゃないけど、エンジニア以外にも競技プログラミングをやってほしいなとは思うところです。
AtCoderがあってよかった
chokudaiさん:今日の話は、どちらかというと「競技プログラミングはプログラミング自体の反復練習になる」とか、「競技プログラミングはエンジニア以外にもおすすめ」とか、あるいは「Webエンジニアで『茶色』とか『緑』なら十分に実務にも役立つ」といった話をしてきたんですが、最後に一つ、AtCoderとはぜんぜん違う話もしたいんですが、よろしいでしょうか?
伊藤さん:もちろんです。
chokudaiさん:実はいま自分は、AtCoderの社長と並行して、他の会社で「アルゴリズム担当部長」という肩書のお仕事もしているんですよ。そこで具体的にはアルゴリズム開発をやっています。
所澤さん:それは、その会社の製品に組み込むアルゴリズムを考えているんですか?
chokudaiさん:いや、そうした専門的な領域のアルゴリズムでなく、運送や工場についての最適化を解くためのアルゴリズムを必要とする仕事がいっぱいあるんですよ。
伊藤さん:社内に最適化が求められる領域がけっこうあるので、それらに対してchokudaiさんが最適化のアルゴリズムをいろいろ開発しているわけですね。
chokudaiさん:そんな感じです。それで最近思うのが、「こういう仕事ができるためにはAtCoderの『黄色』は欲しいな」ということなんです。
伊藤さん:えっ、「黄色」以上っていうことですか?
chokudaiさん:はい。ぼくの部下には「黄色」以上が必要です。
伊藤さん:なかなかのハードルですね…。さすが、chokudaiさんの要求水準は高い。
chokudaiさん:「青」でも採用する可能性はあるけれど、「黄色」以上でないと使えないだろうな、と思うんですよ。
伊藤さん:アルゴリズムについて高度なスキルを持つ人が求められるような仕事があることは、ぼくにも想像がつきます。ぼくらの業界にも特化型の検索エンジンを開発している会社さんとかがあり、そういう会社さんのブログとかを読むと、やはり競技プログラミングで強い人がかかわっていたりするんですよね。
chokudaiさん:最近はウェブのUIもレスポンシブなところが増えているので、反応が返ってくるまでの時間はますます大切になっていそうですよね。実際、検索エンジンはもちろん、Webページの内容とユーザーの属性から秒で適切な広告を出さないといけない広告配信なんかも、機械学習を含めたアルゴリズムの塊です。
伊藤さん:思い返せば、一休の仕事でも、データサイエンティストが作ったレコメンドエンジンや機械学習のモデルがあり、それを現実的な制約のもとでユーザーの画面に出せるように実装すべく、そういう強い人たちが手を入れる場面がよくあります。今日の一連の話を伺っても、改めて「アルゴリズムに対する効率的なコードを実装できる人」はやはり強いと感じました。
chokudaiさん:そういうアルゴリズムに強い人、具体的にはAtCoderで「黄色」以上の人って、10年前くらいだとほんとに数えられるくらいしかいなかったと思うんですよ。でも今はそれなりの数の人が「黄色」になっています。なので、おそらくなんですが、AtCoderが誕生したことで、そういう仕事をできる人が増えたっていう面があると思っているんです。
伊藤さん:確かに、AtCoderをはじめとする競技プログラミングの普及は、そういう仕事ができる人が増えた要因のひとつだと思います。
chokudaiさん:だから、今はこのへんも「競技プログラミングが実務で役立つか」という話をするときのキーポイントになるかなと。
伊藤さん:なるほど。ぼくの中では、「適切な問題解決にはメンタルモデルが重要だから、普段とは違うメンタルモデルを培うための反復練習にうってつけの競技プログラミングは実務に役立つ」という考え方があったんですが、それに留まらず、本当に強い人たちの土壌がはぐくまれるという側面も当然ありますよね。
AtCoder、本当に素晴らしいサービスだと思います。このような素敵なサービスを作り上げていただき、ありがとうございます。
chokudaiさん:ぼくとしては、とにかく競技プログラミングを広めたくて、最初は当時の2ちゃんねるのプログラミング・スレにTopcoderの問題を貼り付けて翻訳して解くのをやっていたりしたのが原点なんですよね。そこから「これを続けていても普及しない」と思って始めたのがAtCoderだったんですが、ぼくがやりたかったものを青木をはじめとする社員が頑張って作り上げてくれて、それでこうして続けられているのだと思っています。
伊藤さん& 所澤さん:これからも長く続くことを祈っております。
chokudai さん& akenshoさん:ありがとうございます。
この記事では、AtCoderの高橋直大さん、青木謙尚さん、そして一休の伊藤直也さん、所澤友大さんによる座談をお届けしました。
競技プログラミングという具体的な活動を通し、一見すると縁が薄いように思える「プログラミング」と「スポーツ」に共通する反復練習の重要さ、それが練習のための練習に陥らないためのメタ認知、さらには、アルゴリズムの開発そのものが生み出す新しい価値についてまで、さまざまな考えを深めるきっかけになったのではないでしょうか。
興味を持たれた方は、ぜひ一度無料相談を利用してみてください。
編集協力:鹿野桂一郎(ラムダノート株式会社)
撮影協力:帝国ホテル 東京
東京都千代田区内幸町1-1-1
あわせて読みたい関連記事
この記事を読んでいる人におすすめの記事