前回は・・・
コンビニなどの小売店内を想像したエージェントシミュレーションをしました。リンクを張っておきます。
<エージェントシミュレーション(コンビニ)>
https://qiita.com/chutake_exe/items/7906fa90f8272f326895
テーマ:噂の広まり方
大学が夏休みに入り、自由な時間が増えました。前回のコンビニのシミュレーションをもとに、今回は噂の広まり方をシミュレーションしてみようと思います。
今回は噂を伝える人、噂を受け取る人を用意します。ここではそれぞれAとBと名付けます。
・レイアウト
前回と同様に、pythonのTKinterを使います。30×30マスでフィールドを構築します。また観察を行うために必要なボタンもいくつか実装しました。
ここで赤い人物はA(噂を知っている人)、青い人物はB(噂を知らない人)としています。
・現実に近づけるために
今回のシミュレーションでは、噂の広まる方法は近くで聞こえてしまった場合に限ります。
また皆さんは、聞こえてしまった噂を鵜呑みにはしないですよね?(笑)
これらを踏まえてエージェントに2つの変数を用意し、その変数を調整することで観察をします。
変数1・・・Aが噂を発信する範囲(マス)
変数2・・・Bが噂を信じるまでの回数
少し前の噂や事実は忘れられていくように、情報には期限があります。よってあるステップ経過後、自動的にAはBに変化する(噂を広めなくなる)ようにしました。
・実験方法
変数を1つだけ変えて以下の試行をし、①~③で対照実験をする。
1, Bを10人単位でフィールドに追加していきます。
2, Aを1人追加する。(噂の発信源)
3, 全員に噂が広まる(全員Aになる)
4, 3になるまでのステップ数を計算する
① 人数を変えてみる。11人(A=1,B=10)と21人(A=1,B=20)
② 噂を広める範囲を変更してみる。
③ 噂を信じる回数を変更する。
「たまたま人が集まっていて、噂が広まるのが早すぎた」などが起こると、終了状態になるまでのステップ数が異常値になってしまいます。今回は各5回ずつ試行し、その平均値をとることで解決とします。(各5回では少ない気もしますが...)
結果
①~③の結果をグラフにしてみました。
① 人数を変える。左:11人(A=1,B=10)と右:21人(A=1,B=20)
全員がAになるまで、それぞれ平均 1503.6ステップ、__639.6ステップ__でした。まあ、人数が多ければ多いほど噂って広まりますよね。
② 噂の広まる範囲を変える。(左:2マス→右:4マス)
全員がAになるまで、それぞれ平均 1503.6ステップ、476.6ステップ でした。
③ 噂を信じるまでの回数を変える。(左:5回→右:3回)
全員がAになるまで、それぞれ平均 1503.6ステップ 、936.6ステップ でした。
・まとめ
1番ステップ数が短いのが、②の噂を広める範囲を変えることでした。
結果をみて自分で「そりゃそうだろ、広い範囲に影響を与えるのが早く広まるじゃん」と思いました(笑)。よってこの観察の結果から、噂を早く広めたいとするならば
・有名人やインフルエンサーが発信源となることで、効率的に噂を広めることができる
ということが分かりました。
最後に
前回同様、現実世界になるべく近づけることが課題になります。フィールド上のA(噂を広める人)は一定期間ではありますが、その間は噂をし続けるという条件です。他にも噂を知る手段としてSNSなどがあります。このシミュレーションと同時並行で、エージェントがSNSを見ていて影響を受けるというのも面白そうですね。今回もプログラムを載せておきます。
プログラム(シミュレーション本体)
from tkinter import *
import random
import time
from dataclasses import dataclass
import matplotlib.pyplot as plt
CELLSIZE=20
FIELD_X,FIELD_Y=30,30
WIDTH,HEIGHT=FIELD_X*CELLSIZE,FIELD_Y*CELLSIZE
tk=Tk()
tk.title("噂の広まり方")
canvas=Canvas(tk,width=WIDTH,height=HEIGHT)
canvas.pack()
##変数##
layout=[]#Fieldの座標などが入る
agents=[]#エージェントたち
speed=2#観察の速さ
Allr=False #全員RedになったらTrueに
Allb=False #全員blueになったらTrueに
Start=False #フィールドにREDを追加したらカウント開始
RedAgent=[]#StartがTrueのとき、ステップ毎にREDの増減をadd
##/変数##
##dataclass##
@dataclass
class people:
i:int
x:int
y:int
status:str #購入前(yet)→購入後(done)
life:int
effectnum:int #影響受ける数
@dataclass
class Layout:
x:int
y:int
##/dataclass##
##関数##
def make_layout(layout):#レイアウトを作る
for x in range(FIELD_X):
for y in range(FIELD_Y):
layout.append(Layout(x,y))
def create_layout(L):
for x in range(FIELD_X):
canvas.create_line(x*CELLSIZE,0,x*CELLSIZE,FIELD_Y*CELLSIZE)
for y in range(FIELD_Y):
canvas.create_line(0,y*CELLSIZE,FIELD_X*CELLSIZE,y*CELLSIZE,)
def rectangle_buyer(agents):
for agent in agents:
x=agent.x*CELLSIZE
y=agent.y*CELLSIZE
if(agent.status=="done"):
canvas.create_rectangle(x,y,x+CELLSIZE,y+CELLSIZE,fill="red")
elif(agent.status=="yet"):
canvas.create_rectangle(x,y,x+CELLSIZE,y+CELLSIZE,fill="blue")
def add():#BLUE追加
for x in range(10):#客をふやす
i=len(agents)+1
x=random.randint(0,29)
y=random.randint(0,29)
status="yet"
life=0
effe=0
agents.append(people(i,x,y,status,life,effe))
def add_rumor():#RED追加
global Start
i=len(agents)+1
x=random.randint(0,29)
y=random.randint(0,29)
status="done"
life=200#うわさの寿命
effe=0
agents.append(people(i,x,y,status,life,effe))
Start=True
def move(agents):#エージェントの移動
for agent in agents:
while(True):
rand=random.randint(0,50)#1~5で方向を決める
if(rand<10):#右
p=FIELD_X*(agent.x+1)+agent.y
if(not p>FIELD_X*FIELD_Y):
agent.x+=1
break
elif(rand<20):#下
p=agent.y+1
if(not p>FIELD_Y-1):
agent.y+=1
break
elif(rand<30):#左
p=FIELD_X*(agent.x-1)+agent.y
if(not p<0):
agent.x-=1
break
elif(rand<40):#上
p=agent.y-1
if(not p<0):
agent.y-=1
break
else:#止まる
break
##行動おわり
def check(distance,effectNum):#distance=噂が広まる距離、effectNum=噂が広まるための回数
for checkagent in agents:
if(checkagent.status=="done"):
for agent in agents:
if(abs(agent.x-checkagent.x)<=distance and abs(agent.y-checkagent.y)<=distance and agent.status=="yet"):#近くにいるとき
agent.effectnum+=1
if(agent.effectnum>=effectNum):
agent.status="done"
agent.effectnum=0
agent.life=200
checkagent.life-=1
if(checkagent.life<0):
checkagent.status="yet"
checkagent.life=0
def checkAllr():##全員redか判定
if(len(agents)==0):
return False
for agent in agents:
if(agent.status=="yet"):
return False
return True
def checkAllb():#全員blueか判定
if(len(agents)==0):
return False
for agent in agents:
if(agent.status=="done"):
return False
return True
def print_all():#エージェント総出力
for agent in agents:
print(agent)
def change_speed():#観察のスピード変更
global speed
speed=txt.get()
def RedAgentCount():#REDを数える
count=0
for agent in agents:
if(agent.status=="done"):
count+=1
return count
def Redprint():#REDの増減をリストで出力
print(RedAgent)
##/関数##
#レイアウト構成
make_layout(layout)
##UI##
btn1=Button(tk,text="人追加",command=add)
btn1.pack()
btn2=Button(tk,text="出力",command=print_all)
btn2.pack()
btn3=Button(tk,text="重要人物",command=add_rumor)
btn3.pack()
txt=Entry(width=20)
txt.insert(0,"2")
txt.pack()
btn4=Button(tk,text="速さ変更",command=change_speed)
btn4.pack()
btn5=Button(tk,text="REDのデータ",command=Redprint)
btn5.pack()
##/UI##
##main##
def main_loop():
global Allr,Allb
canvas.delete("all")#全部消す
create_layout(layout)#レイアウト描画
move(agents)#エージェントの移動 (エージェントのリスト)
check(2,5)#噂が広まるかチェック (広まる距離、近づいた回数)
rectangle_buyer(agents)#エージェントの描画
#全員がREDかBLUEになるまでリストに追加する
if(checkAllr()):
Allr=True
print(f"{RedAgentCount()} : 全員REDになりました。")
if(checkAllb() and Start):
Allb=True
print(f"{RedAgentCount()} : 全員BLUEになりました。")
if((not Allr) and (not Allb)):
if(Start):
RedAgent.append(RedAgentCount())
tk.after(speed,main_loop)
##/main##
##main呼び出し
main_loop()
プログラム(グラフ書く用)
import matplotlib.pyplot as plt
#取得した値を入れる
data1=[]
data2=[]
data3=[]
data4=[]
data5=[]
#何ステップで終了したか出力
print(f"data1:{len(data1)}")
print(f"data2:{len(data2)}")
print(f"data3:{len(data3)}")
print(f"data4:{len(data4)}")
print(f"data5:{len(data5)}")
#グラフの描画
plt.title("噂を広める範囲=2,信じるまでの回数=5",fontname="MS Gothic")
plt.xlabel("移動回数(時刻)",fontname="MS Gothic")
plt.ylabel("噂が広まった人数(人)",fontname="MS Gothic")
plt.plot(data1,color="red")
plt.plot(data2,color="blue")
plt.plot(data3,color="black")
plt.plot(data4,color="orange")
plt.plot(data5,color="green")
plt.show()
##5回の平均
print((len(data1)+len(data2)+len(data3)+len(data4)+len(data5))/5)