年の差ラジオのWOC

https://radio.rcc.jp/toshinosa/

ザ・ギース尾関高文とOCHA NORMA広本瑠璃の年の差ラジオをでゲームをやっていましたが、全く勝てそうになかったのに勝ちました。勝つ確率はどれくらいなのか算出します。

ルールとしては明確ではなかったですが、プレーヤー1は1イニングに1~20のどれかの点数が同確率で入る、プレーヤー2はアウトが1/2、単打が1/3、ホームランが1/6で起こるとします。イニングが終わって10点以上差がついていたらコールドです。

まず、ランダムに1億回プレイしてみましょう。

from __future__ import annotations
from itertools import *
from collections import Counter
import random
import sys
from enum import Enum
from typing import List, Tuple

State = Tuple[int, int, int]

class Action(Enum):
    OUT = 1
    HIT = 2
    HOMERUN = 3
    
    @staticmethod
    def select_randomly():
        r = random.randrange(1, 7)
        if r <= 3:
            return Action.OUT
        elif r <= 5:
            return Action.HIT
        else:
            return Action.HOMERUN

def transit(s: State, a: Action) -> State:
    (score, out, base) = s
    if a == Action.OUT:
        return (score, out + 1, base)
    elif a == Action.HIT:
        if base == 3:
            return (score + 1, out, base)
        else:
            return (score, out, base + 1)
    else:
        return (score + base + 1, out, 0)

def play1_randamly() -> int:
    return random.randrange(1, 21)

def play2_randomly() -> int:
    s = (0, 0, 0)
    while True:
        s = transit(s, Action.select_randomly())
        if s[1] == 3:
            return s[0]

def play_randomly() -> str:
    score1 = 0
    score2 = 0
    for _ in range(9):
        score1 += play1_randamly()
        score2 += play2_randomly()
        if score1 >= score2 + 10:
            return 'Lose'
        elif score2 >= score1 + 10:
            return 'Win'
    
    if score1 > score2:
        return 'Lose'
    elif score1 < score2:
        return 'Win'
    else:
        return 'Draw'

N = int(sys.argv[1])
c = Counter(play_randomly() for _ in range(N))
print(c)
Counter({'Lose': 99860253, 'Win': 139604, 'Draw': 143})

勝つ確率は0.14%程度のようです。
次に、真面目に確率計算をします。最初にプレーヤー2が1イニングに何点入るかの確率を求めます。ただし、181点以上入ったら必ず勝つので、181点以上はまとめて181点とします。

from __future__ import annotations
from functools import reduce
from itertools import *
from collections import Counter
import random
import sys
from enum import Enum
from typing import List, Tuple

State = Tuple[int, int, int]

class Action(Enum):
    OUT = 1
    HIT = 2
    HOMERUN = 3
    
    @staticmethod
    def probability(a: Action):
        if a == Action.OUT:
            return 3 / 6
        elif a == Action.HIT:
            return 2 / 6
        else:
            return 1 / 6

def transit(s: State, a: Action) -> State:
    (score, out, base) = s
    if a == Action.OUT:
        return (score, out + 1, base)
    elif a == Action.HIT:
        if base == 3:
            return (score + 1, out, base)
        else:
            return (score, out, base + 1)
    else:
        return (score + base + 1, out, 0)

def probs_inning(N: int) -> list[float]:
    probs: list[float] = [0.0] * (N + 2)
    c: Dict[State, float] = Counter()
    c[(0, 0, 0)] = 1.0
    while c:
        new_c: Dict[State, float] = Counter()
        for s0, p0 in c.items():
            for action in Action:
                s1 = transit(s0, action)
                score, out, base = s1
                p1 = p0 * Action.probability(action)
                if out == 3:
                    probs[score] += p1
                elif score > N:
                    pass
                else:
                    new_c[s1] += p1
        c = new_c
    print(1.0 - sum(probs[:11]))    # 11点以上取る確率
    probs[N+1] = probs[N]   # 半分ずつ減っていくのでたぶんこれくらい
    return probs

