唯物是真 @Scaled_Wurm

プログラミング(主にPython2.7)とか機械学習とか

AtCoderの参加回数が多い人ほどレーティングが高い?

前にAtCoderのレーティングの分布について調べて記事を書きました
sucrose.hatenablog.com
Twitterで「AtCoderにたくさん参加すればレーティングが上がる」という話を見かけて気になったので、ユーザーの参加回数とレーティングの関係を雑にグラフにしたりレーティングの分布のグラフを書いたりしてみました

調べるのが簡単なAtCoderのランキングに書いてある現在のレーティングと参加回数の表のデータを使いました

AtCoderのレーティングの分布

レーティングの色分けについてはこちら↓

1回しか参加していないユーザーも数えているので灰色が全体の半分近くを占めています
f:id:sucrose:20180128234236p:plain

レーティング レーティング上位何%か
2800(赤) 1%
2400(オレンジ) 2%
2000(黄色) 5%
1600(青) 11%
1200(水色) 21%
800(緑) 35%
400(茶色) 52%
0(灰色) 100%

参加回数とレーティングの関係

見たところ10回から15回ぐらいの参加まではレーティングの平均が大きく変わっていそう
その後の参加回数が増えるごとにレーティングの平均は徐々に増えていっているように見えますが回数が増えてもばらつきは結構大きいです
f:id:sucrose:20180129010422p:plain

感想

参加回数が多いほどレーティングの平均が増えていそうな気もする
やる気の有無が参加回数とレーティングの上昇の両方に関わってくると思われるので、もっとちゃんと調べるなら個々のユーザーのレーティングの上下を追ってちゃんと調べないとダメそう

おまけ: 箱ひげ図

箱ひげ図も書いたのでついでに貼っておきます
上の平均と標準偏差の棒グラフと見比べると結構印象が変わります
f:id:sucrose:20180128235546p:plain

ソースコード

# coding: utf-8
import pyquery
import requests
import time
import scipy.stats
import numpy as np
import pandas as pd
import seaborn as sns
import matplotlib.font_manager
import matplotlib.cm as cm
import tqdm
import matplotlib.pyplot as plt

rating_atcoder = []
counts = []
for i in tqdm.tqdm(xrange(1, 129)):
    table = pyquery.PyQuery(url='https://atcoder.jp/ranking?p={}'.format(i))
    for elm in table.find('tr')[1:]:
        tr = pyquery.PyQuery(elm)
        tds = tr.find('td')
        rank = int(pyquery.PyQuery(tds[0]).text())
        name = pyquery.PyQuery(tds[1]).text()
        rating = int(pyquery.PyQuery(tds[2]).text())
        count = int(pyquery.PyQuery(tds[4]).text())
        
        rating_atcoder.append(rating)
        counts.append(count)
    time.sleep(1)

df = pd.DataFrame({
    'rating_atcoder': rating_atcoder,
    'count': counts
})

prop = matplotlib.font_manager.FontProperties(fname=r'C:\Windows\Fonts\meiryo.ttc', size=12)

plt.hist(df[df['rating_atcoder'] < 400].reset_index()['rating_atcoder'], bins=range(0, 4001, 100), histtype='stepfilled', color='#808080')
plt.hist(df[(400 <= df['rating_atcoder']) & (df['rating_atcoder'] < 800)].reset_index()['rating_atcoder'], bins=range(0, 4001, 100), histtype='stepfilled', color='#804000')
plt.hist(df[(800 <= df['rating_atcoder']) & (df['rating_atcoder'] < 1200)].reset_index()['rating_atcoder'], bins=range(0, 4001, 100), histtype='stepfilled', color='#008000')
plt.hist(df[(1200 <= df['rating_atcoder']) & (df['rating_atcoder'] < 1600)].reset_index()['rating_atcoder'], bins=range(0, 4001, 100), histtype='stepfilled', color='#00C0C0')
plt.hist(df[(1600 <= df['rating_atcoder']) & (df['rating_atcoder'] < 2000)].reset_index()['rating_atcoder'], bins=range(0, 4001, 100), histtype='stepfilled', color='#0000FF')
plt.hist(df[(2000 <= df['rating_atcoder']) & (df['rating_atcoder'] < 2400)].reset_index()['rating_atcoder'], bins=range(0, 4001, 100), histtype='stepfilled', color='#C0C000')
plt.hist(df[(2400 <= df['rating_atcoder']) & (df['rating_atcoder'] < 2800)].reset_index()['rating_atcoder'], bins=range(0, 4001, 100), histtype='stepfilled', color='#FF8000')
plt.hist(df[2800 <= df['rating_atcoder']].reset_index()['rating_atcoder'], bins=range(0, 4001, 100), histtype='stepfilled', color='#FF0000')
plt.title(u'AtCoderのレーティングの分布', fontproperties=prop)
plt.xlabel(u'AtCoderのレーティング', fontproperties=prop)
plt.ylabel(u'ユーザー数', fontproperties=prop)
plt.show()

print 'rating: percentile'
for i in [0, 400, 800, 1200, 1600, 2000, 2400, 2800]:
    print '{}: {:.3}'.format(i, 100 - scipy.stats.percentileofscore(df['rating_atcoder'], i))

df = df.dropna()

# 回数ごとの分布も出す

sns.barplot(x=df['count'], y=df['rating_atcoder'], ci='sd')
plt.title(u'AtCoderの参加回数ごとのレーティングの平均と標準偏差', fontproperties=prop)
plt.xlabel(u'AtCoderの参加回数', fontproperties=prop)
plt.ylabel(u'レーティングの平均', fontproperties=prop)
plt.show()

