基于蒙特卡洛模拟复现谢林模型(计算机社会学)

谢林模型引言

在谢林模型中,有一片方形区域,区域又被均匀分成若干小方格;有一群被称为代理的个体,每个代理居住在一个小方格内。这些代理可以分成若干类,所有代理都希望周边8个格子尽可能住有较多的同类代理,若居住区域不满足代理的居住要求,则该代理会搬到另一区域居住。

如图1-1所示,在5×5的区域内有13名代理,不同的颜色表示不同的代理类型。整个区域还存在着一些无人居住的空白方格,代理们可以搬到这些空白方格去居住。

图1-1 

假设代理5希望自己周围8格内同类代理的数量至少比异类代理的数量多2,则该代理会搬到如下图所示的位置。

图1-2 

假如代理1也有同样的诉求,那么在图1-1中他的居住要求是能得到满足的。而在图1-2中,由于代理5搬走,代理1的居住要求不再被满足,此时他也会想搬走。当一个代理搬走时,一方面会使得该代理周边同类代理的居住满意度下降,另一方面也会留下空白方格,从而允许其它类型的代理搬入。由此不难推出,这是一种级联现象,微观上单个代理的行为有可能引发整个群体的连锁反应,从而导致宏观上各类型代理在居住分布上的同类大片连续、异类相互隔离的现象。

蒙特卡洛模拟法

基本思想

蒙特卡罗方法是由冯诺依曼和乌拉姆等人发明的,“蒙特卡罗”这个名字是出自摩纳哥的蒙特卡罗赌场,这个方法是一类基于概率的方法的统称,不是特指一种方法。

蒙特卡罗方法也成统计模拟方法,是指使用随机数(或者更常见的伪随机数)来解决很多计算问题的方法。他的工作原理就是两件事:不断抽样、逐渐逼近

本题的蒙特卡洛思想

实际生活中,每一个代理(人家)搬家一定会提前选择好搬家的地址,也就是说搬家一次后基本不会发生变动(别人变动有概率让他变动),但是在程序中,我们的搬家并不能提前知道要搬到什么地方。于是,我们就让其随便搬家,如果不满足条件,则继续搬家,直到符合要求。

通过大量的模拟,代理一定会达到其应该在的位置并且不再搬家。

总结

通过大量模拟,让计算机模拟的模型(具体算法、图片都是模型)以概率(随着模拟量的增加,概率逐渐趋近于1,但始终不会是1)逼近真实的模型

代码验证 

# 谢灵模型的属性:规格、阙值、矩阵、种群数量(最多为5种)、人口
import random
import matplotlib.pyplot as plt
import numpy as np