def decide_probs():
    probs1 = [0.0] + [1/20] * 20
    probs2 = probs_inning(180)  # 181点上なら必ず勝つ
    
    probs = [0.0] * 3   # [Lose, Win, Draw]
    scores = { (0, 0): 1.0 }
    for i in range(9):
        new_scores: Dict[tuple[int, int], float] = Counter()
        for (score1, score2), p0 in scores.items():
            for s1, s2 in product(range(1, 21), range(182)):
                prob1 = probs1[s1]
                prob2 = probs2[s2]
                new_score1 = score1 + s1
                new_score2 = score2 + s2
                p = p0 * prob1 * prob2
                if new_score1 >= new_score2 + 10:
                    probs[0] += p   # Lose
                elif new_score2 >= new_score1 + 10:
                    probs[1] += p   # Win
                else:
                    new_scores[(new_score1, new_score2)] += p
        scores = new_scores
    
    for (score1, score2), p in scores.items():
        if score1 > score2:
            probs[0] += p   # Lose
        elif score1 < score2:
            probs[1] += p   # Win
        else:
            probs[2] += p   # Draw
    
    print("Lose: %.6f, Win: %.6f, Draw: %.2e" % tuple(probs))

decide_probs()

まず、プレーヤー2が1イニングに11点以上取る確率は、0.6%程度です。
そして、

Lose: 0.998606, Win: 0.001392, Draw: 1.39e-06

ランダムにプレイしたときとほとんど変わらないですね。

ハロプロ楽曲大賞2019集計(3)

第18回ハロプロ楽曲大賞'19

投票者がどのユニットの曲に入れたかを、自分の推しのユニットごとに集計した。
兼任は当分配で。
グラフは、累積の割合。

f:id:aya_momo:20200105154524p:plain

どのユニットも、自ユニットに最も入れているのは変わらないが、その次に入れているユニットかを見てみると、BEYOOOOONDSヲタ以外はBEYOOOOONDSであることがわかる。

順位を表にすると分かりやすい。

f:id:aya_momo:20200105183431p:plain

以下に、全てのデータを示す。

,モーニング娘。,アンジュルム,Juice=Juice,カントリー・ガールズ,こぶしファクトリー,つばきファクトリー,BEYOOOOONDS,others
モーニング娘。,6635.5,1807.75,2677.25,546.5,1317.0,706.25,3858.5,811.25
アンジュルム,220.75,2258.0,543.5,173.5,332.0,122.0,820.25,280.0
Juice=Juice,262.5,409.0,1990.0,118.0,335.5,153.5,757.5,264.0
カントリー・ガールズ,138.25,231.5,177.25,508.0,96.75,29.75,278.0,95.5
こぶしファクトリー,55.5,110.5,182.5,53.0,1262.0,39.5,307.5,59.5
つばきファクトリー,92.5,149.5,254.0,32.0,171.0,443.5,365.5,92.0
BEYOOOOONDS,260.5,319.5,345.75,92.75,298.75,119.25,2861.75,146.75
others,1068.0,1382.5,1384.75,715.5,695.75,247.5,1696.0,2185.0

ハロプロ楽曲大賞2019集計(2)

第18回ハロプロ楽曲大賞'19


前に記事にしたものだが、グラフにしてみた。

推し部門に入れた投票者を投票先の推しのユニットの推しだとみなして、上位楽曲はどのユニットの推しが投票したのか調べた。兼任メンバーは0.5ずつとした。グラフはポイントの累積割合。

f:id:aya_momo:20200104110432p:plain

1位はJuice=Juiceヲタよりモーニング娘。ヲタの方がポイントを入れている。
モーニング娘。ヲタは全体の41.5%という巨大勢力なので、こういうことも起こる。

ハロプロ楽曲大賞の投票者数の減少は本当にカントリー・ガールズヲタのせいなのか?

第18回ハロプロ楽曲大賞'19

ハロプロ楽曲大賞の投票人数が去年は一昨年に比べて約400人減ったという。推しメン部門では、3905人から3511人になった。
これについて、カントリー・ガールズのヲタが投票しなくなったから、という言説をどこかで見た。これを検証してみる。

まず、一昨年の得票数は、

森戸知沙希 85票
船木結 62票
梁川奈々美 57票
山木梨沙 54票
小関舞 31票
嗣永桃子 29票

と、全体で318票で、400票に満たない。

では、カントリー・ガールズヲタは、他のユニットのヲタより減少しているのだろうか。
一昨年と去年で同じIDの投票者を追いかければ分かるかと思ったが、実は同じ投票者でもIDは変わるらしい。そこで、ニックネームで追いかけることにした。
では、ニックネームはどれくらい重複するだろうか。

一昨年は、ニックネームは3610あり、ユニークな(一度しか現れない)ニックネームは3405。去年は、ニックネームは3287あり、ユニークなニックネームは3129。

