本章将介绍一个全新的NetLogo多主体模型,它是由羊和草两个物种构成的简单的生态系统。
羊-草生态系统模拟了自然界和人类社会都存在的最基本的生存逻辑:个体生存依赖一定的资源。这些资源可以是食物,也可以是财富、声望、权力、关系等。资源可以从环境中获得,它可以给个体带来好处(维持生存、达到行动目标),而失去资源将使个体处于不利的境地,不论是因为缺少食物而饿死,还是因为声誉损耗殆尽而无法在某地继续生存。
羊-草生态系统是关于系统内部食物、财富、声望、权力、关系等资源变化的基本模型,它以“羊”代表“个体”,以“草”代表“资源”,通过它我们可以研究资源分布、资源存量与个体生存及繁衍间的动态关系。掌握了羊-草生态系统模型,我们就可以将其扩展到其他资源和个体关系领域

5.1 羊-草生态系统规则

  1. 这是一个由羊(turtle)和草(patch)两个物种构成的小型生态系统。
  2. 羊的内部有一个能量值。吃掉草可以增加能量值。每一个周期都在消耗能量。能量值小于或等于0,羊就会死掉。
  3. 羊能够繁殖。当能量累积到一定水平,就会繁殖。繁殖需要消耗能量。新出生的羊会天然具备一定的能量。
  4. 草可以自发地从地里长出来。

5.2 初始化羊-草生态系统

下面用NetLogo实现这个基本的程序,首先初始化生态系统。
在“界面”添加“setup”按钮。由于羊的内部都有一个能量值,因此我们需要自定义一个turtle的属性—— energy。跟第4章讲的patches-own语法类似,使用turtles-own命令,方括号内是变量名energy:
turtles-own[energy]
接下来,清空之前的所有状态,如下所示:
image.png
然后动态地向生态系统添加草。本次模拟设置20%的草,80%的空地。

  1. 首先使用ask patches对所有patch进行循环,
  2. random-float命令产生一个01之间的随机小数 **random-float n****产生一个0n之间的随机小数**
  3. 这个数值如果小于0.2,就把当前patch设置成绿色。set pcolor green = pcolor = green
  4. 因此总体运行效果就是近20%的patch变成绿色,这样草的初始化就完成了。代码如下:

image.png
接下来初始化系统中的羊,使用create-turtles命令创建一只羊,并且把它的初始能量设置成100,否则这只羊没有能量用于移动。
image.png
以上这部分就是初始化功能,完成后我们可以运行一下。每次单击“setup”按钮,都会随机产生20%的绿草,羊就位于这个世界的中心位置。初始化的生态系统完整的初始化程序如下:
image.png

5.3 添加to go程序

接下来添加“go”按钮的程序,即每一个模拟周期要完成的功能。根据前述生态系统的规则,每一个模拟周期都要完成如下功能:

  1. 草要自然生长
  2. 每只羊要不断地移动
  3. 羊在能量积累到一定值时繁育后代
  4. 如果羊的能量消耗尽,就会死亡。
  5. 我们用子函数(也称子模块)的方式实现这几个功能,在“go”按钮对应的代码中添加如下语句:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    to go
    add_food ;;添加食物子函数
    ask turtles[
    turtle_move ;;turtle移动子函数
    turtle_breed ;;turtle繁殖子函数
    turtle_die ;;turtle死亡子函数
    ]
    tick
    end
    执行程序时,它就会调用相应子函数,从而实现整体功能。接下来我们看看每一个功能模块如何操作。

5.3.1 add_food

为了实现添加草的功能,我们添加了一个自定义模块,该模块跟to setup、to go代码类似,用to作为关键词,后面跟要定义的模块名称,最后用end结束,这样用户就可以定义自己的函数了。add_food函数要实现的功能就是在每个周期都添加一定量的草。可以添加如下代码:

