本章将介绍一个人工经济模型,通过该模型进一步理解turtle之间的互动

6.1 货币转移模型

社会经济中有一个显著的现象:财富分布的不均衡性。
早在19世纪,意大利著名经济学家维弗雷多·帕累托(Vilfredo Pareto)就分析了大量实证数据,发现财富分布遵循一条幂律分布曲线。
这条幂律分布曲线满足帕累托法则(Pareto principle),也叫二八定律,即20%的财富被80%的穷人拥有(图中绿色的区域),而80%的财富被20%的富人占有(图中黄色的区域),二八定律充分反映了社会财富分布的不均衡性。
image.png为了解释这种分布不均衡性,2000年,物理学家Victor M.Yakovenk提出了一个非常简单的人工经济模型:货币转移模型(money transfer model)
在这个模型里,他把经济体比喻成分子,把货币量比喻成能量。对于一个气体系统来说,气体分子在碰撞过程中,能量只能从一个分子转移到另一个分子,而总能量保持守恒。经过大量碰撞,最终气体分子会达到一个非常不均等的能量分布状态。
货币在该模型中的分布也具有类似的特性。因此,货币转移模型符合如下基本规则:

  1. 经济系统中人和财富的总量保持不变;
  2. 开始的时候,每个人都有等量的货币;
  3. 每当两个主体相遇,他们就随机分配财富。

image.png

6.2 添加全局变量

首先打开NetLogo“界面”,添加两个按钮:“setup”和“go”,添加“go”按钮时勾选“持续执行”。然后添加一个全新的控件——“滑块”,使用滑块控件可以很方便地调整变量的数值。在“添加”下拉框中点选“滑块”,然后在空白处单击添加,这时会出现弹框,如图6-3所示。首先输入全局变量名称num_agents,代表主体个数,即货币转移模型中一共有多少人。“最小值”“增量”“最大值”用于设定数值变化范围,这里设定数值从1开始变到1000,增量是1,初始值设置成100,单击“确定”,这样全局变量就创建好了。拖动滑块,就可以看到变量的取值发生变化。
image.png
与此类似,我们可以再添加一个滑块来控制系统中的货币总量,变量名为total_money,取值范围选取1到1 000 000,增量为1,默认值设为10 000。这两个滑块所定义的总量值都可以在我们的代码中应用。

6.3 初始化模拟世界

1
2
3
4
5
6
7
8
to setup
clear-all
reset-ticks
create-turtles num_agents[
set money (total_money / num_agents) ;; 初始情况下每个turtle都拥有相同的货币量
setxy random-xcor random-ycor ;; 设置为随机取值,实现随机分布
] ;; 除号两侧要保留空格
end

6.4 主体之间如何交互

1
2
3
4
5
6
7
8
9
10
11
to go
ask turtles[
let agsets other turtles-here
if count agsets >= 1
[
transaction (one-of agsets)
]
forward 1
]
tick
end
  1. 在NetLogo里,let和set都是赋值语句,它们的用法一样,但二者有一个非常重要的区别:set只能应用于已经定义好的变量,而let适用于为第一次使用的变量赋值,它包含定义变量的意思。

比如变量agsets,前文并未定义,如果用set去赋值,系统会提示错误,所以这时必须使用let语句给agsets赋初始值。为什么以前没有遇到过let呢?比如初始化money时就没有定义过,其实turtles-own就给每一个turtle定义了money变量。另外像pcolor这样的变量,赋值前也没有先定义,但是pcolor是每一个patch都有的属性,它是NetLogo自带的变量,因此在初始化模拟世界时,patch的pcolor属性已经定义了。

  1. other turtles-here,顾名思义,就是其他所有turtles-here。第4章讲过,turtle和patch存在多个turtle对应一个patch的情况,比如当前turtle是我,我站在一个patch上,这个patch上也可能有其他turtle。
    1. turtles-here返回值就是当前patch上包含了我的所有turtle的集合
    2. other turtles-here返回值就是当前patch上除我以外其他turtle的集合,即我的潜在交易对象
  2. one-of agsets
    1. one-of agentset,从集合agentset中随机选择一个元素;
    2. n-of n agentset,从集合agentset中随机选择n个元素。
    3. 当元素个数不满足时,比如集合为空,它返回的也为空;小于n时,就返回小于n的所有元素。

6.4.1 transaction模块

  1. 类比于函数,有自己的参数,通过方括号传入
  2. let money1 ([money] of trader)此处[money] of trader相当于我们在写Java或者Python这种面向对象语言时用到的trader.money,在NetLogo中是用of来表示的,并且变量要用方括号括起来,如果要访问多个变量,可以用逗号隔开。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    to transaction [trader]
    let deltam 0
    let money1 ([money] of trader)
    let epsilon (random-float 1)
    set deltam (epsilon - 1) * money + epsilon * money1

    if money + deltam >= 0 and money1 - deltam >= 0
    [
    set money money + deltam
    ask trader[
    set money money1 - deltam
    ]

    ]
    end

6.4.2 变量作用域

