Javascriptで競技プログラミングに参加してみた

date_range 2022/11/14
GUARDIAN Creative BLOG
web-g11be91697_1920 (1)

Web業界でエンジニアをされているみなさん、競技プログラミングに参加したいと思ったことはありますか?


なんか楽しそうだし、スキルアップのためにやってみようかな…


と思ってみても、一般的にはAtcoder、Topcoder、Codeforcesなどの競技プログラミングで最も使用されるプログラミング言語はC++ですよね。

それからだいぶ差をあけて次いでPythonという感じです。


Web業界で働かれている方、もしくはこれから働きたい方はJavascriptやPHP、Rubyなどのプログラミング言語を使われていることが多いかと思いますが、それらのプログラミング言語はC++に比べて実効処理が遅いため、競技プログラミングにおいて使用されることはほとんどありません。


ところが最近は競技という点にそれほどシビアでなく、むしろスキルアップ、プログラミング学習という点にフォーカスされたサイトも非常に人気が出ています。


そういったサイトではAtCoder等のようなガチガチの競技プログラミングとは違い、JavaScriptやPHPなども含め沢山のプログラミング言語に対応しているのが特徴です。


もっと気軽に、自分の学びたい言語を楽しみながらプログラミングを身につけたい!という方には、そのような競技プログラミングのコンテストに出るのはとてもおすすめです!


今回は、そんな海外で人気のLeetcodeという競プロに出場してみました。


Leetcodeの他にも海外では沢山の競技プログラミングが開催されています。

中にはとてもマイナーなプログラミング言語に対応しているサイトもありますので、自分の学んでいる言語で参加できるコンテストがきっと見つかるかと思います。


過去に書いた記事では、海外の競技プログラミングサイト21選を取り上げていますので是非ご覧ください。

筆者について

・Web業界3年目

・フロントエンドエンジニア

・競プロ未体験(興味はあった)

・バリバリの文系出身

・前職は飲食店(趣味でプログラミング)


正直なところ今までほとんど競プロやアルゴリズムといったものを意識してコードを書いてこなかったのですが、最近になってそのおもしろさが分かり始めてきました。

競プロに継続的に取り組むならC++を覚えないとダメなのかなとも思ったのですがC++はめちゃくちゃ学習ハードルが高いですし、一応現職での役割的にはフロントエンジニアですし、Leetcodeではせっかく多種多様なプログラミング言語に対応されているので、Javascriptでの参加をすることにしました!

Leetcodeのシステム、そして参加してみた結果

Leetcodeのプログラミングコンテストは毎週日曜の午前11:30から、また隔週で土曜の23:30から開催されます。

所要時間は1時間半。

問題は計4問出題され、Easy、Medium×2、Hardの計4つがあります。


どれから解いても構いませんが、まぁ普通はEasyから解くのでしょう。


参加するにあたり、毎回registerという手続きが必要みたいです。

プログラミングコンテストに参加するための手続きと思っておけば良いでしょう。

これはあらかじめしておくことができるので、直前になってやらなくて済むように早めに終わらせておきましょう!



そして肝心の結果ですが、結論から言うと4問中最も易しいEasyの一問しか時間内に解くことができませんでした😭


正直なところ競プロ特有の表現があったりで、そもそも問題文の意味を読み解くことがかなり難しかったです。

これは単純に、繰り返し競技プログラミングの問題を解いて慣れるしかなさそうです。


【Easy】2465. Number of Distinct Averages

0をインデックスとする、偶数長のinteger型の配列numsが与えられます。


numsが空になるまで下記の手順を繰り返してください。


・numsに含まれる最小数を求め、それを取り除く。

・numsに含まれる最大数を求め、それを取り除く。

・取り除いた2つの数の平均値を算出する。


2つの数aとbの平均は、(a + b) / 2で求められます。


・例えば、2と3の平均は、(2 + 3) / 2 = 2.5です。


上記の手順で算出された平均値の個数を、返り値として返してください。


注:最小値もしくは最大値として同じ数値が複数ある場合は、いずれを取り除いても構いません。

Example 1:

Input: nums = [4,1,4,0,3,5]
Output: 2
Explanation:
1. numsから0と5を取り除く。平均値は(0 + 5) / 2 = 2.5,  nums = [4,1,4,3] となる.
2. numsから1と4を取り除く。平均値は (1 + 4) / 2 = 2.5, nums = [4,3] となる.
3. numsから3と4を取り除く。平均値は (3 + 4) / 2 = 3.5 になる.
すると 2.5, 2.5, 3.5 の中に固有の数字が2つあるので、2が返り値となる.

Example 2:

Input: nums = [1,100]
Output: 1
Explanation:
1と100を取り除くと平均値はひとつのみとなるため、返り値は1となる。

(いずれも筆者訳)