image.png
这里用到了一个具有强大功能的函数——n-of。它的一般格式为:**n-of size agentset**
**n-of 10 patches**的作用就是从patches集合中随机挑选10个patch,形成了一个新的patches集合。
有了集合以后,再对集合中的每一个元素进行循环,将其颜色设置成绿色,无论这个patch当前是绿色的还是黑色的。
该函数的作用相当于下了一场“食物雨”,每一个模拟周期都会运行一遍add_food函数,都会有10个单位的patch添加上草。
完成了第一步add_food的操作,接下来要做的就是循环访问现在系统中所有的turtle,并且每一个turtle的一生都伴随着3件事——移动、繁殖和死亡。

5.3.2 turtle_move

关于第二个功能,首先我们来看羊的移动需要哪些操作,同样用一个子模块的方式定义移动相关代码,如下所示:
image.png
它要实现的功能包括如下几点。

  1. 如果当前patch是绿色的,即上面有草,那么这只羊会吃掉草,羊的能量也相应增加。这里设置羊的energy增加10,并将当前patch的颜色设置为黑色,表示草被吃掉了。这就是第一部分代码所完成的功能。
  2. 为了使羊的移动看起来更自然,我们让羊在每个周期以0.2的概率随机转换方向,其他时间都匀速直线前进
  3. 羊的移动功能。每个周期都消耗1个单位的能量,向前移动一步。这三部分代码都可以通过前面讲过的知识完成。如何设置变量的值、如何产生随机数、如何设置turtle的运动方向、用fd 1完成移动(这里fd是forward的缩写),这些语句组合在一起就实现了turtle_move这个函数。

5.3.3 turtle_breed

接下来用turtle_breed来完成繁殖这部分功能,代码如下所示
image.png
在这部分代码中,首先检测这只羊的能量水平是否足够繁育后代,这里规定当它的能量值大于500时才能够繁育后代。繁育后代涉及两件事情,

  1. 是自己的能量值减少。这部分代码也体现了为人父母的辛苦,养育后代要消耗自身大量能量。
  2. 是新生儿的出生。这里用到一个新命令——hatch。hatch的英文含义是孵化,非常形象。方括号内的这部分内容就是针对新出生的这只羊的。这只新出生的羊要完成两个操作,
    1. 第一是往前走一步——fd 1,新出生的羊的指向heading是随机取值的,往前走一步就可以跟它的母体分开。
    2. 第二个操作是给新出生的羊一个初始能量,否则它一出生就死掉了。此处把它的能量值设置成100。这样就完成了繁殖功能。

5.3.4 turtle_die

最后一个功能是turtle_die,判断一只羊的能量值小于或等于0,它就会死掉。在NetLogo中,我们可以用die这个单词作为命令来杀死turtle。
image.png
以上就是羊-草生态系统模型所需要的代码,运行一下看看它的效果。单击“setup”按钮,然后单击“go”按钮,开始可能大家会觉得画面非常乱,稍微把速度调慢一点儿,这时你就会看到有一些用小箭头表示的羊在环境里随机游走,并且可以吃掉草
image.png
我们可以重新运行程序,开始的时候只有一只羊,很快它就会繁殖出更多的羊,大家可以看到它的繁殖过程。只要吃掉的草足够多,它就可以进行繁殖。

5.4 追踪某一个具体的turtle或者patch的行为

  1. 右击turtle,可以对turtle进行inspect、watch和follow操作
    1. inspect:弹出记录了turtle属性的窗口
    2. watch:指定的turtle周围出现聚焦的圆圈
    3. follow:指定turtle出现在屏幕中心位置
  2. 右击patch,可以对patch进行inspect操作

5.5 变量的主体

  1. NetLogo中的每一个变量都有隶属关系
  2. 如果不是在ask turtles这个循环里调用turtle_move子函数,它的调用主体就不是turtle了,而是一个全局的调用主体obsever,obsever没有pcolor、energy属性,程序就会提示错误。

