LoginSignup
2
0

More than 3 years have passed since last update.

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

Last updated at Posted at 2020-12-22

はじめに

男性と女性10組ずつ、
第3希望まで入力したときにマッチング期待値は何組になるのだろう?

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

実装

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回で婚活パーティを1回開催したことになる。

結果

[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]

という感じでマッチング結果が作成され、
今回のマッチング数はなんと9組!意外とマッチングするもんですね…!

考察

10組 で1000回婚活パーティに参加したとき

希望数 平均マッチング数
3 9.061
4 15.978
5 24.998
10 100.0

という感じで、希望数の2乗の近似値になる可能性が高いことがわかってきました。

男女の人数が変化した場合は…?

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

実際は選ばれる人が決まってるからもっと偏るんだろ!?

というのもよく聞くもっともな意見なので、
試しに20人組、希望数は5人まで、上位3割にだけ投票が偏るようにプログラムを書き換えると…。

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割はマッチング率100%、
その他の方々は不発という結果になりました。悲しいですね。

マッチング確率

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

sel/y × sel/x × y

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

  for i in range(x):
      total+=score[i].count(2)
      sumvote+=sum(score[i])
      # print(score[i]) # マッチング表確認

所感

マッチング数(率)は地獄のようになるのかな?と思ってましたが、
意外と良心的 ※ただし
と言うことがわかりました。
その後に繋がるかはわかりませんが、参加人数:希望数で、
その婚活パーティーの期待値と言うか良心と言うか、
そこら辺の事情も読み取れる気がしてきました。

2
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
0