クソ診断紹介1「ちんぽ揃えゲーム」
この記事は、クソ診断 Advent Calendar 2019 の2日目の記事です。
- 1. はじめに
- 2. 確率について考える
- 3. 実装してみる
- 4. Twitterの診断結果を分析する
- 5. おわりに
- (参考1) 診断メーカーの仕様
- (参考2) 「ちんぽ」が揃う確率の一般項を求める
- (参考3) ソースコード
1. はじめに
ごあいさつ
皆様はじめまして。赤きちと申します。twitterで主に女子中学生をしています。 昨年に引き続き、「クソ診断 Advent Calendar 2019」が作成されたとの事なので、 4回にわたり過去に作成したクソ診断(診断メーカー)を紹介していきたいと思います。
ちんぽ揃えゲームの概要
「ち」「ん」「ぽ」の3種類の文字から等確率に1文字を選び、 左から右に順番に並べる操作を、末尾3文字が「ちんぽ」となるまで繰り返します。 5文字以内に「ちんぽ」が揃うと、何かが起こるそうです(?) shindanmaker.com
本記事の主な内容
2章「確率について考える」 では、「ちんぽ」の文字列が揃うまでの過程から確率漸化式を立て、文字数の確率分布を調べる方法について解説しています。
3章「実装してみる」 では、診断メーカーの制約条件を乗り越えて実装する工夫の過程を綴っています。末尾の「(参考)診断メーカーの仕様」を適宜参照しながら読むと理解が深まるかもしれません。
4章「Twitterの診断結果を分析する」 では、ツイートされた全診断結果(約3000件)を取得し、ツイートが特定の診断結果に偏っていないか統計検定を行った結果が書かれています。
先行プログラムの紹介
2013年には、たろいも氏により「おちんぽ」の文字列が出るまで「お」「ち」「ん」「ぽ」の4文字を表示し続ける「おちんぽ表示プログラム」が作成されています。 musicstd.nobody.jp 正直なところ、「ちんぽ揃えゲーム」は完全に二番煎じなのです…
2. 確率について考える
確率漸化式をつくる
「ちんぽ」が揃うまでの過程を分かりやすくするため、文字列の状態に~という名前を付けておきます。
状態名 | 意味 |
---|---|
末尾3文字が「ちんぽ」 | |
末尾2文字が「ちん」 | |
末尾1文字が「ち」 | |
~のいずれにも当てはまらない |
空の文字列(状態 に相当)に対し、下に示す操作を 回行います。操作終了後に文字列の状態が , , , となる確率を、それぞれ と表すものとします。
「ち」「ん」「ぽ」の3種類の文字から等確率で1文字を選び、文字列の末尾に加える。
ただし、末尾3文字が「ちんぽ」(状態 )の場合は、何も行わない
このとき、ある状態から操作を1回行ったときに各状態に遷移する確率は、 図1 のように表せます。
また、は、以下の漸化式で表すことができます。
この漸化式を用いて、 回以内の操作で「ちんぽ」が揃う確率は 、ちょうど 回の操作で「ちんぽ」が揃う確率は と表すことができます。
文字数の分布をみる
「ちんぽ」が揃ったときの文字数の頻度および累積確率分布を 図2 に示します。 中央値は20文字で、20文字以内に半分以上の確率(51.96%)で「ちんぽ」が揃うことになります。
また、期待値の27文字はちょうど3の3乗と綺麗な数字になりますが、「ちんぽ」以外の並びでは、(直観に反して)揃うまでの文字数の期待値が異なることがあります。3文字の全ての組み合わせ27通りで期待値を平均すると、29文字となります。
文字列パターン | 組み合わせ数 | 期待値 |
---|---|---|
ちんぽ | 6 | 27 |
ちちん | 6 | 27 |
んちち | 6 | 27 |
ちんち | 6 | 30 |
ちちち | 3 | 39 |
3. 実装してみる
状態遷移を再現する
文字列の状態ごとに抽選に使うリストを作成し、次回の抽選に使うリストを再帰的に参照することを考えます。
- 状態 のリスト
抽選結果 | 次回の抽選に使うリスト |
---|---|
ち | 状態 |
ん | 状態 |
ぽ | 状態 |
- 状態 のリスト (末尾1文字が「ち」)
抽選結果 | 次回の抽選に使うリスト |
---|---|
ち | 状態 |
ん | 状態 |
ぽ | 状態 |
- 状態 のリスト (末尾2文字が「ちん」)
抽選結果 | 次回の抽選に使うリスト |
---|---|
ち | 状態 |
ん | 状態 |
ぽ | なし(完成!) |
上の表を見ると、抽選結果次第では、次回の抽選に使うリストが今回と同一になる事がわかります。
しかし、診断メーカーの仕様上、リストからは「異なるリスト」の抽選結果しか参照する事ができません。
ここで、以下のように抽選回数の奇偶によりリストを分ける工夫を行います。こうすることにより、同一の状態に遷移する場合でも、次回必ず異なるリストを使用するため、診断メーカー上でも正しくリスト間の移動を行うことが可能になります。
リスト名 | リストを使用する条件 |
---|---|
状態 かつ 奇数回目の抽選 | |
状態 かつ 偶数回目の抽選 | |
状態 かつ 奇数回目の抽選 | |
状態 かつ 偶数回目の抽選 | |
状態 かつ 奇数回目の抽選 | |
状態 かつ 偶数回目の抽選 |
抽選確率の精度を高める
抽選されたリストが再び使われた場合、無限ループに陥り、診断結果が出力されなくなります。そのため、一度抽選されたリストは、次回以降の抽選で使われないように設定する必要があります。
一方で、1度使用したリストの値を削除するとその後の抽選確率が変化してしまいます。この影響を最小限に抑えるためには、
- リストの値の個数を可能な限り増やす
- リストが再帰的に参照される回数を可能な限り減らす
ことが有効です。
今回は、後者の「リストが再帰的に参照される回数を可能な限り減らす」方針で、1回のリスト参照で抽選する文字数を増やしてみましょう。
「ち」「ん」「ぽ」の3種類の文字を一列に 個並べたときの場合の数は、 通りです。 ここで、各リストの値の数は999個以下である必要があり、この制約条件を満たす最大の は6となります。
1回のリスト参照で6文字抽選する場合、抽選前の状態に応じた、抽選後の状態の場合の数は以下のとおりとなります。
抽選後 | 状態 | 状態 | 状態 | 状態 (完成時の文字数) | ||||||
---|---|---|---|---|---|---|---|---|---|---|
6 | 5 | 4 | 3 | 2 | 1 | |||||
抽選前 | 状態 | 331 | 216 | 75 | 26 | 27 | 27 | 27 | 0 | 0 |
状態 | 291 | 190 | 66 | 23 | 24 | 27 | 27 | 81 | 0 | |
状態 | 216 | 141 | 49 | 17 | 18 | 18 | 27 | 0 | 243 |
文字数をカウントする
診断メーカーには、文字数をカウントする関数こそ用意されていないものの、リストから参照された数値の合計を計算する 関数が実装されています。この関数では、実際に結果に表示されなくとも、関数内で使用されたリストの数値は合計対象に含まれます。
そこで、~の文字列値の後ろに、条件に関わらず何も表示しない関数を挿入し、条件文中で文字数と等しい数値が格納されたリストを参照しています。
リスト数には限りがあるため、 および に 6 を、 に 1 をそれぞれ格納し、2文字~5文字の抽選結果については、枝番を変えてを複数回参照することで文字数をカウントさせています。
(例1) 6文字の抽選結果の場合
ちちちちんぽ=IF([LIST8_1]=0,"","")
(例2) 3文字の抽選結果の場合
ちんぽ=IF(=CALC([LIST10_1]+[LIST10_2]+[LIST10_3])=0,"","")
特殊演出を表示させる
少ない文字数で「ちんぽ」を出せた場合に、評価およびアスキーアートを表示させています。
なお、複数行にわたるアスキーアートは診断結果基本テキストに直接書き込むことはできないので、 内にアスキーアートを格納しています。
- 3文字~5文字→ EXCELLENT評価 (出現確率:11.11%)
\( ^ω^)/ EXCELLENT!! \ \ \ γ∩ミ ⊂:: ::⊃) /乂∪彡\
- 6文字~10文字→ GREAT評価 (出現確率:16.48%)
(/^ω^)/ 〇U〇 GREAT!
4. Twitterの診断結果を分析する
診断結果を取得する
作成した診断メーカーが診断結果ツイートを通して広く拡散し、沢山の人に遊ばれると嬉しいものです。診断メーカーのアドレスを検索窓に入力して、個々のツイートや診断結果に続く一言コメントを見るのが趣味の一つになっているといっても良いでしょう。
さて、診断結果ツイートが数百、数千と蓄積していくと、個々の診断結果だけでなく、「気に入った結果のときだけ診断結果をツイートしていないだろうか?」といった疑問に対する統計的な答えを知りたくなることがあります。
このような欲望に応えてくれるAPIが、twitter開発者アカウントを登録すると使用可能になる"Search API Full-Archive"です。これを用いると、Twitterサービス開始以来の全ツイートを対象としたツイートの検索および取得ができます。
なお、このサービスには無料版(Sandbox) と 有料版(Premium) があります。有料版の料金は最低でも月額$99 からと、趣味用途に用いるには高額ですが、無料版でも月間50回まで、1回あたり最大100ツイートの検索および取得が可能です。
サービスの詳細は以下のリンクを参考にしてください。 https://developer.twitter.com/en/account/subscriptions/search-fullarchive
さて今回は、無料版のAPIを用いて、診断作成日(2019/01/02)以降で、「ちんぽ揃えゲーム」のアドレス(https://shindanmaker.com/855159)を含む診断結果を取得してみました。取得したツイート数は下記のとおりで、無料版の範囲内に収まりながらも統計的な考察をするには十分なサンプルが集まったかと思います。
- 取得に要した検索回数: 33回 / 上限50回
- 取得できたツイート数(RTを除く): 3,031ツイート
また、診断メーカーサイト上での診断人数カウントは 10,922人 (2019/11/30 16:00現在) ですので、診断ページにアクセスした人のうち3割弱(27.8%)が診断結果をツイートしていたことも分かりました。
診断結果ツイートの偏りを調べる
この節では、EXCELLENT評価(3文字~5文字) および GREAT評価(6文字~10文字) が出現する確率が、理論的に期待される確率と有意差があるか調べることを目標に、データ処理や検定を行っていきます。
正規表現による検索
次に、正規表現を用いて、ちんぽ揃えに成功したときの文字列を抽出しました。
- 文頭が「ち」「ん」「ぽ」のみで構成された文字列で、その後に左括弧が続くものを検索します。
^[ちんぽ]+\(
- また、最後の1文字でちんぽ揃えに成功した場合として、文頭が「ち」「ん」「ぽ」のみで構成された文字列で、その後に"ちんぽ…"が続くものを検索しました。
^[ちんぽ]+ちんぽ
事後確率の計算
「特殊演出を表示させる」の節で、EXCELLENT評価 と GREAT評価 が出現する確率はそれぞれ 11.11% と 16.48% となる事を紹介しました。しかし、正規表現で抽出したツイート中での各評価の出現確率は、先述した確率よりも高くなります。
これは、「ちんぽ」を揃えるまでにツイートが省略されるほど文字数の多い診断結果は、正規表現にマッチしなくなり、サンプルに含まれなくなるからです。
2019年11月6日以降の診断メーカーのシステムでは、診断結果本文に最大120文字が使用可能です。本診断では、120文字から以下の10文字を除いた110文字が、「ちんぽ」を揃えるためのボーダー文字数になります。
- ハッシュタグ「 #ちんぽ揃えゲーム」(全角9文字分、半角スペース含む)
- 途中省略が発生した場合、省略記号の「…」(全角1文字分)
さて、正規表現で抽出したツイート中での各評価の出現確率は、「110文字以内にちんぽ揃えに成功した」という情報が加わったときの事後確率と考えることができます。110文字以内に「ちんぽ」が揃う確率が98.80%である事を考慮すると、求める確率は以下の通りとなります。
評価 | 事前確率 | 事後確率 |
---|---|---|
EXCELLENT | 11.11% | 11.25% |
GREAT | 16.48% | 16.68% |
ツイートの取捨選択
上記で求めた出現確率は、「110文字以内にちんぽ揃えに成功した」ツイートが母集団であるときに成り立つものです。何らかの理由でちんぽ揃えの成功条件となるボーダー文字数が異なるツイートが含まれる場合、以下のように取捨選択の処理を行いました。
- 成功条件のボーダー文字数が110文字を超えるツイート → 111文字以上でちんぽ揃えに成功したツイートのみを除外
これは、本診断の診断作成日(2019年1月2日)から現在までの間に、診断メーカーの仕様変更(2019年11月6日)が行われたことに起因します。
仕様変更以前は、診断結果に「#shindanmaker」タグが付与されなかったぶん診断結果に使用可能な文字数が現在よりも多く、成功条件のボーダーもその分緩くなっていました。現在のボーダー文字数に基準を合わせるため、111文字以上でちんぽ揃えに成功したツイートは集計の対象外としました。
- 成功条件のボーダー文字数が110文字未満のツイート → 全て除外
失われた分布(実際には110文字以内でちんぽ揃えに成功していたにも関わらず抽出不能なもの)は、どんなに頑張っても復元することはできません。よって、診断結果本文に使用可能な文字数を減少させうる条件が含まれるツイートは、文字数の結果によらず全て集計の対象外としました。
具体的には、文頭に「ち」「ん」「ぽ」以外の文字が含まれるツイートを除外しました。対象となったツイートのほとんどは、他者への@リプライとして診断結果をツイートしたものでした。
以上の手順で、合計2,964件のサンプルを抽出し、ちんぽが揃うまでの文字数を集計しました。結果は図4のとおりです。
二項検定の実施
最後に、「各評価のツイート中の出現率は、診断結果での理論的な出現率と等しい」と帰無仮説を設定し、有意水準 α=0.05 の両側二項検定を行いました。
評価 | 期待値 | 実測値 | Z統計量 | p値 |
---|---|---|---|---|
EXCELLENT | 333.32 | 561 | 13.24 | <0.001 |
GREAT | 494.42 | 526 | 1.56 | 0.060 |
結果として、EXCELLENT評価は、理論的な期待値よりも有意に多く出現している(1.68倍)が、GREAT評価は、理論的な期待値と有意差があるとはいえない事が分かりました。
診断結果に偏りが見られる原因については、
- 良い結果が出た場合のみツイートをしている
- 良い結果が出るまで名前を変えつつ何度も診断してからツイートしている
- 良い結果に改ざんしてツイートをしている
などが考えられます。3番目のような不正行為はやめましょうね!
5. おわりに
本記事では、作成した「ちんぽ揃えゲーム」について、確率的な考察や、実装方法の解説、診断結果の統計分析などを雑多に解説してみました。 私がブログを開設して初めて作った記事という事もあり、想像以上に筆の進みが遅く、まとまった量の文章を書くには普段からの鍛錬が必要であることを痛感させられました。
最後に、診断メーカーを経由せずに「ちんぽ揃えゲーム」をプレイする方法を紹介したいと思います。
echo ちんぽ|grep -o .|shuf -r -n 999|tr -d '\n'|sed 's/ちんぽ.*$/ちんぽ/'|awk '{print $0,"(ボロン\n",length($0),"文字目でちんぽを出せました! #ちんぽ揃えゲーム"}' #シェル芸
— 赤きち (@yryrrrrryryr) 2019年8月5日
シェル芸bot(@minyoruminyon)にフォローバックされる必要がありますが、上記ツイートをコピーしてツイートすると、引用リプライで結果が返ってきます。
シェルスクリプトであれば1行で書けてしまうこのゲームを、診断メーカーで実装しようとすると何十倍もの労力がかかるのは事実です。しかし、何万人あるいは何十万人に診断を楽しんでもらえるのは、抜群に利用者の多い診断メーカーというプラットフォームがあってこその事でしょう。
皆様の診断結果ツイートを励みにして、今後も時間を見つけながら新しい診断メーカーを作っていきたいと思います。
(参考1) 診断メーカーの仕様
診断メーカーの基本データは、「診断結果基本テキスト」と「リスト」で構成されます。
診断結果基本テキスト
- 診断をした際に「診断結果」として表示される文章のベースとなる部分
- 全てのリストから抽選結果を呼び出し可能
- 改行記号を直接的に使用できない
リスト
- 抽選結果の候補となる「値」を格納する部分
- 異なるリストから抽選結果を呼び出し可能
- 改行記号の使用が可能
リストの呼び出し方法
(は省略可能)
記号 | 意味 |
---|---|
リストの番号 | |
枝番 (枝番が異なると、異なる抽選結果が得られる) | |
値の要素番号 (値がカンマで区切られている場合のみ有効) |
制約条件
リスト上の制約
作成可能なリストの上限数は10個です。また、リストごとに以下の制約があります。
対象 | 上限値 |
---|---|
値の数 | 999 |
各値の文字数※1 | 300 |
値の合計バイト数※2 | 65535 |
※1 全角文字、半角文字を問わず1文字として計算
※2 全角文字は3バイト、半角文字は1バイトとして計算
診断結果本文の自動カット
ツイートボタン経由のツイート、および「コピペ用(140文字)」の診断結果では、文字数が長い診断結果の全角換算文字数が139文字以下になるように末尾が省略されます。
- 診断結果本文に使える全角換算文字数は120文字以下 (ツイートの際、#shindanmaker タグおよび診断メーカーへのリンクが自動で付与されるため)
- 120文字を超過した場合、119文字目までが有効、以降は「…」として省略される
- 自分でハッシュタグを設定した場合、その分だけ本文に使える文字数が減る (診断結果の後にタグが付与されるため)
関数の仕様
関数
- リストから参照された数値の合計を計算する
- は、カウント対象とするリストの番号
- について、リスト番号 と枝番 が同一のものが複数回参照された場合、1回のみカウント
(参考2) 「ちんぽ」が揃う確率の一般項を求める
ゲームのルールのシンプルさとは裏腹に、「ちんぽ」が揃う確率の一般項はやや複雑なものとなります。
ここでは、 回以内の操作で「ちんぽ」が揃う確率 の一般項について、簡単な導出過程を含め紹介していきます。
まず、ちょうど 回の操作で「ちんぽ」が揃う必要十分条件は以下の通りです。
- 回目の操作終了時に、まだ「ちんぽ」が揃っていない
- 回目の操作で、「ちんぽ」の 文字が続く
このことは、式のように漸化式で表すことができます。
式を整理し、式のように変形します。式の形は一般に「定数係数線形漸化式」と呼ばれます。
漸化式を満たす具体的な解の つを「特解」、(漸化式の右辺) としたときの一般解を「斉次解」としたとき、一般解は「特解」と「斉次解」の和として求めることができます。
まず、 (定数数列) は、式を満たす解の つであるため、式の特解となります。 次に、斉次解を求めるため、式の右辺を とした式の一般解を求めていきましょう。
式中の を に置き換えた特性方程式を考えます。
なお、式は下表のとおり、異なる3つの実数解 をもちます。
記号 | 値 | 近似値 |
---|---|---|
式の解のうち つを とします。
とおき、式に代入することで、式を得ます。
は 式の解であることから となり、式は の値によらず恒等的に成立します。
について同様のことがいえるため、を任意定数として、式の斉次解は式のように書き表すことができます。
なお、は、式にを代入し連立方程式を解くことで、下表のとおり求まります。
記号 | 値 | 近似値 |
---|---|---|
ここで、 は定数で、 です。
以上より、特解と斉次解を足し合わせた式が求める一般項となります。
(参考3) ソースコード
診断に使用した基本診断テキストおよび各リストをgithubに公開したので、興味のある人はご覧ください。 github.com