問題文を理解するまでに結構時間がかかりましたが、意味が分かった後はこれなら解けそうだという感触がありました!

提出したコード

初めての競プロの問題文をひと通り理解した後は、以下のように考えました。


・「取り除く」という表現がされているが、正しい返り値が返ればいいだけなので実際に配列から要素を削除する必要はない

・最小値と最大値を順に足していくので、numsを最初にsortしておくべき

・そのうえでnumsの要素数をnだと仮定し、0番目とn番目の平均値、1番目とn-1番目の平均値…を求めていけば良さそう


また次にピンときたのは、最終的な答えは重複した値を除いた上での個数なので、Setオブジェクトを使うのが良さそうだということです。

(Setとは英語で「集合」のことです。高校の数1で習うやつですね。Setオブジェクトを使うと重複した値を瞬時に取り除けます)。


最終的に提出したコードは下記です。ただ、後から修正した方がいい点も見つかりました。





実は、最初に提出した際に赤字の部分を書かずにただnums.sort()として提出し、rejectされてしまいました。

原因は、sortをすると文字列として解釈されてしまうため、たとえば nums = [4,1,10,0,3,5] を nums.sort() とした場合 [0,1,10,3,4,5]と返ってきてしまうからです。


https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Array/sort

compareFn が与えられなかった場合、undefined 以外のすべての配列要素は文字列に変換され、文字列がUTF-16コード単位順でソートされます。

例えば、"banana" "cherry" の前に来ます。数値のソートでは、9 80 の前に来ますが、数値は文字列に変換されるため、Unicode 順で "80" "9" の前に来ます。

undefined の要素はすべて、配列の末尾に並べられます。

つまり、数値型の配列の場合は比較関数を渡してあげる必要があります。

それが(a, b) => a - b の部分です。

上記はアロー関数式での書き方になりますが、要するに


function(a,b){

    return a - b;

}


と同じです(実際にそう書いてももちろん問題ないです)。

そこを書き直したうえで、再度提出したらacceptされました。


また、提出後に他の方が書いたコードを見たら次のようになっていました。




いったん配列に格納してからSetオブジェクトに渡していましたが、最初にSetオブジェクトを宣言し後からaddしていけばいいんですね(ー ー;)

あんまりちゃんと使ったことがなかったので分かってませんでした…


こういう感じで、真剣に問題に取り組んだ結果として、他の人のコードを見るのはとてもプログラミングの勉強になります!


次のMediumに関しては、先に言及した通り時間内に提出ができなかったのですが、せっかく取り組んだのでご紹介したいと思います!(需要はあるのだろうか…)


ただ思ったより長くなったため、また次回の記事に持ち越しとさせていただきます。

参加して思ったこと

いやー、難しかったのですが競プロめちゃくちゃ楽しいです!


感覚としては、実務で行うプログラミングは覚える範囲がとてもとても広く、たとえばWebアプリを作るのであればネットワークやデータベース、APIの使い方、セキュリティなど総合格闘技的にあらゆる方面で学ぶことがあるかと思います。

それに比べて競技プログラミングは基礎文法さえ身についていればとりあえずチャレンジすることができるので、もっと気軽に競プロに取り組んでみてもいいんだ!と思うようになりました。



その代わり難易度の高い問題ほど数学的な発想力が必要になるので、競プロもやはり易しいものでは決してなさそうです。

どちらがいいかと言う話ではないので、実務でも競プロも、お互い補い合って身につけていくことでやがて相互的に作用し合うようになるのではないでしょうか。自分はそう期待して、今後も競技プログラミングを続けてみようと思っています!

プログラミングを学び始めて基礎文法をひと通り覚えたよ!と言う方は、一度競技プログラミングに挑戦してみてはいかがでしょうか。


次回、結局最後まで提出できなかったMedium問題を掲載したいと思います!

実のところ問題文がちんぷんかんぷんで、ようやく理解できてコードを書き始めようかというところで残り10分になってしまっていました(笑)


残りの2問に関してはチャレンジすらできなかったと言う体たらく…。・゜・(ノД`)・゜・。


そんな失敗談でしかない次回の記事ですが、ぜひ笑って読んでくだされば幸いです!


最後に今回開催されたプログラミングコンテスト問題のリンクを載せておきますので、興味がある方は是非チャレンジしてみてください!(Leetcodeへの登録が必要です)


2465. Number of Distinct Averages

2466. Count Ways To Build Good Strings

2467. Most Profitable Path in a Tree

2468. Split Message Based on Limit

人物

マーケティング部 クリエイティブプロダクト隊 クォリティコントロール班 班長

松村 晶(まつむら あき)


HTMLとCSSから始まり、Web関連の記事など広く更新していきます。

毎週2回(月・金予定)に欠かさず更新(できたらいいなと思っている)


今回使用した技術:Javascript