【問題】配列{"ドド","スコ"}からランダムに要素を標準出力し続け、『その並びが「ドドスコスコスコ」を3回繰り返したもの』に一致したときに「ラブ注入♡」と標準出力して終了するプログラムを作成せよ(配点:5点)
— ((🐑++)) (@Sheeeeepla) 2022年8月1日
このプログラムにおいて, 配列{"ドド","スコ"}からランダムに要素を標準出力する回数の期待値を求めました.
また, そのあと「ドド」の出る確率をとしたときの出力回数の期待値を求め,
「ドド」の出る確率を何にしたら期待値を最小にできるかを調べました.
今の状態を定める
もっと一般に, 「ドドスコスコスコ」が回繰り返したものに一致するときに終了するものとします.
「ドド」と「スコ」をそれぞれ「個」として数えます.
「文字列の末尾の個が『ドドスコスコスコ×』の開始個に一致して, どんなに対してもの末尾の個が『ドドスコスコスコ×』の開始個に一致しない」ことを, の状態はであると言うことにします.
ここで, 任意の標準出力した文字列に対して, その状態の値が一意に定まります.
(理由: 「の末尾の個が『ドドスコスコスコ×』の開始個に一致する」という条件を満たすの集合をとすると, は有限集合の部分集合です. さらになのでは空ではなく, よって最大元が存在します. このとき, は状態です.)
状態に対する期待値を定める
を, 状態から終了するまでの出力回数の期待値とします. 文字列が異なっても, 上の状態が同じであれば, その状態から別の状態への遷移(後述)は共通します. なので, 状態が同じであるものをすべて同一視して, を定めることができます.
状態は最後の並びが「ドドスコスコスコ×」となっているので, もう出力することはなくラブを注入することになります. よってとなります.
求めたいのはで, これがドドスコードの出力回数の期待値になります.
を求める
とします.
がで割り切れるとき, 標準出力の末尾は「ドドスコスコスコ…ドドスコスコスコ」のようになっています. の末尾に「ドド」が加わった場合, 状態はに進みます. 末尾に「スコ」が加わった場合, 残念ながら状態はに戻ってしまいます.
がで割り切れないとき, 標準出力の末尾は「ドドスコスコスコ…ドドスコ(スコが足りない)」のようになっています. の末尾に「ドド」が加わった場合, 残念ながら状態はに戻ってしまいます. 末尾に「スコ」が加わった場合, 状態はに進みます.
各について, が満たす式は
です. ここで, の末尾に「ドド」「スコ」が加わる確率は各で, さらに出力回数がされることに注意します.
たとえば, (もとのツイートと同じ)のとき, 次のようになります.
これは元の連立一次方程式になります. 行列を考えれば, 掃き出し法によって解くこともできますが, の値がわかっていれば, 後ろから順番に求めていくことでからまですべて求まります. なので, を文字で置いて仮定することを考えます.
を仮定する
, とおきます. ここで, として数列をおきます.
これをふまえて, は
よって, については
については
については
となります.
これは, 簡単な漸化式になっています! は, 簡単な式で表すことができます(後述). を具体的に求めるまえに, それらでをどうやって解くかを書きます.
が分かったとき
が成立するので,
…①
…②
となります.
②を変形すると, …②'となります.
①を変形すると,
2倍して②'を使うと となり, について解くとがわかります.
なので, 求める期待値はです.
, を求める
先ほどの
式より, をまとめて考えると
となります.
これは簡単に解くことができます. 特性方程式を考えるなどして, が得られます. 数列は等比数列なので, を得ます. よって, です.
を求めてみましょう.
です. 同様により よって となります.
を求める
はがの倍数のときだけに新たにを生み出し, それ以外は後ろの項をに減衰させているというイメージを持つことができます. よって, に注目すると, です.
ここにくると, 同様に より が従います.
なので, です.
ついに が求められるぞ
材料は揃いました. を求めましょう. さきほど, を求めました. これに,
を代入すると,
となり, が導かれました. 思ったより簡単な式になりました.
これにを代入したものが, 目的の期待値になります. その期待値は, です.
なので, プログラムを動かすと, 平均で回ものドドスコを覚悟しなければなりません.
は合同式などを考えることによって, で割り切れることがわかります. よって, がどんな正整数でもドドスコードの出力回数の期待値は整数になります.
以下はの期待値です.
出力回数の分布を調べてないので詳しいことは言えませんが, でドドスコードを実行するのは危険そうです.
偏りを持ったドドスコ
さっきまでは「ドド」「スコ」は均等に出現するときを考えていました. 今度は, 「ドド」と「スコ」で偏りがある場合を考えてみます.「ドド」を出す確率を, 「スコ」を出す確率をとします.
同様に考えると,
は
よって,
となります.
とくに,
であるので, 同様に連立方程式を考えると
より
すなわち,
となります.
さきほどを求めるときに使ったテクニックを使います.
について より です.
よって, です.
について より です.
よって, です.
について より です.
よって, です.
さあ, を求めていきます.
まず,
です.よって,
です. おぞましいです.
期待値が最小となるの値
これ以上は厳しそうなので, Desmosさんに助けてもらいます.
https://www.desmos.com/calculator/fydiaq65et
↑は各に対する期待値の対数をとったものを表示させています. こんなグラフも対応できるDesmosさんはすごいです.
ということで, のときの期待値の最小値は, のとき, すなわち「ドド」が25%, 「スコ」が75%のときの951.751らしいです. 最速でラブを注入したいときはこの比率に設定しましょう.
ちなみに, 実験の結果によると, どんなでものとき最小になりそうです.(証明はできていません) 直感的には0.25周辺になる感じはありますが, 0.25ピッタリというとちょっとピンと来ません.
以下はのでの期待値です.(小数点第2位まで)
こっちはでもいけそうです.
のときの期待値を, のときの期待値をとするとき, の値は以下のようになります.(小数点第2位まで)
が大きいほどの方がいいことがわかります.
で試してみました. 標準出力をいっぱいするのは厳しいので, 擬似的なものになりますが…
import random from collections import deque import datetime print(datetime.datetime.now()) n = 10 a = deque() b = deque() for i in range(4*n): if i % 4 == 0: b.append("ドド") else: b.append("スコ") a.append("") i = 0 while True: a.popleft() if random.randrange(4) == 0: a.append("ドド") else: a.append("スコ") i += 1 if a == b: print(i) break if i % 100000000 == 0: print(datetime.datetime.now(), i) a.append("ラブ注入♡") print(*a,sep="")
なんと期待値の1/20で帰らせてくれました. 今回はやさしかったです.
感想
ぼくは大学の必修科目の課題がまだ残っているにもかかわらず, ドドスコの出力回数の期待値を求めるのに心血を注ぎました は?
でも数字をいじるのはたのしかったです