そして、一昨年のユニークなニックネームの投票者3405人が去年どうしたかを調べた。

全体では、1729人が投票、1676人が不明(ニックネームを変えているかもしれないので)。

では、カントリー・ガールズヲタの投票者はどうしたのか調べた。

名前 投票 不明
梁川奈々美 30 21
小関舞 8 18
嗣永桃子 13 12
森戸知沙希 36 34
山木梨沙 31 22
船木結 23 31
合計 141 138

全体の投票・不明の割合と変わらない。カントリー・ガールズヲタが投票しなかったという証拠は見つからなかった。

ついでに、各ヲタが推し変したかを調べた。

梁川奈々美 -> 梁川奈々美 9 小林萌花 3 山木梨沙 3 西田汐里 2 その他 13
小関舞 -> 小関舞 6 その他 2
嗣永桃子 -> 嗣永桃子 8 その他 5
船木結 -> 船木結 16 江口紗耶 2 その他 5
森戸知沙希 -> 森戸知沙希 29 野村みな美 2 その他 5
山木梨沙 -> 山木梨沙 26 その他 5

ちなみに、一昨年上位は、

佐藤優樹 -> 佐藤優樹 134 その他 26
小田さくら -> 小田さくら 57 その他 9
宮本佳林 -> 宮本佳林 65 その他 14
加賀楓 -> 加賀楓 74 その他 13
和田彩花 -> 和田彩花 19 中西香菜 4 上國料萌衣 4 その他 27

ニックネームで追うのもそんなに悪くないことが分かる。

ハロプロ楽曲大賞2019集計(1)

第18回ハロプロ楽曲大賞'19


上位の曲に、どのユニットのヲタが投票したかをポイントで集計してみた。
兼任の3人は、等分。

song モーニング娘。'19 アンジュルム Juice=Juice カントリー・ガールズ こぶしファクトリー つばきファクトリー BEYOOOOONDS others
「ひとりで生きられそう」って それってねえ、褒めているの? 1266.0 343.5 1066.5 84.0 126.0 136.0 158.0 401.5
「ひとりで生きられそう」って それってねえ、褒めているの? (New Vocal Ver.) 66.75 26.75 39.5 5.0 11.5 18.0 17.0 15.0
ニッポンノD・N・A! 805.75 260.0 231.0 87.25 82.0 108.5 391.0 268.0
眼鏡の男の子 673.0 211.5 158.0 45.5 60.0 58.0 481.0 187.0
青春Night 1296.5 89.5 89.0 60.0 16.0 35.0 73.0 147.0
赤いイヤホン 378.75 641.25 133.0 64.0 45.0 46.0 62.5 208.0
都営大江戸線六本木駅で抱きしめて 480.0 147.25 120.0 75.75 75.5 94.0 312.0 200.0
人生Blues 1033.0 56.5 85.0 34.0 18.0 24.5 51.5 127.5
I surrender 愛されど愛 873.0 37.75 32.0 12.25 10.0 14.0 34.5 87.5
恋してみたくて 840.25 28.0 34.5 28.75 8.0 12.0 20.5 97.0
微炭酸 240.5 80.5 342.5 35.0 17.0 59.5 29.0 117.0
消せやしないキモチ 178.75 66.25 83.0 31.0 279.5 31.5 54.5 124.5
人生、すなわちパンタ・レイ 153.25 419.0 49.5 39.75 10.0 21.5 33.5 107.0
全然起き上がれないSUNDAY 225.5 291.5 64.5 40.5 11.5 19.0 55.5 78.5
ふわり、恋時計 259.0 68.75 71.5 14.75 15.0 203.5 52.0 79.0
One Summer Night ~真夏の決心~ 161.75 82.75 58.5 190.0 22.5 16.0 31.5 82.0
ハルウララ 179.25 69.0 56.0 16.25 164.0 47.5 41.5 64.0
三回目のデート神話 195.0 51.75 73.0 13.75 19.5 194.0 31.5 39.0
元年バンジージャンプ 117.25 27.75 68.0 21.5 16.5 14.5 212.0 66.0
弱気女子退部届 126.25 54.5 34.5 152.25 16.5 15.0 27.5 103.5

カントリー・ガールズのブログ更新時間帯

新メンバーが書き始めた1/2以降で調べてみた。

21時台、22時台で72%を占めている。
この前後をもう少し詳しく見てみると、

ほぼ、23:30までチェックすればいいらしい。