立命館大学競技プログラミング合宿参加記

3/5~3/7に立命館大学で開かれていた立命館大学競技プログラミング合宿2019(以下RUPC)に参加してきました。 昨年に引き続き二回目の参加です。

RUPCでは三日間にわたり、それぞれ立命館大学会津大学北海道大学の学生が作成した問題セットを三人一組のチームを組んで解きます。 コーディングに使用できるPCは同時に一台で、ICPCと同じ形式です。

チーム編成は事前にチームを組むかランダムで決めてもらうかのどちらかなのですが、今年はtsutajさんのCompetitive Programming Team Makerが大活躍していました。 チーム戦の醍醐味として同じくらいの実力の人と組んでワイワイ考察するのと、強い人に組んでもらって色々教えを請うの両方がありますが、今回も両方楽しめたので良かったです。

一日目

一日目は寝坊しましたが何とか自己紹介には間に合いました。

この日はランダムでチームを組み、kawabysさんとprdさんでチームrupc_ponta_gori_kusaを組みました。

問題セットはこちらです(https://onlinejudge.u-aizu.ac.jp/beta/room.html#RitsCamp19Day1)

チーム全体では5問を解き、私はB, Cの考察&実装、D問題の考察をしました。

割と好きな問題セットでとても楽しかったです。

B問題

この問題は問題文の通りに実装をやるだけの問題でした。 prdさんがA問題の実装をしている最中にB問題のコードをホワイトボードに下書きし、 prdさんが実装終わった瞬間にコードを書き始めました。

C問題

Bを実装した後D問題を読みましたが、インタラクティブ問題だったので交換してC問題を解き始めました(AOJとpythonインタラクティブは相性が悪いらしい) この問題、見つけた人はよく見つけたな!?とびっくりできるので、是非一度問題文を読んでみてください。

私はホワイトボード上で実験しました。(5, 4)に石を置けたら普通のオセロっぽくなるのにと思って一人オセロをしましたがどうしても置けず、置けない場所に規則性があるのでは?と思いました。 4*4の盤面で全列挙すれば法則が見れるだろうと思いホワイトボード上で書き、法則を見つけました。 では法則をどう実装したものか……と悩みましたがprdさんに相談して累積和で書けばいいと言われたので二次元累積和を書いて通しました。

D問題

C問題を通している間にprdさんがE問題を通してくれたので、順位表的にD問題をきっちりと通そうと決めました。 改めてkawabysさんから問題の概要を聞き、グラフの構造はこちらで指定できると知りました。 kawabysさんの方針を元に二進数28でグラフをパッと閃いたのがこの日のハイライトで、kawabysさんにそのまま実装をお願いしてACになりました。

F/G問題

見たけど歯が立ちませんでした……

懇親会

立命館のオードブルは結構豪華で、大学の設備とは思えないぐらいおいしかったです。 懇親会の途中で普段にじさんじのこと呟いてますよね?とこうきさんに話しかけてもらったので、 オタク特有のどこまで喋れるかのジャブをうったところ一期生の初期から統合後の新ライバーまで全部話せることが分かり狂喜乱舞しました。 好きなライバーや伝説の配信、哲学について話しまくってのどが渇いたのでお茶を飲もうとしたとき謎ドーラをやったら通じたので凄くうれしかったです。

二日目

この日も遅刻しました。間違いなく目覚ましアプリが鳴っていなかったせいです。

この日もランダムでチームを組み、tsukasa_diaryさんとtuki_remonさんでrupc_tsunaremonを結成しました。

問題セットはこちらです(https://onlinejudge.u-aizu.ac.jp/beta/room.html#RitsCamp19Day2)

チームでは5完、私はC問題しか解けず激冷えをしました。

戦゛争゛だ゛っ!

C問題

この問題は長さNの電話番号を圧縮した後ボタンを全列挙するのですが、電話番号の圧縮に気づく前の方針を引きずりすぎて面倒な実装をしてしまいました。 何をしたのかというと、真ん中のボタン、上下左右のボタン、斜めのボタンに分けるとそれぞれのグループで列挙に必要な個数を減らせるので、これで計算量を削ろうという方針でした。 斜めのグループでは対角の距離に考慮する必要があるので実装が面倒になり、この時点で落ち着いて引き返すべきだったのですが、無理に実装をして無駄に時間を使いました。

B問題

正解の方針には比較的すぐたどり着いてはいました。ただ、x < aの時にそのままxを出力する部分でMODをとるのを忘れ無限に時間を溶かして結局AC出来ませんでした。 コンテスト後にちゃんとMODを付けたら通ったので本当に辛かったです……

懇親会

しうかつの話がでてきて、やっぱ競プロerは競プロerのいる会社に就職するんだなぁと思いました。 ほかにもきゅうりさんから昔阪大で勉強会やってた時の内容を聞けたので、近いうちに似た内容をやりたいと思います。

三日目

この日はあまり眠れていなかったのですが、ちゃんと目覚まし時計(物理)を用意したのでちゃんと起きられました。えらい。

最終日は元からチームを組んでいたので、ixmelさんとprdさんでチームを組みました。

ixmelさんに介護してもらいながらprdさんとD問題までを解き、最終的にチームで5完でした。

私はA, C, Dを解きました。

A問題

やるだけでしたが、最初に書いたコードは出力の形式がガバでした。 サンプルが丁寧だったので無事通せました。ありがとう

C問題

1012までの数字の素因数は106までの素数を確認すればチェックできるのですが、この際106より大きい素因数を忘れていたのをixmelさんから指摘されました。 素因数分解でここを忘れがちだから気を付けることを覚えたので、今後はもう大丈夫だと思います。

D問題

コストを前計算して二分探索でクエリを消化していく系の問題で、方針は見えやすかったです。 ただ、コードを書く際変数のつけ方がぐちゃぐちゃでごっちゃにしてしまったので反省です。

E問題

ローリングハッシュを勘違いして覚えていました。結局考察から実装までixmelさんにやってもらいましたが、これはちゃんと復習してろりは自分の物にしたいと思います。

三日間を振り返って

三大学の皆さんには毎回素敵なイベントをを運営していただいて本当に感謝しています。 また、一緒にチームを組んだり戦った他の競プロerの人にも感謝です。

一年前に参加した時は典型の知識がなくボロボロだったのですが、一年競プロをやっていた成長を感じました。 ただ、まだ後ろの方の問題が解説の用語の意味も分からず、フローとか聞くと思考停止してしまうので、蟻本中級編以降の知識もちゃんとつけていこうと思いました。

Cyclic Voltammetryのデータをグラフ化前に前処理

二、三週間くらい前に作ったものですが、
研究室で使うポテンショスタットの機械が吐き出すデータをグラフにするまでの操作が面倒だったので、前処理の部分を自動化しました。

pyinstallerでexe化することを前提に作ったので、pandasとかは使わずにインポートするモジュールのライブラリは極力減らしました。

読み込むテキストデータは例えば

0.000, -7.641e-006
0.001, -7.549e-006
0.002, -7.477e-006
~~~~~~~~~~~~~~~~~~
0.498, +2.116e-006
0.499, +2.087e-006
0.500, +1.906e-006
0.499, +1.823e-006
0.498, +1.776e-006
~~~~~~~~~~~~~~~~~~
0.002, -5.564e-006
0.001, -5.524e-006
0.000, -5.341e-006
0.001, -5.261e-006
0.002, -5.216e-006

みたいな感じで一番左の値が行ったり来たりを繰り返しますが
グラフにするのは最後の一周分で、右の値もアンペアからマイクロアンペアに直します

一回の実験で複数のデータを取って重ね書きすることが多いので、それらのデータを並べ、
かつコピペでテーブルに貼れると便利です

f:id:Py2k4:20161203125048p:plain

こんな感じにフォルダとアプリケーションを配置します(なければアプリで勝手に作ります)
f:id:Py2k4:20161203125319p:plain
inputフォルダの中に処理したいデータを入れ
CVcollecter.exeをダブルクリックで起動
一秒くらいで処理が終わってoutputフォルダ内に結果とプロパティーが出ます
f:id:Py2k4:20161203125512p:plain

結果をカレイダグラフやエクセルに全選択、コピペで貼り付けましょう
僕は事前にこのアプリで前処理した後、pandasとseabornでグラフ化してます

ソースコードはこちら

# -*- coding: utf-8 -*-
import os
import traceback

data = {}  # {pathname1:Data1, pathname2:Data2, ……]}
v_set = set()
data_keys = []


class Data:
    """
    cv_reader()の使用を推奨
    cv_readerで読み取ったデータとその詳細を格納する

    self.va: (V, μA)のタプルのリスト、ただし折り返し地点のみ同じデータを二個持つ
    self.potential: dataの持つVの値を集めた集合
    self.v_step: vの刻み値
    self.v_segment: 1segmentあたりの大きさ
    """
    def __init__(self, read):
        self.va = read
        self.potential = set([row[0] for row in self.va])  # row[col]
        self.v_step = self.va[1][0] - self.va[0][0]
        self.segment = int((max(self.potential) - min(self.potential)) / self.v_step)
        self.count = 0

    def get(self, v):
        if v in self.potential and self.count < len(self.va):
            if v != self.va[self.count][0]:
                print(v, end=":")
                print(self.va[self.count][0])
                return ""

            else:
                self.count += 1
                return self.va[self.count - 1][1]
        else:
            return ""


def cv_reader(pathname):
    """
    CVの機械から持ち出したデータを(Potential(V), Current(μA))のリストに変換する
    read[row][col]
    readを図にするとこんな感じ↓
      V |μA
     ---|--
     0  |0.123
     0.1|0.123
    """
    with open("./input/"+pathname, "r") as f:
        lines = []
        for line in f:
            lines.append(line)

    data_mat = [[float(values.strip(" ")) for values in line.split(",")]
                    for line in lines]
    potential = set([row[0] for row in data_mat])  # row[col]
    maxV = max(potential)
    minV = min(potential)
    stepV = data_mat[1][0] - data_mat[0][0]
    segment = int((maxV - minV) / stepV)
    read = [(round(data_mat[i][0], 3), round(data_mat[i][1]*10**6, 3))
                for i in range(len(data_mat) - 2 * segment, len(data_mat))]
    return read


if __name__ == "__main__":
    if not os.path.isdir('input'):
        os.mkdir("input")
    if not os.path.isdir('output'):
        os.mkdir("output")
    path_list = [path for path in os.listdir("./input") if ".txt" in path]

    description = "データの説明 値はμAに変換済み\n1列目 電圧\n"
    description_counter = 2
    for pathname in path_list:  # テキストを読み込んでDataに変換
        try:
            print(pathname)
            value = Data(cv_reader(pathname))
            key = pathname.split(".txt")[0]
            data_keys.append(key)
            data[key] = value
            v_set.update(data[key].potential)

            description += str(description_counter) + "列目 " + key + "\n"
            description_counter += 1
        except:
            print(traceback.format_exc())
            print(pathname+"は対応していない形式です")

    scan_v = (sorted([v for v in v_set]) +
              sorted([v for v in v_set], reverse=True))

    newtext = ""
    description += ("出力範囲: " +
                    str(scan_v[0]) + "~" +
                    str(scan_v[len(v_set) - 1]) + "\n")

    for v in scan_v:
        newtext += str(v)
        for data_key in data_keys:
            newtext += "\t" + str(data[data_key].get(v))
        newtext += "\n"

    with open("./output/result.txt", "w", encoding="shift_jis") as f:
        f.write(newtext)
    with open("./output/property.txt", "w", encoding="shift_jis") as f:
        f.write(description)

追記
exe化はpyinstallerを使ってます
pipでpyinstallerをインストールして

pyinstaller --onefile ファイル名

バイナリ化できます

もうちょっとくわしく「あああ」の分析

前回の内容をもうちょっと詳しく分析
逃げ恥の「あああ」絶叫ツイートの「あ」を数える(2016/11/22 22:48のみ) - Py2k4の日記
まずは基礎統計量を見てみる
f:id:Py2k4:20161123222940p:plain
時間ごとのツイート数の推移がこれ
f:id:Py2k4:20161123222548p:plain
「あ」の文字数と時間の相関関係を見る
f:id:Py2k4:20161124005523p:plain
もうちょっとy軸を絞る
f:id:Py2k4:20161124005605p:plain

「あ」の入力数に応じてツイートするのが遅れていそう?
もうちょっと詳しく相関分析をして、LOWESS平滑化を行ってみました
f:id:Py2k4:20161124012923p:plain
ツイートの文字数で同じ分析を行ったのがこれ
f:id:Py2k4:20161124013133p:plain
ピークにズレがあります

これは「あああ」を短く連打してすぐにツイートする派と感想を詳しく述べる派の違いですかね?
もうちょっと範囲を広げたり最適なモデルを考えたりしたいです

逃げ恥の「あああ」絶叫ツイートの「あ」を数える(2016/11/22 22:48のみ)

はじめまして


 

僕自身は逃げ恥を見ていないのですが、twitter上で

11/22 22:48の「あ」の数を数えるプログラムを教えて

というツイートを見かけたので、データサイエンスの練習にと試しに調べてみました。

 

まず初めにpythonのモジュールのtweepyを用いて

日本の、「あああ」を含むツイートを、ある程度日時を絞って17400ほど取得しました。

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

import tweepy
import datetime
import sys
from pandas import DataFrame
import pandas

#twitterの設定
CONSUMER_KEY = ""
CONSUMER_SECRET = ""

auth = tweepy.OAuthHandler(CONSUMER_KEY, CONSUMER_SECRET)

ACCESS_TOKEN = ""
ACCESS_SECRET =""

auth.set_access_token(ACCESS_TOKEN, ACCESS_SECRET)
non_bmp_map = dict.fromkeys(range(0x10000, sys.maxunicode + 1), 0xfffd)
# text.translate(non_bmp_map)

api = tweepy.API(auth)

tweets = []
count = 0
for status in tweepy.Cursor(api.search, locale="ja", count = 100, q="あああ", max_id=801060320181506048).items():
    status.created_at += datetime.timedelta(hours=9)
    tweets.append((status.created_at, status.text, status.id))
    count+=1
    if count%100 == 0:
        print(count)

twframe = DataFrame(tweets) 
twframe.to_csv("aaatweets")

こうして抽出したデータをjupyter notebook上でpandasを使って操作して

import datetime

import numpy as np
import pandas as pd
import matplotlib as plt
import seaborn as sns

from pandas import DataFrame, Series

twframe = pd.read_csv("aaatweets2.csv")

twframe = twframe[[1, 2]]
twframe.columns = ["datetime", "text"]

# それぞれの行のあの数を数えた列を追加
def a_count(text):  
    count = str(text).count("あ")
    return count
twframe["count"] = twframe["text"].apply(a_count)

# datetime列がstr型なのでdatetime型に変換
def datecomverter(text):  
    try:
        result = datetime.datetime.strptime(text, "%Y-%m-%d %H:%M:%S")
        return result    
    except:
        return np.nan    
twframe["datetime"] = twframe["datetime"].apply(datecomverter)

# 22:48分のツイートのみを抽出
twframe1 = twframe.dropna(how = "all")
twframe1 = twframe1[datetime.datetime(2016, 11, 22, 22, 49, 00) > twframe1["datetime"]]
twframe1 = twframe1[twframe1["datetime"] >= datetime.datetime(2016, 11, 22, 22, 48, 0)]

これで準備完了です
もう少し操作した後ですが、twframe1の中身はこうなっています
f:id:Py2k4:20161123201118p:plain
twframe1.info()で詳細を見てみましょう

<class 'pandas.core.frame.DataFrame'>
Int64Index: 1408 entries, 1656 to 3063
Data columns (total 3 columns):
datetime    1408 non-null datetime64[ns]
text        1408 non-null object
count       1408 non-null int64
dtypes: datetime64[ns](1), int64(1), object(1)
memory usage: 44.0+ KB

これにtwframe1.sum()を適応するだけで

count    19579
dtype: int64

約20000の「あ」が一分間にtwitter上で叫ばれていたことが分かりました
TL上を見る限り「あ~~~~」とか「あぁぁぁぁぁぁ」とかもありましたがそれはカウントに入れていません
鍵垢も表示されないです

恐るべし逃げ恥……


おまけ:秒ごとに分類してツイート数のカーネル密度推定を行ってみました
f:id:Py2k4:20161123222548p:plain

追記:不等号が>のせいで0秒と59秒を含んでいなかったので修正しました