class schellingModel:
    def __init__(self, size, threshold, kindNum):
        # 初始化谢林模型所需的参数
        self.size = size
        self.threshold = threshold
        self.matrix = np.zeros((size, size))
        self.kindNum = kindNum
        self.population = 0
        if kindNum == 2:
            self.red = 1
            self.white = 0
            self.blue = 2
        elif kindNum == 3:
            self.red = 1
            self.white = 0
            self.blue = 2
            self.yellow = 3
        elif kindNum == 4:
            self.red = 1
            self.white = 0
            self.blue = 2
            self.yellow = 3
            self.purple = 4
        else:
            print("输入的种群数量要小于4,大于2")
        # 初始化谢林模型参数——种群矩阵、人口数
        for i in range(size):
            for j in range(size):
                self.matrix[i][j] = random.randint(0, kindNum)
                if self.matrix[i][j] != 0:
                    self.population += 1

    # 判断(i,j)点是否满意————判断(i,j)点周围有多少个相似的邻居
    def is_satisfied(self, i, j, kind):
        same_neighbour = -1  # 要把自己除去
        neighbour_matrix = self.matrix[i - 1 if i - 1 > 0 else 0:i + 2 if i + 2 <= self.size else self.size,
                           j - 1 if j - 1 > 0 else 0:j + 2 if j + 2 <= self.size else self.size]
        for x in range(len(neighbour_matrix)):
            for y in range(len(neighbour_matrix[x])):
                if neighbour_matrix[x][y] == kind:
                    same_neighbour += 1
        if same_neighbour >= self.threshold:
            return True
        else:
            return False

    # 随机获取一个位置作为搬家地点
    def random_find_place(self):
        i = random.randint(0, self.size - 1)
        j = random.randint(0, self.size - 1)
        while self.matrix[i][j] != 0:
            i = random.randint(0, self.size - 1)
            j = random.randint(0, self.size - 1)
        return (i, j)  # 返回i、j构成的元组

    # 集体完成一次搬家
    def move(self):
        for i in range(self.size):
            for j in range(self.size):
                kind = self.matrix[i][j]
                judge = self.is_satisfied(i, j, kind)
                if judge == 0:  # 不满意
                    (x, y) = self.random_find_place()
                    self.matrix[x][y] = kind
                    self.matrix[i][j] = self.white  # 把原本的家庭住址变为空

    # 计算当前的满意家庭数
    def satisfy_num(self):
        satisfy = 0
        for i in range(self.size):
            for j in range(self.size):
                if self.matrix[i][j] != 0:
                    kind = self.matrix[i][j]
                    judge = self.is_satisfied(i, j, kind)
                    if judge == 1:
                        satisfy += 1
        return satisfy

    # 画图谢林模型图
    def draw(self, time, satisfy):
        if self.kindNum == 2:
            redx = []
            bluex = []
            redy = []
            bluey = []
            for i in range(self.size):
                for j in range(self.size):
                    if self.matrix[i][j] == self.blue:
                        bluex.append(i)
                        bluey.append(j)
                    elif self.matrix[i][j] == self.red:
                        redx.append(i)
                        redy.append(j)
            plt.scatter(redx, redy, c='r', marker='.', linewidths=0)
            plt.scatter(bluex, bluey, c='b', marker='.', linewidths=0)
            if satisfy == 0:
                plt.title('Initial')
                plt.show()
                return 0
            else:
                title = str(time) + ' times satisfy:' + str(float(satisfy) / float(self.population)*100) + "%"
                satisfy_rate = float(satisfy) / float(self.population)*100
                plt.title(title)
                plt.show()
                return satisfy_rate
        elif self.kindNum == 3:
            redx = []
            bluex = []
            redy = []
            bluey = []
            yellowx = []
            yellowy = []
            for i in range(self.size):
                for j in range(self.size):
                    if self.matrix[i][j] == self.blue:
                        bluex.append(i)
                        bluey.append(j)
                    elif self.matrix[i][j] == self.red:
                        redx.append(i)
                        redy.append(j)
                    elif self.matrix[i][j] == self.yellow:
                        yellowx.append(i)
                        yellowy.append(j)
            plt.scatter(redx, redy, c='r', marker='.', linewidths=0)
            plt.scatter(bluex, bluey, c='b', marker='.', linewidths=0)
            plt.scatter(yellowx, yellowy, c='y', marker='.', linewidths=0)
            if satisfy == 0:
                plt.title('Initial')
                plt.show()
                return 0
            else:
                title = str(time) + ' times satisfy:' + str(float(satisfy) / float(self.population)*100) + "%"
                satisfy_rate = float(satisfy) / float(self.population)*100
                plt.title(title)
                plt.show()
                return satisfy_rate
        elif self.kindNum == 4:
            redx = []
            bluex = []
            redy = []
            bluey = []
            yellowx = []
            yellowy = []
            purplex = []
            purpley = []
            for i in range(self.size):
                for j in range(self.size):
                    if self.matrix[i][j] == self.blue:
                        bluex.append(i)
                        bluey.append(j)
                    elif self.matrix[i][j] == self.red:
                        redx.append(i)
                        redy.append(j)
                    elif self.matrix[i][j] == self.yellow:
                        yellowx.append(i)
                        yellowy.append(j)
                    elif self.matrix[i][j] == self.purple:
                        purplex.append(i)
                        purpley.append(j)
            plt.scatter(redx, redy, c='r', marker='.', linewidths=0)
            plt.scatter(bluex, bluey, c='b', marker='.', linewidths=0)
            plt.scatter(yellowx, yellowy, c='y', marker='.', linewidths=0)
            plt.scatter(purplex, purpley, c='p', marker='.', linewidths=0)
            if satisfy == 0:
                plt.title('Initial')
                plt.show()
                return 0
            else:
                title = str(time) + ' times satisfy:' + str(float(satisfy) / float(self.population)*100) + "%"
                satisfy_rate = float(satisfy) / float(self.population)*100
                plt.title(title)
                plt.show()
                return satisfy_rate