# 箱ひげ図
sns.boxplot(x=df['count'], y=df['rating_atcoder'])
plt.title(u'AtCoderの参加回数ごとのレーティング(箱ひげ図)', fontproperties=prop)
plt.xlabel(u'AtCoderの参加回数', fontproperties=prop)
plt.ylabel(u'レーティング', fontproperties=prop)
plt.show()

print df.groupby('count').mean()['rating_atcoder']

AtCoderのショートコード数で1位になってみた(コードゴルフ)

Competitive Programming (その2) Advent Calendar 2015の4日目の記事です

注意、ショートコーディング(コードゴルフ)のテクニックっぽい話はほとんど載っていません

ちょっと2ヶ月ぐらいAtCoderでショートコーディングをがんばってみました。楽しかったので頭の体操としておすすめです



7月に宇宙ツイッタラーXさんのAtCoder ProblemsAtCoderの問題ごとの最短コードの獲得数のランキングが追加されました

kenkoooo.hatenablog.com

ショートコードを意識して書いているのは10人もいないっぽいので「結構簡単に上位には入れそう」と思って7月から9月の2ヶ月間ぐらいがんばってみました
最初は自分のショートコード数は1問しかなかったけど1位の人でも100問ぐらいしかなくて、1日がんばるだけで1桁順位になれました

AtCoderは他人の提出が見られるので、今の最短の他人のコードをなんとかして1バイト縮めることができれば最短になれます(マナー的にはよくないかも知れませんが(?)
他人のコードを見るのはかなり勉強になるので、最初は「自分でショートコーディングする」「最短あたりの人のコードを見る」「自分のコードを直す」って感じで進めてました
他人のコードを見ると知らないコマンドや関数、文法がたくさん見つかって面白かったです
テクニックをいろいろメモしていたら150行ぐらいになりました
はまってくると数バイト縮めるアイディアを思いつくだけで脳汁が出ます

Beginner ContestのA,B問題などは簡単なので電車通勤の合間にスマホで解くとかできて時間のない社会人にも優しいです(?)

一時はショートコード数140問まで行きました

最近はやってないのとPerlゴルフガチ勢の%20(x20)さんの参戦などで109問に減っています

f:id:sucrose:20151204000655j:plain

というわけでAtCoderで上位に入れない自分でも、ショートコーディングでは簡単に上位に入れたりして楽しかった、という話でした
本気でやれば1桁順位ぐらいはすぐにいけると思うのでぜひ試してみてください

ランキングができてかなりやる気が出たので宇宙ツイッタラーXさんに感謝!
AtCoder Problemsは他にもいろいろ機能がついていて便利です

おもしろいこととか微妙なハマりどころとか

どの言語がよいか

AtCoderではいろんな言語が使えますが、競技プログラミングなので高速な言語でないと解けない問題があり基本C++で解かれているのが多いです。難しい問題だと最短コードの提出でも最初に大量にマクロが定義されていたり短く書こうとしていないのがほとんどなので、改行を消したりするだけで最短が取れると思います
ショートコーディングをちゃんとしているのだとRubyPerlPythonなどやシェルスクリプト(Bash)が強い感じ
自分は普段はPythonをメインで使っているのですが、ショートコーディングの為にRubyを勉強しました
Perlはかなり短くかけて強いのですが、慣れてないのでまだ他人のコードが読めません><

AtCoder Problemsに載ってた最短コードを適当にクロールして言語別にカウントしました

f:id:sucrose:20151204233736p:plain

言語 ショートコード数
C++ 281
Ruby 236
C++11 149
Python 82
Perl 82
Bash 73
C 29
Text 21
Python3 15
Haskell 13
Java 6
Scala 2
C++14 2
PHP 1

クロールに使ったソースコード(pyqueryを入れないと動かない)

# -*- coding: utf-8 -*-

import pyquery
import time
import collections

if __name__ == '__main__':
    link_list = pyquery.PyQuery(url='http://kenkoooo.com/atcoder/')
    for elm in link_list.find('tr td:nth-child(7) a'):
        a = pyquery.PyQuery(elm)
        href = a.attr('href')
        
        if 'http' not in href:
            continue

        solution = pyquery.PyQuery(url=href)
        lang = solution.find('table:eq(0) tr:nth-child(4) td:nth-child(2)')
        print lang.text()
        
        time.sleep(1.5)

1行毎に言語が出力されるのでuniq -cでカウントして一部手で修正

sort temp.txt | awk '$0=$1' | sed '/^$/d' | uniq -c | sort -nr | awk '$0=$2" "$1'

ついでにpyqueryの使い方の記事を昔書いたので貼っておくsucrose.hatenablog.com

見ていて一番おもしろかったコード

AtCoder Regular ContestのD問題を3バイトで通すSubmissionです
Submission #476526 - AtCoder Regular Contest 026 | AtCoder

インタプリタコンパイラのバージョンが古い問題と新しい問題で違う

例えば古い問題だとRuby 1.9、新しい問題だとRuby 2.1なのでたまにコードの動作が違って動かないことがあります

改行が2バイトになるか1バイトになるか


普通にブラウザから提出すると改行が2バイトになる(環境による?)ので手元で改行が1バイトのファイルを作って下記のツールで提出しましたshindolog.hatenablog.com

変な入力

古い問題だと入力の改行などがおかしい場合がある

簡単な問題は正解のチェックがゆるい

A問題などは、題意を満たさないコードでもACになることがあるので、これを利用すると短く書けたりします