5.6 添加绘图框

  1. 在NetLogo“界面”加号旁边的下拉框中选中“图”(plot),选中时鼠标指针变成一个十字,然后在空白处单击,就会出现一个弹框
  2. 设置“名称”为Population(种群);设置它的“X轴标记”为Time,显示时间;“Y轴标记”设置为Population,显示种群数量;X和Y的最小值和最大值可以设置,也可以不设置;勾选“自动调整尺度”后,如果曲线的最大值超过坐标的最大值,绘图框将自动调节坐标最大值;“显示图例”就是在绘图框右侧位置标出每一个片条对应的曲线;“绘图笔”对应图中曲线。
  3. 第一条曲线绘制系统中羊群数量随时间变化的趋势。我们可以修改绘图笔的颜色为黑色,名称设为sheep。这里请尽量用英文来设置,因为有时曲线名称会对应函数来进行调用,写中文的话可能会出错。
  4. “绘图笔更新命令”表示实现这条曲线需要的代码,在每一次绘制这条曲线的新点的时候,需要激活这部分代码来实现绘图功能,默认代码plot count turtles刚好满足我们的需求。plot语句的作用就是绘制图形,count turtles就是统计turtle的数量。
  5. 第二条曲线绘制草总量的变化情况。添加一个绘图笔,把它的颜色改为绿色,名称改成grass。这时绘图命令就需要我们手动填写了plot count patches with [pcolor = green]这条命令表示绘制曲线,统计当前pcolor属性是绿色的patch数量。单击“确定”,绘图框就设定好了。
  6. 重新运行程序,你会发现运行得很好,但是右侧新加入的绘图框没有任何反应。这是因为在NetLogo当前版本中,只有设定了tick,绘图框才会生效。这点不难理解,不设置tick时,模拟世界有一个时钟,绘图本身的更新也有一个时钟,我们用tick来做模拟世界和绘图框的时间同步。跟之前的程序设定一样,在setup代码中添加reset-ticks进行重置,然后在to go代码中添加tick进行计时。这时再运行程序,就画出了漂亮的曲线

image.png

5.7 小结

  1. 介绍了pcolor、energy等每一个属性变量都有一个相应的调用主体,在实现子模块时要特别注意。
  2. 介绍了操作NetLogo界面追踪某一个具体的turtle或者patch的方法。这对于模型调试,以及理解turtle的行为是否正确,起到了重要作用。
  3. 介绍了如何添加plot绘图框。绘图框的使用非常方便,通过简单的设置就可以完成。但是要记得添加tick代码,确保模拟世界和绘图框的时间同步。
  4. 介绍了hatch和die两个关键字
    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
    54
    55
    56
    57
    58
    turtles-own[energy]
    to setup
    clear-all
    reset-ticks
    ask patches[
    if (random-float 1) < 0.2[
    set pcolor green
    ]
    ]
    create-turtles 1[
    set energy 100
    ]
    end

    to go
    tick
    addFood ;; 添加食物子函数
    ask turtles[
    turtleMove
    turtleBreed ;;turtle繁殖子函数
    turtleDie ;;turtle死亡子函数
    ]
    end

    to addFood ;;每一个模拟周期都会运行一遍add_food函数,都会有10个单位的patch添加上草
    ;; n-of 10 patches的作用就是从patches集合中随机挑选10个patch
    ;; 形成了一个新的patches集合
    ask n-of 10 patches[
    set pcolor green
    ]
    end
    to turtleMove
    if pcolor = green [
    set energy energy + 10
    set pcolor black
    ]
    if random-float 1 < 0.2 [
    set heading (random 360) ;; 在每个周期以0.2的概率随机转换方向,其他时间都匀速直线前进。
    ]
    set energy energy - 1 ;; 每个周期都消耗1个单位的能量,向前移动一步。
    forward 1 ;;
    end

    to turtleBreed
    if energy > 500 [
    set energy energy - 500
    ;; hatch。hatch的英文含义是孵化,非常形象。方括号内的这部分内容就是针对新出生的这只羊的。
    hatch 1 [
    forward 1 ;; 新出生的羊的指向heading是随机取值的,往前走一步就可以跟它的母体分开
    set energy 100 ;; 给新出生的羊一个初始能量,否则它一出生就死掉了。此处把它的能量值设置成100
    ]
    ]
    end
    to turtleDie
    if energy <= 0[
    die ;;给新出生的羊一个初始能量,否则它一出生就死掉了。此处把它的能量值设置成100
    ]
    end