在主体之间相互交互时,有一个关键点:变量的作用域。首先对所有turtle进行循环。因为每个turtle会随机选当前patch的另一个turtle做交易,所以transaction模块的所有变量,特别是money变量,隶属于当前turtle。也就是说,turtle在不停地进行循环。假如循环到的当前turtle是agent1,那么这时transaction模块的money就是agent1相应的属性
image.png
除去[money] of trader(方括号配合 of 的限定)set money money1 - deltam(作用域的限定)中的money是trader的属性,其他money都是当前turtle也就是agent1的属性。在所有NetLogo程序里,一定要捋清楚变量到底隶属于谁,当出现多层嵌套的时候,一定要清楚其中任意一个变量到底是哪个主体的。

6.5 命令中心

在编程中,首先要保证语法通顺,但是在代码实现过程中,也可能有一些逻辑错误,这时系统并不会报错。针对这个例子,如何验证程序中的逻辑是否正确呢?根据货币转移模型规则,系统中的人和货币的总量不变,但是代码中并没有验证这两个量是否守恒,所以需要一个调试手段来验证程序的正确性。我们可以使用命令中心的功能,通过命令交互的方式来访问整个NetLogo的变量。比如对于货币总量,我们可以在“观察者”那里输入以下命令:
sum [money] of turtles
它的作用就是把所有turtle的money组成一个集合,然后进行累加求和。
NetLogo是一个面向对象的层次性结构,它的最上层是observer,是由observer直接对整个系统进行操作的。因此我们刚才输入的命令实际是观察者(observer)输入的,它的返回结果也是由observer给出的。从返回值可以看到,它跟系统引入的货币量是一致的,小数是由于计算误差引起的,特别是过程中有随机小数把这些钱不停地进行分割,因此它在允许范围内会有一定误差。

6.6 绘制财富分布直方图

  1. 调整绘图笔为条形,这样它就会以柱状图的方式显示图形
  2. 绘图命令也要改,因为默认命令比较适合绘制时间序列曲线,例如横坐标轴是时间、纵坐标轴是种群数量的图形。但是当前需要绘制模型每一时刻的财富分布情况,因此要删掉绘图命令,并且修改X轴标记为Wealth,Y轴标记为Counts,在绘图更新命令中添加自定义函数——to-update-plot,这样在绘图时就会动态调用绘图更新命令。

histogram直方图

  1. 定义一个变量lst,该变量的值是所有turtle的money属性组成的集合,lst是一个数值列表,其中每一个数值对应某个turtle的财富值。
  2. 设定了统计小区间的个数为100。
  3. if not empty? lst判断lst这个列表是否为空。这里empty? 是NetLogo自带的变量,该变量是一个布尔型变量,返回true或false。因此整个判断语句的意思是,如果lst不为空,则执行下面两行代码
  4. set-plot-x-range 0 (max lst)设定了X轴的取值范围是从0到财富值的最大值。在默认情况下,它的取值范围是0~10,但是这并不合理,因为当turtle发生交易后,财富值有可能非常大,所以这时我们要动态调整它的最大值。
  5. histogram lst的作用是把lst变量以直方图的方式进行统计,并且把统计结果绘制到绘图界面。histogram是NetLogo自带的命令。
  6. 保持模拟世界和绘图界面同步更新。在初始化代码中加入reset-ticks,然后在to go函数中添加tick进行计时,最后记得把“视图更新方式”选项改为“按时间步更新”
    1
    2
    3
    4
    5
    6
    7
    8
    to to-update-plot
    let lst [money] of turtles
    set-histogram-num-bars 100 ;; 设定了统计小区间的个数为100
    if not empty? lst [
    set-plot-x-range 0 max lst
    histogram lst
    ]
    end

6.7 小结

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
turtles-own [money]
to setup
clear-all
reset-ticks
create-turtles num_agents[
set money (total_money / num_agents) ;; 初始情况下每个turtle都拥有相同的货币量
setxy random-xcor random-ycor ;; 设置为随机取值,实现随机分布
]
end
to go
ask turtles [
let agsets other turtles-here ;; 其他所有turtles-here
let agentNum count agsets
if agentNum >= 1 [
transaction (one-of agsets)
]
forward 1
]
tick
end
to transaction [trader]
let deltam 0
let money1 ([money] of trader)
let epsilon (random-float 1) ;; 定义epsilon为一个0~1的随机数。前面用过random-float,它会产生一个0~1的随机小数
-------------------
公式1
set deltam epsilon * money1 + (epsilon - 1) * money
if money + deltam >= 0 and money1 - deltam >= 0[
set money money + deltam
;;set [money] of trader money1 - deltam不能对其使用set
ask trader [
set money money1 - deltam
]
]
-------------------
公式2
let all money + money1
set money epsilon * all
ask trader [
set money (1 - epsilon) * all
]

end

to to-update-plot
let lst [money] of turtles
set-histogram-num-bars 100
if not empty? lst [
set-plot-x-range 0 (max lst)
histogram lst
]

end