Реализация
Вот наивная реализация вашей симуляции Монте-Карло.Он не предназначен для обеспечения производительности, вместо этого он позволяет вам перепроверить настройку и увидеть детали:
import collections
import numpy as np
def runMonteCarlo(nw=3, nh=20, nt=4, N=20):
"""
Run Monte Carlo Simulation
"""
def countWomen(c, nt=4):
"""
Count Number of Women per Table
"""
x = np.array(c).reshape(nt, -1).T # Split permutation into tables
return np.sum(x, axis=0) # Sum woman per table
# Initialization:
comp = np.array([1]*nw + [0]*(nh-nw)) # Composition: 1=woman, 0=man
x = [] # Counts of tables without any woman
p = 0 # Probability of there is no woman at table A
for k in range(N):
c = np.random.permutation(comp) # Random permutation, table composition
w = countWomen(c, nt=nt) # Count Woman per table
nc = np.sum(w!=0) # Count how many tables with women
x.append(nt - nc) # Store count of tables without any woman
p += int(w[0]==0) # Is table A empty?
#if k % 100 == 0:
#print(c, w, nc, nt-nc, p)
# Rationalize (count->frequency)
r = collections.Counter(x)
r = {k:r.get(k, 0)/N for k in range(nt+1)}
p /= N
return r, p
Выполнение задания:
for n in [100, 1000, 10000]:
s = runMonteCarlo(N=n)
E = sum([k*v for k,v in s[0].items()])
print('N=%d, P(X=k) = %s, p=%s, E[X]=%s' % (n, *s, E))
Возвраты:
N=100, P(X=k) = {0: 0.0, 1: 0.43, 2: 0.54, 3: 0.03, 4: 0.0}, p=0.38, E[X]=1.6
N=1000, P(X=k) = {0: 0.0, 1: 0.428, 2: 0.543, 3: 0.029, 4: 0.0}, p=0.376, E[X]=1.601
N=10000, P(X=k) = {0: 0.0, 1: 0.442, 2: 0.5235, 3: 0.0345, 4: 0.0}, p=0.4011, E[X]=1.5924999999999998
Построение графика распределения приводит к:
import pandas as pd
axe = pd.DataFrame.from_dict(s[0], orient='index').plot(kind='bar')
axe.set_title("Monte Carlo Simulation")
axe.set_xlabel('Random Variable, $X$')
axe.set_ylabel('Frequency, $F(X=k)$')
axe.grid()
Дивергенция с альтернативной версией
Предостережение: этот метод не отвечает заявленной проблеме!
Если мы реализуем другую версию симуляции, в которой мы меняем способ проведения случайного эксперимента следующим образом:
import random
import collections
def runMonteCarlo2(nw=3, nh=20, nt=4, N=20):
"""
Run Monte Carlo Simulation
"""
def one_experiment(nt, nw):
"""
Table setup (suggested by @Inon Peled)
"""
return set(random.randint(0, nt-1) for _ in range(nw)) # Sample nw times from 0 <= k <= nt-1
c = collections.Counter() # Empty Table counter
p = 0 # Probability of there is no woman at table A
for k in range(N):
exp = one_experiment(nt, nw) # Select table with at least one woman
c.update([nt - len(exp)]) # Update Counter X distribution
p += int(0 not in exp) # There is no woman at table A (table 0)
# Rationalize:
r = {k:c.get(k, 0)/N for k in range(nt+1)}
p /= N
return r, p
Возвращает:
N=100, P(X=k) = {0: 0.0, 1: 0.41, 2: 0.51, 3: 0.08, 4: 0.0}, p=0.4, E[X]=1.67
N=1000, P(X=k) = {0: 0.0, 1: 0.366, 2: 0.577, 3: 0.057, 4: 0.0}, p=0.426, E[X]=1.691
N=1000000, P(X=k) = {0: 0.0, 1: 0.37462, 2: 0.562787, 3: 0.062593, 4: 0.0}, p=0.42231, E[X]=1.687973
Эта вторая версия сходится к другим значениям, и она явно не эквивалентна первой версии, она не отвечает на тот же вопрос.
Обсуждение
Чтобы определить, какая реализация является правильной у меня вычисленные выборочные пространства и вероятности для обеих реализаций.Кажется, что первая версия является правильной, поскольку она учитывает, что вероятность того, что женщина сядет за стол, зависит от того, кто был выбран ранее.Вторая версия не принимает это во внимание, поэтому ей не нужно знать, сколько людей существует и сколько человек может сесть за стол.
Это хорошая проблема, потому что оба ответаобеспечить близкие результаты.Важной частью работы является правильная настройка входов Монте-Карло.