인간 사회에는 다양한 그룹이 존재한다. 이 글에서는 선택자 그룹과 허락자 그룹이 있다고 가정하고, 각각의 그룹의 특성과, 상호작용의 특성, 그리고 그 집단에 속한 개인의 최선의 전략 등에 대해 생각해보기로 한다.
이러한 예시로 가령, 특정 대학교 내에 속한 남성과 여성의 그룹의 연애와 관련된 상호작용을 생각할 수 있을 것이다. 혹은 기업에 지원한 지원자의 그룹과 기업의 상호작용을 생각할 수 있을 것이다. 이때 한 그룹은 선택자의 위치를 맡는다. 즉, 상대 집단의 특정 개인 (원소)에 대해 ‘관심’을 표출하게 된다. 그러면 선택된 피선택자는 이를 ‘허락’하거나 ‘거부’할 권리가 있다.
이 선택자-허락자 게임 문제를 좀 더 형식화해보자.
어떤 유한 집합 X와 Y가 있다고 하자. 각 집합의 크기는 N, M이다. 어떤 x in X는 어떤 y in Y를 “선택”할 수 있다고 하자. 이때 y는 x에 의해 “선택 받음”이라고 하며, 이 선택을 “허락”하거나 “거절”할 수 있다. 만일 “허락”한다면 (x, y) 쌍을 matched라고 한다.
임의의 x의 선택 횟수를 s (select)라고 하자. 집합 X의 원소 x들의 평균 선택 횟수를 ms (mean select)라고 하고, 선택 횟수의 표준 편자를 sigs라고 하자. 이때, 이 선택 횟수라는 변수가 Gaussian(ms, sigs)를 따른다고 가정하자.
[가정1] s ~ Gaussian(ms, sigs)
임의의 y의 선택 받음 횟수를 c (chosen)라고 하자. 집합 Y의 원소 y들의 평균 선택 받음 횟수를 mc (mean chosen)라 하고, 선택 받음 횟수의 표준 편차를 sigc라고 하자. 이 선택 받음 횟수 c는 x의 선택 s에 종속되는 변수이다. 분포도 그에 따라 자동으로 결정될 것이다.
또한, 원소 y에 대해 허락 횟수를 alpha라고 하자. 그러면 허락률 a=alpha/c라고 하자. 평균 허락률을 ma, 허락률의 표준 편차를 siga라고 할 때, 이 허락률이라는 변수가 Gaussian(ma, siga)를 따른다고 가정하자.
[가정2] a ~ Gaussian(ma, siga)
즉, 컨트롤 할 수 있는 변수는 s와 alpha이며, c와 b(뒤에 나옴)는 분포가 자동으로 결정된다.
우리의 관심사는 원소 x와 y가 “심리적으로 느끼는” matched 가능성에 관한 난이도를 계산하고 싶은 것이다. 물론 matched 관계는 항상 1대1이기 때문에 이를 기준으로 난이도를 정의한다면 항상 X와 Y집단의 평균 난이도는 같을 것이다. 그러나 이것 이외에도 다른 방식으로 심리적 난이도를 정의할 수 있다. 이 에세이에서는 이 심리적 난이도를 다음과 같이 정의한다.
어떤 x가 느끼는 난이도는 선택 횟수 중 허락 받은 횟수를 통해 계산할 수 있다. x의 선택 s 중, 허락 받은 횟수를 b라고 할 때, 허락률 beta=b/s를 생각할 수 있을 것이다. 이 허락률을 난이도로 정의한다. dx (difficulty of x)=beta=b/s. dx는 0과 1사이의 값이며, 1일 경우는 임의의 y를 선택하면 무조건 허락 받아 matched 된다는 것이다.
[가정3] dx (difficulty of x)=b/s
어떤 y가 느끼는 난이도는 허락과 상관 없다. 왜냐하면 허락을 하느냐 마느냐는 전적으로 y에게 달려있기 때문이다. 허락자 원소 y의 난이도는, 평균적으로 선택 받는 횟수에 비해 자신이 선택 받는 횟수를 통해 계산할 수 있다. 즉, “남보다 얼마나 더 선택 받는가”가 중요하다. y가 느끼는 난이도는 dy (difficulty of y)=mc/c로 정의한다. dy는 양수값을 가지며 dy=0.5이면 남들보다 2배 더 선택 받기 쉽다는 것이다.
[가정4] dy (difficulty of y)=mc/c
그러면 이제, 이 기본적인 가정을 했을 때, N, M, ms, sigs, ma, siga에 따른 dx, dy의 분포 변화를 살펴보자.
위의 그림은 N=1000, M=1000, ms=4, ma=0.4로 가정하였을 때, (10yrs는 특정 기간을 잡아야 한다는 의미이며 실제 10년을 의미하는 것은 아니다) 왼쪽부터 # of dash count of X, # of chosen of Y, # of matched of Y, dash success rate, # of matched of X를 의미한다.
----- # of dash count of X: X에서 select를 한 숫자를 분포로 나타냄
----- # of chosen of Y: Y에서 chosen된 숫자를 분포로 나타냄
----- # of matched of Y: Y에서 최종적으로 accept/reject을 거쳐 matched된 숫자를 분포로 나타냄
----- success rate of X: X에서 최종적으로 select 대비 accept된 비율을 분포로 나타냄
----- # of matched of X: X에서 최종적으로 matched된 숫자를 분포로 나타냄
분포를 보면 column1과 column2가 비슷한 형태이다. 이는 X의 select가 곧장 Y의 chosen을 결정하기 때문이다. 어떻게 보면 Y집단의 난이도를 결정하는 chosen은 상대 집단인 X에 의해 결정된다고 할 수 있다.
또한, column4는 Y의 accept의 직접적인 영향을 받는다. 따라서 어떻게 보면 X 집단의 난이도를 결정하는 success rate은 상대 집단인 Y에 의해 결정된다고 할 수 있다.
그러면 변수를 바꿔가며 분포 변화를 관찰해보자.
우선, dash count는 9로 고정한 채, acceptance rate을 0부터 1까지 바꿔가면서 실험해보았다.
이를 볼때, column2는 큰 변화가 없다. column2의 분산은 Y집단의 난이도를 결정하는 중요한 분포인데, 변화가 없다는 것이다. 즉, Y집단의 평균 허락률은 Y집단의 난이도와 별 관련이 없다. 반면 column4는 큰 변화를 보인다. acceptance rate이 작을 때는, success rate이 0인 것이 많다. 그러나 acceptance rate이 올라감에 따라 급격이 증가함을 알 수 있다. 즉 Y집단의 평균 허락률은 X집단의 난이도와 일맥 상통한다. 그렇다면 Y집단의 평균 허락률은 어떻게 결정될까?
다음으로 acceptance rate을 0.4로 고정한 채, dash count를 0에서 10까지 바꿔가면서 실험해 보았다.
이를 토대로 볼 때, 평균 select가 낮을 수록 column2의 평균이 0에 가깝다. 즉, dash가 없으므로 한 번도 선택 받지 못한 Y의 원소들이 존재하는 것이다. 평균 선택 횟수가 증가함에 따라, chosen의 평균도 증가한다. 그런데 사실 중요한 것은 분산이다. 어차피 Y집단의 난이도는 relative하게 정의했기 때문이다. 따라서, Y집단의 난이도는 X집단의 select의 표준 편차 (아래서 얘기하는 dash function의 분산)과 일맥상통한다고 할 수 있다. (그렇다면 X집단의 select의 표준 편차는 어떻게 결정될까?) 반면 column4에서 success rate의 평균은 0.4~0.5로 거의 유지되는 것을 볼 수 있다.
<에필로그>
이 실험에 사용한 소스 코드는 다음과 같다.
----------------------
#-*- coding:utf-8 -*-
import numpy as np
import random
import matplotlib.pyplot as plt
import ipdb
def DashFunction(x, Y):
# Given x, return an element of Y
# x hits on 'attractive' elements of Y
return random.sample(Y, 1)[0]
def AllowFunction(y, ma, siga, x):
# Given x, y, return 0 or 1
# y accepts 'attractive' elements of X
a = np.random.normal(ma, siga, 1)[0]
if a >= 0.5:
return 1
else:
return 0
class Selector():
def __init__(self, id, ms, sigs):
self.select = []
self.rank = 0.5
self.count = 0
self.accepted = []
self.ms = ms
self.sigs = sigs
self.id = id
def dash(self, Y):
count = int(np.random.normal(self.ms, self.sigs))
self.count = count
while count > 0:
y = DashFunction(self, Y)
y.chosen.append(self)
self.select.append(y)
count -= 1
def calc_dx(self):
try: self.dx = 1.0*len(self.accepted)/len(self.select)
except: self.dx = -1.0
def print_info(self):
print("id: %d, count: %d, rank: %f" % (self.id, self.count, self.rank))
for i in range(len(self.select)):
if self.select[i] in self.accepted:
print("%d<->%d (matched)" % (self.id, self.select[i].id))
else:
print("%d->%d" % (self.id, self.select[i].id))
try:
print("dx: %f" % (self.dx))
except:
pass
class Allower():
def __init__(self, id, ma, siga):
self.chosen = []
self.rank = 0.5
self.accept = []
self.ma = ma
self.siga = siga
self.id = id
def allow(self):
for x in self.chosen:
a = AllowFunction(self, self.ma, self.siga, x)
if a == 1:
x.accepted.append(self)
self.accept.append(x)
def calc_dy(self, mc):
self.dy = 1.0*len(self.chosen)/mc
return self.dy
def experiment(N,M,ms,sigs,ma,siga):
# ----------------------------
# Define X
X = []
for i in range(N):
x = Selector(i, ms, sigs)
X.append(x)
# Define Y
Y = []
for j in range(M):
y = Allower(j, ma, siga)
Y.append(y)
# Dash from X to Y
SELECT = []
for i, x in enumerate(X):
#if i % 10 == 0: print(i)
x.dash(Y)
SELECT.append(len(x.select))
# Y: allow or reject
for j, y in enumerate(Y):
#if j % 10 == 0: print(j)
y.allow()
CHOSEN = []
ACCEPT = []
for x in X: x.calc_dx()
for y in Y:
CHOSEN.append(len(y.chosen))
ACCEPT.append(len(y.accept))
mc = 1.0*len(CHOSEN)/len(Y)
for y in Y: y.calc_dy(mc)
# accepted of x
ACCEPTED = []
ACCEPTED_CNT = []
for x in X:
if len(x.select) == 0: pass
else: ACCEPTED.append(1.0*len(x.accepted)/len(x.select))
ACCEPTED_CNT.append(len(x.accepted))
# --------------------------
# calculate various metrics
# --------------------------
# mean difficulty of x
mdx = 0
for x in X: mdx += x.dx
mdx = 1.0*mdx/len(X)
# mean difficulty of y
mdy = 0
for y in Y: mdy += y.dy
mdy = 1.0*mdy/len(Y)
fig = plt.figure()
st = fig.suptitle("# of male = # of female = 1000 \n AVG dash of male = %d times (10yrs) \n AVG acceptance rate (F) = %.2f" % (ms, ma))
# plot distribution of select of x
ax1 = fig.add_subplot(151)
ax1.hist(SELECT,align='mid',color='blue',rwidth=0.5,bins=[x-0.5 for x in list(range(0, ms+int(sigs)+4, 1))])
ax1.set_xlabel("# of dash (M)")
axes=plt.gca()
axes.set_ylim([0,N])
# plot distribution of choose of y
plt.subplot(1,5,2)
plt.hist(CHOSEN,align='mid',cumulative=False,color='red',rwidth=0.5,bins=[x-0.5 for x in list(range(0, ms+int(sigs)+4, 1))])
plt.xlabel("# of chosen (F)")
axes=plt.gca()
axes.set_ylim([0,N])
# plot distribution of accept of y
plt.subplot(1,5,3)
plt.hist(ACCEPT,align='mid',color='yellow',rwidth=0.5,bins=[x-0.5 for x in list(range(0, ms+int(sigs)+4, 1))])
plt.xlabel("# of rel (F)")
axes=plt.gca()
axes.set_ylim([0,N])
# plot distribution of accepted of x
plt.subplot(1,5,4)
plt.hist(ACCEPTED,align='mid',cumulative=False,color='green',rwidth=0.3,bins=[-0.05,0.05,0.15,0.25,0.35,0.45,0.55,0.65,0.75,0.85,0.95,1.05])
plt.xlabel("dash success rate (M)")
axes=plt.gca()
axes.set_ylim([0,N])
# plot distribution of accepted_cnt of x
plt.subplot(1,5,5)
plt.hist(ACCEPTED_CNT,align='mid',cumulative=False,color='green',rwidth=0.5,bins=[x-0.5 for x in list(range(0, ms+int(sigs)+4, 1))])
plt.xlabel("# of rel (M)")
axes=plt.gca()
axes.set_ylim([0,N])
#plt.show()
name = "Game/result/%d_%d_%f_%f.png" % (ms,sigs,ma,siga)
plt.savefig(name)
plt.close()
# x selects 1, y accepts all
#experiment(1000,1000, 1,0,1,0)
# x selects 1 with variation, y accepts all
#experiment(1000,1000, 1,0.2,1,0)
for i in range(1,10):
for j in range(0,10):
experiment(1000,1000, i,1.0*i/2,1.0*j/10,0.2)
print("main.py is finished.")
----------------------
핵심 내용은 다음과 같다.
Selector 클래스
Allower 클래스
Dash function(x, Y): 원소 x가 Y집단의 누구를 select하는 가를 결정하는 함수
Allow function(y, x): 원소 y가 어떤 원소 x에 의한 chosen을 accept할지 reject할지 결정하는 함수
- 추후계획-
(1) Total Rank Assumption
추후에는 selector 집단과 allower 집단의 각각의 원소들에 rank가 존재하여, 이 rank에 따라 전 원소를 “줄 세우기” 할 수 있다고 가정한다. 어떻게 보면 이는 “매력 지수”라고 생각할 수 있다. 이 rank에 따라 dash function과 allow function의 형태를 바꾸어 분석을 진행 할 수 있을 것이다. 그랬을 때 분포에 어떤 변화가 나타날 지를 관찰해 볼 것이다.
(2) Social Microenvironment Partition
또한, 현재는 모든 x가 모든 y에 dash할 수 있다고 가정하고 있으나, 실제 사회 환경에서는 “미시 사회 환경”이라는 것이 존재한다. 즉, 특정 대학교에 소속된 사람은 그 대학교 내의 성비 등에 영향을 받을 것이다. 이런 방식으로 집단 X 및 Y를 분할 (partition)하고, 동일한 미시 사회 환경에 속한 원소들끼리만 select 및 allow를 할 수 있다고 가정할 때, 분포의 변화가 어떠할 지도 관찰해 볼 것이다.
일단 이번 글은 끝.