if __name__ == '__main__':
    s = schellingModel(150, 4, 2)  # 150户人家、阙值点为4,两类人群
    s.draw(0, 0)  # 画出初始图
    i = 1
    satisfy_num = 0

    while(1):
        # 两次move计算聚合层度变化率
        s.move()
        satisfy_num1 = s.satisfy_num()
        satisfy_rate1 = s.draw(i, satisfy_num1)
        i += 1
        s.move()
        satisfy_num2 = s.satisfy_num()
        satisfy_rate2 = s.draw(i, satisfy_num2)
        cluster_rate = (satisfy_rate2-satisfy_rate1)/satisfy_rate1
        if cluster_rate < 0.000001:  # 什么时候停止算法迭代
            break
        i += 1

模型结果分析 

设置一片500×500的区域。设区域中居住有两类代理,每类代理各有100000个,初始时随机分布。设两类代理的居住要求都是周边8各中有至少4名同类代理。使用计算机程序进行模拟迭代,每次迭代时,按随机顺序判断每个代理的居住要求是否得到满足,不满足者搬往随机空白方格,程序模拟运行结果如下:

可以看到,在迭代20次后,整个区域就基本稳定下来(如下图)。

二十万个代理,只需20次迭代就可以从随机混沌的状态演变成同类聚居、异类隔离的格局,由此可见同质性的强大威力。观察稳态的图片不难发现,在不同类的同质块间存在着真空区域,将不同类代理聚居区域隔离开来。

改变模型参数,取20×20的区域,设置两类各15000名代理,设每个代理要求周边有至少5名同类代理,则可以得到如下结果:

(2000次后) 

(3000次后) 

(5000次后)

 

(15000次后) 

增强了居住要求之后,同类聚集区域变大了许多,隔离现象也变得非常明显。观察迭代过程不难发现,一开始时会存在多个小聚居区,后来这些小聚居区会逐渐合并成一个大的区域,最终只剩下每类代理各一个超大聚居区。由于居住要求过强,小聚居区边缘出的代理的居住要求往往得不到满足,因此这些代理会向外搬离,并级联地带动聚居区内部的代理也向外搬离,最终汇聚到一个大聚居区中,这很好的验证了谢林模型的迁移连锁效应。不过,同样也是因为居住要求过强,大聚居区边缘区域处于动态平衡状态,模拟过程将会无法收敛,聚居区的大小也存在上界。 

取300×300的区域,设两类代理各35000名,设两类代理的居住要求皆为周边8格内有x名同类代理。当x取不同的值时,稳态情况如下:

显然在x取0、1时不发生隔离,在x取2时有很轻微的局部隔离,在x取3时有不明显的隔离,在x取4时有明显的隔离,在x取5时有非常明显的聚集和隔离(难以收敛),在x大于或等于6时不发生隔离。显然,当x过小时,几乎所有代理都满足现状,同质性无法积累导致不发生隔离;而当x过大时,几乎所有代理的居住要求都无法得到满足,由于无法定居而不发生隔离。

总结

1、在要求同类代理占周边8格的约50%时,可以比较容易观察到隔离现象且隔离收敛容易。

2、当阙值要求大于4,模型收敛难度大大提高

3、在阙值比较大时,未必就不会发生隔离。在多次模拟后发现,有小概率在迭代次数很高时突然出现一个某类代理的小聚居区,并以滚雪球之势迅速成长为一个大聚居区。一旦某类代理出现了聚集现象,在多次迭代后其它类型的代理也会更容易出现聚集。

  • 28
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

十二月的猫

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值