婚活のマッチング期待値をモンテカルロで計算する

# はじめに
男性と女性１０組ずつ、
第３希望まで入力したときにマッチング期待値は何組になるのだろう？

婚活に参加してサンプルを取得したいところですが、
コロナ禍のこのご時世の中そんなことをすると、
データと引き換えに大切なものを失いかねませんので、
仕方なくモンテカルロさんの力をお借りすることに。

# 実装
```Python:main.py
# -*- coding: utf-8 -*-
import random
import copy

# 相手を選択する。
# scrtmp:immutable、選択リスト
# target:ターゲット数
# sel:希望順位
# weight:重み
def voteTarget(scrtmp,target):
  # sel数まで選んだら終わり
  if scrtmp.count(1)==sel:
    return True
  #0~target-1 までをランダム
  selectnum=int(random.random() * target)
  scrtmp[selectnum]|=1
  # 再帰処理
  voteTarget(scrtmp,target)

# マッチングメイン
def matchingMain():
  # 初期化
  score = list(range(0))
  msel = list(range(0))
  fsel = list(range(0))
  total = 0
  
  # 男性の選択表作成
  for i in range(x):
    female = list(range(0))
    for j in range(y):
      female.append(0)
    msel.append(female)
    
  # 女性の選択表作成
  for i in range(y):
    male = list(range(0))
    for j in range(x):
      male.append(0)
    fsel.append(male)
    
  # マッチングテーブル作成
  score = copy.deepcopy(msel)

  sumvote=0
  # 男性側選択
  for i in range(x):
    voteTarget(msel[i],y)
  # 女性側選択
  for i in range(y):
    voteTarget(fsel[i],x)
  # マッチング結果計算
  for i in range(x):
    for j in range(y):
      score[i][j]=msel[i][j]+fsel[j][i]
  for i in range(x):
      total+=score[i].count(2)
      sumvote+=sum(score[i])
      # print(score[i]) # マッチング表確認
  # print(sumvote) # 投票数確認
  return(total)

# ~~以下メイン処理~~
# 男性人数
# 女性人数
# 希望順位
# パーティ回数
x=10
y=10
sel=3
party=100
totalmatch=0

for i in range(party):
    totalmatch+=matchingMain()
print(totalmatch/party)

```

# 解説
男性x人と女性y人が第sel希望まで選ぶパーティに、
party回参加してマッチングできる回数の平均を算出する。
その際に、マッチングの組み合わせに乱数を使用していくというすんぽう。

### voteTarget
再帰を使用してsel人選んだら終了。

### matchingMain
メインループ、これ１回で婚活パーティを１回開催したことになる。

# 結果
```
[1, 0, 0, 0, 1, 0, 2, 1, 1, 1]
[1, 1, 1, 1, 0, 1, 0, 1, 1, 0]
[1, 0, 1, 1, 0, 1, 0, 0, 0, 0]
[0, 0, 1, 0, 0, 1, 1, 0, 0, 2]
[1, 0, 2, 2, 1, 0, 1, 0, 0, 1]
[0, 0, 0, 0, 1, 1, 0, 0, 1, 2]
[1, 1, 0, 0, 0, 1, 0, 1, 0, 1]
[0, 0, 0, 2, 0, 1, 1, 0, 1, 1]
[1, 1, 1, 0, 0, 2, 1, 0, 1, 0]
[0, 0, 0, 1, 2, 0, 2, 1, 0, 0]
```
という感じでマッチング結果が作成され、
今回のマッチング数はなんと９組！意外とマッチングするもんですね…！

# 考察
## １０組 で１０００回婚活パーティに参加したとき
|希望数  |平均マッチング数  |
|---|---|
|3  |9.061  |
|4  |15.978  |
|5  |24.998  |
|10  |100.0  |
という感じで、希望数の２乗の近似値になる可能性が高いことがわかってきました。

## 男女の人数が変化した場合は…？
実は男女の人数が変化したとしても、
マッチング数は希望数に依存します。
男女比が崩れても同様です。

## 実際は選ばれる人が決まってるからもっと偏るんだろ！？
というのもよく聞くもっともな意見なので、
試しに２０人組、希望数は５人まで、上位3割にだけ投票が偏るようにプログラムを書き換えると…。

```Python
weight = 0.1~1.0
if x * weight < sel:
  weight = 1
elif y * weight < sel:
  weight = 1
elif weight > 1:
  weight = 1

selectnum=int(random.random() * target * weight) 
```
乱数発生部分を上記のように書き換えればOKです。
どこかしらで値チェックしないと誰ともマッチングしないままパーティが終わるので気をつけてください。

```
# 投票結果
[2, 0, 2, 2, 2, 2, 1, 1, 1, 1, 0, 1, 1, 0, 1, 1, 1, 0, 1, 1]
[1, 1, 2, 2, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0]
[2, 2, 1, 2, 2, 2, 1, 1, 0, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1]
[2, 2, 2, 2, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]
[2, 1, 2, 2, 2, 2, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 0, 1, 0, 1]
[2, 2, 1, 1, 2, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]
[1, 1, 1, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
[1, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
[1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
[1, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
[1, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
[1, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
[1, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
[1, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
[1, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
[1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
[1, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
[1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
[1, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
[1, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
# マッチング数
25.0
```
ということで、結局マッチング数は変わらずに上位3割はマッチング率１００％、
その他の方々は不発という結果になりました。悲しいですね。

## マッチング確率
重複が存在するマッチング確率は

>sel/y ×　sel/x ×　y

です。
参加人数とマッチング数がわかった時点でなんとなく予測はつくと思いますが、
どうしても成功率を知りたい人はここら辺を変更して配列ごとの値の有無で加算して平均をとってみてください。
面白い結果がわかるかもしれません。

```Python
  for i in range(x):
      total+=score[i].count(2)
      sumvote+=sum(score[i])
      # print(score[i]) # マッチング表確認
```
# 所感
マッチング数（率）は地獄のようになるのかな？と思ってましたが、
意外と良心的　※ただし
と言うことがわかりました。
その後に繋がるかはわかりませんが、参加人数：希望数で、
その婚活パーティーの期待値と言うか良心と言うか、
そこら辺の事情も読み取れる気がしてきました。