@zaoriku0さんから鳥海不二夫先生がされていた発表の話を聞きました。SNSが流行るかどうかの必要条件を、ゲーム理論を使って説明できないか、という趣旨の研究のようです。
社会シミュレーションの先駆者のAxelrodの論文が基になってるようです。
language-and-engineering.hatenablog.jp
作りかけですが、詳しいこと(S_tの算出方法とか)はAxelrodの論文読まないと分からないので、とりあえず作りかけのやつ載せときます(数値検証もできないですし)。動くかどうかも怪しいかも…。
11/5追記:ちょっとプログラム修正しました
import numpy as np from numpy import random as rnd from matplotlib import pyplot as plt # エージェントの数 N = 20 # シミュレーションの試行回数 T = 10000 # 利得を表したリストの初期値は全部0 benefit_list = np.zeros(N) class Agent(object): # グローバル変数として利得を更新する(絶対良くない実装だけど) # global benefit_list T = 3 H = -1 E = -9 P = -2 E_d = -9 P_d = -2 # 協調率B_i B_i = rnd.uniform(0,1) # 裏切り者ユーザーへの懲罰率V_i V_j = rnd.uniform(0,1) # 裏切り者を見逃したユーザーへの懲罰率V_k V_k = rnd.uniform(0,1) def __init__(self): # 初期値では、協力(1)か裏切り(0)を1/2で選ぶ self.strategy = rnd.binomial(n=1, p=0.5) # エージェントの番号をセットする def set_agent_number(self, i): self.num = i # 裏切るか協力するか決断する def make_strategy(self): # 協力する if rnd.uniform(0,1) < self.B_i: self.strategy = 1 #自分と他者に0のbenefitを与えるだけなのでコメントアウト #self._get_benefit(0) #self._give_benefit_to_others(0) # 裏切る else: self.strategy = 0 self._get_benefit(self.T) self._give_benefit_to_others(self.H) # 裏切り者ユーザーへの懲罰 def punish_discooperative_user(self, i): # 協力する(懲罰する) if rnd.uniform(0,1) < self.V_j: self.strategy = 1 self._get_benefit(self.P) self._give_benefit_to_discooperator(self.E, i) # 裏切る(懲罰しない、見逃す) else: self.strategy = 0 #自分と他者に0のbenefitを与えるだけなのでコメントアウト #self._get_benefit(0) #self._give_benefit_to_others(0) # 裏切り者を見逃したユーザーへ懲罰を与える def punish_overlook_user(self, j): # 協力する(懲罰する) if rnd.uniform(0,1) < self.V_k: self.strategy = 1 self._get_benefit(self.P_d) self._give_benefit_to_discooperator(self.E_d, j) # 裏切る(懲罰しない、見逃す) else: self.strategy = 0 #自分と他者に0のbenefitを与えるだけなのでコメントアウト #self._get_benefit(0) #self._give_benefit_to_others(0) # 周囲を真似て進化する(本当は遺伝的アルゴリズムを使ってるらしいんですが、とりあえず) def evolution(self, B_i, V_j): if rnd.uniform(0,1) < 0.01: self.B_i = rnd.uniform(0,1) self.V_j = rnd.uniform(0,1) else: self.B_i = B_i self.V_j = V_j # 利得を得る(またはコストを払う) def _get_benefit(self, his_benefit): global benefit_list benefit_list[self.num] += his_benefit # 自分以外の全員のエージェントに利得を与える def _give_benefit_to_others(self, others_benefit): global benefit_list benefit_list[self.num] -= others_benefit #自分からは一旦引き算する benefit_list += others_benefit # 非協力者を攻撃する def _give_benefit_to_discooperator(self, benefit, discooperator_num): global benefit_list benefit_list[discooperator_num] += benefit # 一回の試行の処理 def one_game_trial(agents): agentnums = list(range(N)) rnd.shuffle(agentnums) # 1人目が協力するか裏切るかを決める i = agentnums[0] agents[i].make_strategy() # もし協力者なら何もせずにこの試行を終了 if agents[i].strategy == 1: return None # 2人目が裏切り者を罰するかを決める j = agentnums[1] agents[j].punish_discooperative_user(i) # もし協力者なら何もせずにこの試行を終了 if agents[j].strategy == 1: return None # 3人目が裏切り者を見逃した人を罰するかを決める k = agentnums[2] agents[k].punish_overlook_user(j) # エージェントを一人を選んで、進化させる def evolution_turn(agents): # 利得が最大のエージェントを見つける(注:np.argmaxはインデックスの小さい値を優先する) maxindex = np.argmax(benefit_list) a = agents[maxindex] # 進化させる i = rnd.randint(N) agents[i].evolution(a.B_i, a.V_j) # メイン処理 def main(): # エージェントのインスタンスの入ったリストを作る agents = [Agent() for x in range(N)] for i, a in enumerate(agents): a.set_agent_number(i) # 試行回数の分だけ処理を実行する tmp = [] for t in range(T): one_game_trial(agents) # 一回の試行につき一人を進化させる実装でいいのかな…? evolution_turn(agents) tmp.append(np.sum([a.strategy for a in agents])) return tmp # プログラム実行時の処理 if __name__ == '__main__': plt.plot(main()) plt.show()
グラフが出てきます。縦軸が協調の戦略をとっている人数、横軸がステップ数です。
グラフの形が全く違うので、プログラムのどこかに間違いが潜んでいます。
前のプログラム。グローバル変数の扱いとかいろいろ間違ってました。
import numpy as np from numpy import random as rnd class Agent(object): # グローバル変数として利得を更新する(絶対良くない実装だけど) global benefit_list T = 3 H = -1 E = -9 P = -2 E_d = -9 P_d = -2 # この辺の数字は適当です # 協調率B_i B_i = 0.5 # 裏切り者ユーザーへの懲罰率V_i V_j = 0.1 # 裏切り者を見逃したユーザーへの懲罰率V_k V_k = 0.2 # 発見率 S_t = 0.5 def __init__(self): # 初期値では、協力(1)か裏切り(0)を1/2で選ぶ self.strategy = rnd.binomial(n=1, p=0.5) # エージェントの番号をセットする def set_agent_number(self, i): self.num = i # 裏切るか協力するか決断する def make_strategy(self): # 裏切る if S_t < B_i: self.strategy = 0 self._get_benefit(T) self._give_benefit_to_others(H) # 協力する else: self.strategy = 1 #自分と他者に0のbenefitを与えるだけなのでコメントアウト #self._get_benefit(0) #self._give_benefit_to_others(0) # 裏切り者ユーザーへの懲罰 def punish_discooperative_user(self, i): # 裏切る(懲罰しない、見逃す) if S_t < V_j: #TODO:: S_tの計算方法がわからない(周囲にいる協力者の割合?) self.strategy = 0 #自分と他者に0のbenefitを与えるだけなのでコメントアウト #self._get_benefit(0) #self._give_benefit_to_others(0) # 協力する(懲罰する) else: self.strategy = 1 self._get_benefit(P) self._give_benefit_to_discooperator(E, i) # 裏切り者を見逃したユーザーへ懲罰を与える def punish_overlook_user(self, j): if S_t < V_k: self.strategy = 0 #自分と他者に0のbenefitを与えるだけなのでコメントアウト #self._get_benefit(0) #self._give_benefit_to_others(0) else: self.strategy = 1 self._get_benefit(P_d) self._give_benefit_to_discooperator(E_d, j) # 利得を得る(またはコストを払う) def _get_benefit(self, his_benefit): benefit_list[self.num] += his_benefit # 自分以外の全員のエージェントに利得を与える def _give_benefit_to_others(self, others_benefit): benefit_list[self.num] -= others_benefit #自分からは一旦引き算する benefit_list += others_benefit # 非協力者を攻撃する def _give_benefit_to_discooperator(self, benefit, discooperator_num): benefit_list[discooperator_num] += benefit # 一回の試行の処理 def one_trial(): agentnums = list(range(N)) rnd.shuffle(agentnums) # 1人目が協力するか裏切るかを決める i = agentnums[0] agents[i].make_strategy() # もし協力者なら何もせずにこの試行を終了 if agent[i].strategy == 1: return # 2人目が裏切り者を罰するかを決める j = agentnums[1] agent[j].punish_discooperative_user(i) # もし協力者なら何もせずにこの試行を終了 if agent[j].strategy == 1: return # 3人目が裏切り者を見逃した人を罰するかを決める k = agentnums[2] agent[k].punish_overlook_user(j) # メイン処理 def main(): # エージェントの数 N = 10 # シミュレーションの試行回数 T = 1000 # 利得を表したリストの初期値は全部0 benefit_list = np.zeros(N) # エージェントのインスタンスの入ったリストを作る agents = [Agent() for x in range(N)] for i, a in enumerate(agents): a.set_agent_number(i) # 試行回数の分だけ処理を実行する for t in range(T): one_trial() # プログラム実行時の処理 if __name__ == '__main__': main()