RePast如何使用,中文版,非常全面的翻译.doc_第1页
RePast如何使用,中文版,非常全面的翻译.doc_第2页
RePast如何使用,中文版,非常全面的翻译.doc_第3页
RePast如何使用,中文版,非常全面的翻译.doc_第4页
RePast如何使用,中文版,非常全面的翻译.doc_第5页
已阅读5页,还剩36页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

RePast如何使用,中文版,非常全面的翻译A RePast Tutorial by John T. Murphy, University of Arizona & Arizona State University (contact) (请尊重作者版权)译者:Brian Yang, Anhui Normal University ()原文链接:/Tutorials/H2R/main.htm1.如何生成一个 RePast 模型RePast仿真工具箱是一个预先制作好的编程元素的集合,这些元素可以让你相对容易地构建基于主体(Agent-Based)的仿真模型。但是,有些步骤,明显不是直觉上的,却也没有在任何我所找到的文档里解释清楚;这个简短的向导能让你按照步骤一点一点实现我们的目标。概要/*译者注:为了使用RePast,你的电脑需要配备JDK(安装后最好配置一下环境,如果装有Apple的QuickTime,那么恭喜你,请卸载QT吧,否则配置会出问题;不配置也行),再下载Eclipse和RePast J,给出三个下载的页面链接(2011/7/28):JDK ,Eclipse: 点击直接下载RePast J: (点击直接下载)注意这里并没有使用RePast官网主页面的RePast Simphony的链接,因为我们做的例子是基于Java的,Repast J足够了,注结束*/一个基本的RePast模型包括以下几个元素(这个太重要了!):1. 一个Model对象,它用来作为模型本身,运行也是从这个文件运行2. 一个Space对象,即空间对象,它控制行为发生的环境3. 一个Agent对象,即行为主体对象其实理论上有的时候是可以忽略掉详细的Space对象的,即不去创建这个Space.java,但是我们很少去这么做。因为一旦它被忽略,那么agents就可能被默认在一个无限的空间内生存,或者是默认在一个极小的空间内,以至于无法和别的agents进行交互,那么就丧失了仿真的意义。在这几个元素当中,建立起来最复杂的是Model对象,它会被第一个执行并且控制着整个仿真过程,因此我们从Model对象来一步一步往下说比较合理。2.The SimModel Object (Repast J中的一个对象)Model对象会继承RePast的SimModelImpl对象。我想,你绝对不会看许多SimModelImpl对象实现的代码,但是事实上这些代码是最重要的,因为它控制了整个RePast环境和仿真的过程。而且,它预期在你创建的Model对象中找到一些东西 不幸的是这些东西没有一个真正的Interface(接口)正式,但是思路是差不多的。你创建的Model对象将会被分块在Eclipse上设计,其实就是一个功能一个功能往上加,并且最终由RePast J的工具条上的按钮触发运行。第一个按钮用来打开一个模型,红叉用来关闭一个模型,红叉左边的小灯泡用来设置参数;最重要的是第三、四、五个按钮,第三个表示运行,第四个表示单步运行,第五个表示初始化模型;第六个表示停止,第七个表示暂停,第八个按钮(循环箭头)表示截断并将仿真恢复到未初始化状态。(这些按钮的名称务必记住,后面讲到时不再附图)我们先做一个小小的测试模型,下面译者一步一步实现给大家看:/*译者注1. 打开Eclipse,文件,新,Java项目,项目名填上MyFirstRePastModel,Next此时界面应该是:2. 选择第三个标签:库,右边按钮“添加外部JAR”3. (3,4两步很重要)选择安装RePast的文件夹RePast 3,进入后打开RePast J,单击选中repast.jar文件,点击打开4. 重复步骤2,进入RePast J文件夹后,直接双击打开lib文件夹,Ctrl+A实现全选,点击打开,然后完成 (如果以后写代码时出现包未导入的提示,说明这两步你没有导入成功,这两步其实很no-brainer的:就是把repast.jar和lib文件夹下所有的jar文件都导入就行了)5. 最后点击完成不要管我其他的项目此时,就完成了与RePast J的连接,说白了就是在Eclipse中写代码,然后让RePast J 环境运行,导入外部的这些jar文件,目的是写程序时需要导入诸多Repast包,此时再回头看看教程1里的第一句话:RePast仿真工具箱是一个预先制作好的编程元素的集合。6. 双击打开MyFirstRePastModel,右键src,新建,包,包名填上demo(随意),完成7. 右键demo,新建,类 (分别新建三个类,我这里分别取名为Model,Space,Agent) 8. 对于Model.java,仅保留第一行代码(如右上图所示),然后开始在Model里,请一步一步添加代码,自己敲一遍的过程可以把我们以后要用到的基本方法都熟悉一遍 */9. 我们的模型,主类首先继承SimModelImpl,还必须自己扩充一些方法(methods)。比如,必须有一个getName方法来返回正在仿真模型的名字,这个名字出现在设置参数的面板的工具栏里。这里默认你懂得Java基本的知识,虽然译者对于Java也只能算是初级的水平。下面是getName实现的代码,注意,为加深大家印象,我特意截图,你们不得不自己敲。这里说明一下,教程2的代码全部在package demo; 这一句下方添加。10. 但是,这里还有更多的需要。当你在RePast 工具栏上点初始化按钮的时候,RePast将会触发一个在你模型当中叫做begin的方法,这个方法是为了初始化仿真器。注意,从这里开始,每新加入的代码都用红色粗体标注。11. 用技术上的术语来说,SimModelImpl类是一个抽象类,它实现了一个接口;接口需要实现某些方法,我们已经看到的begin()和getName()就是其中的两个需要实现的方法,马上还会看到更多;然而,有一个附加的框架,虽然不是Java也不是RePast包必须要求的,但是它是更加简单更加传统的方法,用来组织程序。就是将begin()方法分块:buildModel,buildSchedule和buildDisplay。因此,此时代码变为:我前面说了,这种结构不是必须的,你可以按照你自己的方法来。但是,unfortunately,当你发现许多其他RePast模型都这么写的时候,你就不淡定了。而且事实上这样写有很多优点,所以你以后也这么写吧。12. 没有必须的、明确的方法用来实现运行、暂停和停止函数,尽管他们依赖于模型中核心的成分。但是有另外的三个函数是必须实现的。第一个是setup函数,当循环箭头被按下时调用该函数,我通常把这个setup函数放在begin函数前面,个人喜好。13. 第二个是getSchedule函数,它必须返回一个Schedule类型的对象。每一个RePast模型将至少有一个schedule对象。一般的,通过加一个schedule对象作为类变量来生成。注意:Schedule是一个对象,schedule是一个变量,这是Java传统规范规定的。因此:注意到,为了添加一个schedule对象,必须导入该类的包,一个schedule变量在变量域声明,其方法在下方添加。/*到这里,译者简单说下,schedule主要是运行方法,尤其指时间上的安排*/这里有一点奇怪,schedule对象是model的内部的,却被声明为private,这样的话,就不能直接被外部程序或者是对象获取。然后,getSchedule方法作为公共方法被给出,从而间接的将schedule对象传递给外部程序或者是其他对象。显然,直接把schedule给它们似乎更高效,所以把schedule声明为public类型的岂不是更好?当然不行,这简直糟透了。马上RePast引擎就要启动了,它必须要获得模型的schedule,这就必须通过调用getSchedule方法,而这个方法有时SimModelImpl接口所必须实现的。所以,编译器强迫你得这么写。好吧,如果你真的把schedule设成public类型的了,那么RePast引擎就会直接找schedule,这就无法确保编译器是不是温柔了,改了值也不一定。更加重要的是,通过遵守内部变量一律设置成private而且有对应的方法可以间接存取,程序员就轻松了,想怎么实现内部schedule就怎么实现,they are free 在这个例子中,我命名我的schedule对象为schedule,但是这不是必须的,可能有更加间接的名字,比如sch等等。更更更加重要的是,我可以生成一个Schedule类的子类并且添加更多的功能函数,或者干点其他的事来满足我的需要;只要getSchedule方法返回一个合适类型的对象就行了,根本不用管它是不是还做了其他额外的工作,RePast引擎都会很开心,因为getSchedule的工作圆满完成了。最后,我们注意到public/private在这里的重要性并没有它应该有的那么大;我们可以让我们的schedule对象是public的,并且不用太在意它,因为相对于使用我们构建的API的其他程序员来说,我们更加懂得这个模型是怎么使用的,因为模型就是我们做的。14. 最后一个必须的函数比较复杂,它叫做getInitParam,它返回一个字符串变量数组,该数组列出了一组特殊变量的名字。你将在RePast控制面板设置这些变量。假设你想要一个实现了N个实体的仿真器,但是你又想能够通过点击setup和initialize按钮来运行不同的N个实体来进行仿真实验,巧了,RePast允许这样,但是:l 你必须提供给RePast一个参数列表,这个例子里叫做numAgentsl 你必须为每一个列表中的参数创建get和set方法,注意,是每一个下面是控制面板和这一步的代码: 15. 典型的编程实例和特殊的RePast结构需要我们提供get和set方法;最后,你得告诉RePast你想能够通过控制面板来设置这些变量,你需要实现getInitParam方法,完整代码:其实你们不知道,大写是件很难搞的问题,因为变量是NumAgents但是get和set方法变成了getNumAgents(注意这里N是大写)和setNumAgents。事实上,RePast到时候会自动寻找名为”get” + ”NumAgents”和”set” + “NumAgents”的方法,因此找到了”getNumAgents”和”setNumAgents”方法,具体的你们不必深究。注意:getInitParams返回的是一个字符串数组;但是到目前为止我们只有一个字符串会被返回,那就是”NumAgents”;我们学到后面会发现要返回的有很多,绝对不止一个,那么可以这么写:即使只有一个返回的参数名,返回类型也必须是数组类型。其中的告诉我们,返回的其实是一个指向字符串数组的指针。/*译者注:到这里为止,原作的教程2部分就完全结束了。我想大家跟我第一次看这个的时候心情是一样的,估计早就已经迫不及待的点了运行,然后发现竟然要配置运行环境,然后又不管三七二十一直接点了运行,发现Eclipse在苦苦的找main函数,最后弹出个令你灰心丧气的窗口,不得不放弃了。这时候你心想,这不坑爹呢嘛!我敲了那么久的代码,压根不能运行啊。这时候请你回想当初说RePast需要的三个因素,我们到底实现了几个?即使是Model我们也没有实现啊同志们!这个教程2只是为了让我们了解我们今后为Model编程时一些必须的参数或者是方法,这些只是基础中的基础。或者可以说,这些是任何Model的框架,我们今后写项目,就是往这里面填内容,RePast很难的,等你继续往后再学个十几个教程你就知道了。等到学完了教程,实现了那个例子,你又跃跃欲试想写个新的,你又会发现自己什么都不会!就是这样的,哪有随随便便就学会一个新工具的,何况主要的部分是在扩充别人的东西,如果你对那些预先做好的元素不熟,做一个新项目简直就是自杀行为。到这里,我现在把主要的内容总结一下,求求你们了,一定要耐着性子再看几分钟:继承SimpleModel类a) buildModel:创建模型中的智能体对象、空间对象等。b) buildDisplay:建立显示界面内容,包括空间对象的显示、统计图表等。c) buildSchedule:建立程序的运行方法d) Setup:设置/初始化程序(与仿真界面中的setup图形按钮相对应)e) Step:每个仿真时钟向前推进一个单位时,需要执行的操作(与仿真界面中的step图形按钮相对应)f) Begin:程序开始运行,其中需要调用buildModel, buildDisplay, buildSchedule三个方法g) getInitParam:在Settings窗口中显示和编辑的参数名序列h) getSchedule:显示模型中的运行schedule(buildSchedule方法中创建)i) getName:返回模型的名字单单教程1和2,翻译加上做Word文档就用了6个小时!从下一个教程3到最后一个教程,长达30个!而且都只是围绕一个模型来的,如果一步一步来做,收获会非常大!有人把这个例子实现了,又抄了抄帮助文档,竟然发了一篇文章,真是唉,中国的学术3.The CarryDrop model (掉钱捡钱模型)假设我们想创建一个基本的RePast模型,该模型有以下以下特点:l Agents只在有限的网格区域内移动l Agents总是沿上、下、左、右、左上、左下、右上、右下八个方向呈直线移动l 区域内每个格子都有可能放着一些钱;当agents走入一个有钱的格子后就会捡起所有的钱,并且之后也带着这些钱移动l 当一个agent移入一个格子,而恰巧格子里已经有了一个agent,那么碰撞发生,新移入的agent需要付给原先占据格子的agent一个单位的买路钱,并且立刻选择一个新的方向在下一个时间片到来时移出格子,新的方向同样允许八个方向l Agents有生命期,这个生命期由参数给出,参数有一定的范围(min, max);当一个agent死亡后,它生前拥有的钱全部随机分散到区域中的格子里,然后把这个agent从格子里抹掉,即清空格子,但是随即又有一个新的agent在不同的格子里诞生,其拥有新的移动方向规律和新的生命期为了达到这个目的,我们需要三个文件:一个实例化SimModel对象的类,一个指定agents的类,一个描述space的类。请按照之前的教程,新建名为CarryDrop的工程(别忘了添加外部JAR!),在src里新建名为demo的包,在demo里新建三个Java类: CarryDropModel / 与之前不同的是,需要加入Main方法,代码如下图 CarryDropAgent / 新建后暂时不管 CarryDropSpace / 新建后暂时不管细心的话,你应该会发现,这个时候右键,运行,就会出现一个运行方式是“Java应用程序”,而不是先前的运行配置,但是现在依然运行不起来,到底缺少的是什么呢?缺少的东西有很多,事实上。4.添加用户可设定的参数让我们考虑一下哪些参数需要设置为用户可设定:l Agents数量 (NumAgents) l 区域X轴方向长度 (WorldXSize) l 区域Y轴方向长度 (WorldYSize)既然要引入两个新的变量,就需要:l 在类变量域添加新的变量l 在面板上加入变量与变量之间相隔的线l 添加变量相应的get和set方法从这里开始,由于代码越来越长,对截图造成极大不便,所以只截出添加新代码的那部分:还有:5.编译和运行基本模型我想大家等待着一刻都快疯了吧?别着急,直接运行还不行,得再加几个东西。还有Main函数导入包不多解释,需要解释一下Main函数里的三条语句:l 第一行创建了一个SimInit类型的对象,导入部分就是为了它l 第二行创建了CarryDropModel类型的对象,就是实例化了模型对象l 第三行用Init对象里的loadModel方法装载模型在CarryDropModel.java编辑区,右键,运行方式,Java应用程序,然后出现:看右上方的工具条和参数面板的GUI,你发现可以输入数字,但是现在输没有任何意义,我们运行只是为了看一下效果,帮大家缓解一下欲望。你要真是实在忍不住点击了运行按钮,你会发现它蹦出一个窗口:6.用户可设定的参数的 默认值看上上一张图,你会发现参数面板上各个参数值都为0,这样的话,不太可能启动模型,所以我们来为三个参数设定默认值:要修改的代码就这一部分,添加三个常量并赋值为100,40,40,再用常量为初始化变量域,对于Java不熟悉的人来说,你不要管常量怎么声明的,按照上面的方法声明就对了。有些人认为为这些用户运行时要设定的参数赋上初值没有什么意义,确实没意义,但是这是一个非常好的编程习惯,很多小细节的把握会让你的编程水平越来越高。7.子程序的提醒为了示范和教学目的,现添加以下代码,来输出提示信息,在CarryDropModel.java中:现在再运行一下模型看看效果吧!这些代码是完全无害的。在Eclipse运行之后点击工具条上的运行,除了之前说的效果之外,你会发现RePast Output会出现:8.Space 对象从这节教程开始,我们暂时先跳出model对象的构建,来考虑其他两个对象:CarryDropSpace和CarryDropAgent。首先我们考虑一下Space对象。Space对象似乎一个单独的对象,它将包括预先打包好的RePast空间环境因素;这个例子我们使用的是RePast的Object2DGrid对象。一开始我们来为网格空间对象定义变量。Object2DGrid是一个RePast里专门为我们做好了的Space对象;有些它提供的函数还不得不使用,所以先导入这个包:这个例子中,只创建space对象是不够的。不管怎样我们先把这个对象必须的一些东西写好。这个moneySpace存储的是一些整数对象,好吧你可以认为是整数,但是实际上与整数相差甚远。整数对象的值代表着在网格里放着的钱的总数,为了方便,我们一律初始为0:上面的代码告诉我们,Object2DGrid对象的构造器需要两个整数作为参数,分别对应的是X和Y的大小。你肯定问你怎么知道?!开始,所有程序,RePast 3,RePast J,docs,API。既然提到了钱,那么肯定不能全部都是0,所以我们现在来为space对象初始化一些钱。我们的策略将是创建一个构造器,它需要一些钱的参数来初始化space对象。这个时候,肯定需要一个参数:钱的数量。仔细考虑一下这个参数放在哪,Space空间吗?不,个人认为放在Model对象里好一些,为什么?自己想(参数初始化明白了么?):还有下面:别忘了get和set方法:9.在SimModel里的Space对象space对象需要更多的工作来做,但是现在又可以回到model对象了,因为我们需要在model对象里为space对象做点集成性的工作。我们要做三件事:1. 为space对象分配一个变量2. 在一个合适的时间创建space对象3. 在一个合适的时间销毁space对象space对象必须在模型构建起来的时候创建;也就是说,begin方法被调用时,它会在buildModel里被触发。但是奇怪的是,它也会在这会儿被销毁。讲白了就是我一运行,space对象就被销毁了,这不坑爹嘛!所以我们需要在model对象里加一些代码,这些代码生成一个对象变量,它可以维持住space对象,让它不会那么快就挂了;我们需要在setup方法中设置这个变量的值为null,并且需要在buildModel方法中创建space对象。听懂没?10.初始化Space对象我们现在需要存一些钱,放在网格里。这需要为CarryDropSpace对象创建一个新方法,用来在对象空间objectSpace周围随机的在网格里放一些钱。在对象空间被创建后不久这个方法就会被立即调用,在buildModel方法里调用。还有这个方法的具体实现过程:下面简单解释一下这段代码:11.小小的跑题取钱事实上,部分spreadMoney方法里的代码的用处可能远不止散钱这么简单;因此我们把几行代码单独提出来放到一个新的方法里。很不幸的,刚写好的代码又要改了:自习体会一下,这么写是不是更简洁易懂了?貌似没有。但是扩展性就强了。12.一些可见输出概念上说,我们继续谈论model部分是有意义的,我们已经创建了环境,但是没有主体和行为(agents and actions)。但是,如果我们开始创建图形显示界面,很多部分我们会越来越清晰。这就是RePast其中一个非常方便的地方,但是依然有些小问题需要注意。首先我们要定义我们的目标:说到现在我们都还没实现二维网格的可视化呢。背景是白色的;格子里的钱应该是能看见的(如果有的话),意思就是钱的颜色应与背景颜色不同。为了实现这些,我们需要使用三个预先构建好的RePast对象:DisplaySurface, Value2DDisplay, 和 ColorMap.DisplaySurface对象是基本的窗口。一个ColorMap对象是特定值到特定颜色间的单映射,比如0是黑色,1是暗红,2是亮红,3是更加亮的红,通过颜色的深浅可以显示出网格里的数量多少(很直观不是么?但是可惜的是网格太小了,颜色差异不易区分)。Value2DDisplay是一个对象,它连接了一个地图和你模型中要显示的值相对应,它连接了窗体,功能函数,颜色地图于一身。为了使用这些对象我们必须:创建一个DisplaySurface类型的变量来储存它1. 在setup方法里,撤销一个display surface,这通过执行dispose方法来实现(但是只要对象被实例化了,对object != null进行测试就是合适的),然后设置对象值为null2. 实例化一个新的display surface;为了实现这个目的你必须传递两个参数:a) display surface将要服务的模型对象b) 被创建的display surface的名字(这将出现在窗体的标题栏里)3. 注册display surface,告诉RePast它的存在,并且让RePast知道它的名字4. 创建一个color map并指定初始值5. 创建一个Value2DDisplay对象来连接我们想要设置的值的集和color map6. 把Value2DDisplay对象加到DisplaySurface对象中去听起来很复杂,其实做起来容易,但是仍然有许多小的步骤,所以我们慢慢来。DisplaySurface对象的变量将在变量域声明;撤销display surface,重新实例化它,注册,这几步都在setup方法中完成,看下图(注意到有新的import语句!)。注意创建display surface对象的参数是this,这就是说,运行setup方法的model对象将把它自己作为参数传递给display surface。在CarryDropModel.java中加入新代码:13.创建一个Color Map干嘛要创建一个color map,前文书已经说了。比如网格里没钱(0 dollar),网格显示红色;里面有1美元,网格显示亮一点的红,以此类推,我们得在buildDisplay方法中加入相应的代码。首先我们需要创建一个ColorMap对象,这必将会再次引入新的import语句。/*译者注:同志们哪你们看见了没有啊,不归路啊,你根本不知道它到底需要哪些东西啊,这些玩意它不说你是想不到的啊,虽然Eclipse会自动提示你导入要导入的东西,可是你怎么知道要把代码加在buildDisplay里?RePast J里自带的不少活生生的例子,运行看看吧,查查代码吧亲,这玩意真不是我一步一步说的那么简单啊,加油!*/ 有意思的是 map 变量并不是必须在main方法中声明的;它可以是buildDisplay方法里的局部变量。创建的对象被分配到这个变量中,并且将会被作为一个参数传递给别的参数的方法,它始终类似一个指向对象的指针,即使当buildDisplay方法调用结束并且map对象不再拥有作用域。(如果这些你弄不懂,没关系,请再读一遍或者多读几遍,如果还是不懂,那就撞墙吧,forget it,现在不懂没事。)ColorMap对象有一个方法用来映射 值和颜色 的关系。值只是整数而已,颜色却是一个Color对象,它被定义在Java库里,所以要添加新的import语句,初始化我们使用Red/Green/Blue这几个颜色值。我们需要创建16个这样的Color对象,所以我们用循环来做。我们要确保0代表着白色,为什么要这样呢?因为白色是背景色。很容易理解。Value2DDisplay连接了 网格和color map 之间的关系。这里的网格指的是CarryDropSpace对象中的moneySpace格子;我们需要在这个对象中插入一个方法来返回这个网格。最后,把Value2DDisplay加到Display Surface中去:还有CarryDropSpace.java中:14.生成一个Display为了看到我们之前做的工作的效果,现在只需要一步:在begin方法里,执行DisplaySurface对象的display方法,换句话说,打开显示器。加入代码后运行看看效果吧:15.Agent对象让我们看看Agent对象吧。现在我们来赐予行为实体(agents)一些属性:l money,钱l x坐标l y坐标l 离死亡还有多少步我们已经说了我们的模型里的实体们拿着钱到处跑来跑去,所以钱这个变量是必须的;XY坐标变量可能会有点点儿让人感到费解,这里要说的是用XY来存储它的位置,而且这将被保留在space对象的另一个Object2DGrid中;离死亡的步数是另一个基本方面,特别的,我们怎么设定生命期的值域min, max?Ideally we would like to reset these as model parameters. This means that they get built into the model object as every other model parameter (default values, get and set methods, added to the array of strings returned by getInitParams. /*这句话我没有理解,不是语法的问题,是我不知道它想表达什么,请在教程4和教程5找相关说明*/我们告诉agents它们生命期的方法是在agent对象的构造器中传递这些值作为参数。在CarryDropModel.java中:在CarryDropAgent.java中:16.创建agentsModel对象将创建指定数量的实体(agent)对象的实例,从而来开始仿真。这需要几个元素:l 一组agents,我们用Java对象中的ArrayList来实现l 创建n个agent并把它们加到数组里l 当setup函数撤销model的时候,销毁这些agents现在我们将创建agents并把它们加到Agent数组里,这个用一个单独的方法来实现。17.Agent在Space里我们需要修改space对象从而可以把agents加进去。我们将使用过去用来存钱的对象来做;但是,这个对象会有许多不同的用途,比如,我们将不会用它来显示agents。首先,我们想能够问它:在(x, y)这个地方有一个agent实体吗?这个问题在space对象里很好作答(看看x, y你就知道答案了),但是如果你只是把空间位置信息作为变量存放在每一个agent的实例中,那么问题就复杂了。如果你这么做了,你能回答这个问题的唯一方式就是问每一个agent:唉,老兄,你在(x, y)坐标位置吗?显然,这样的话,规模一大,不但你疯了,电脑也疯了。所以我们不会这么做,而且还有一个好处就是它会直接让我们避免了两个agent放到一个网格的情况出现。我们需要做的就是:1. 创建Agent Space对象。这不需要按照money space那样用对象来初始化,因为我们会按照一样的方式使用它,但是它却是需要被声明为一个变量,然后以同样的方式实例化2. 创建一个函数,如果一个网格被一个agent占据了,返回true,否则返回false3. 创建一个函数,把agent加到space中去这里面的第3步最为复杂。我们想确定函数不但把agent加到space中去了,而且可以很体贴得顺便把它扔到一个未被其他agent占据的网格里。我们还希望确定agent被放到了agentSpace中并且agent拥有的x和y值被同时赋值。为了达到这个目的,我们还需要创建一个set方法来为agent对象的x和y位置变量赋值。/*译者注:上面的代码中,为什么那个countLimit里为什么要乘以10,看懂了吗?如果agent被成功加进去了,addAgent将会返回true,否则返回false。有一个原因可能导致添加失败,那就是没有空网格了。所以该方法将最多猜n次,这里的n=10*网格数,如果在n次之后依然没有,那么才返回false,这样的话,程序的健壮性要好一些*/18.把Agents加到Space里我们已经创建了把agents放到space对象里的方法。最初在buildModel创建的那些agents能够通过一句代码放入space内。19.报告这是一个常见且有用的策略,尽管它与model本身并没有密切关系,但是最好还是要提供报告函数。实现这个需要几个有用的技术:1. 提供一个report函数,它将为指定对象列出实例变量的值2. 为对象的每一个实例提供一个唯一的ID变量为了达到第二个效果,我们要使用静态变量(static variables)。我们将创建可以被类得所有实例读出的静态变量,并当一个新的agent被创建时,它的值会在构造器内自增。我们将同时提供getID方法来返回agent的唯一的ID,顺便也写一下money和StepsToLive的get方法吧。这些在我们想要收集信息的时候会非常有用,我们在buildModel里加一段代码让它遍历agent数组并且让他们提供报告。在CarryDropAgent.java中:在CarryDropModel.java中:20.显示agents要想把agents显示出来我们需要做下面这些工作:1. 我们需要创建Object2DDisplay对象,这跟Value2DDisplay的工作原理很像,都是把网格和要显示的什么东西连接起来。但是在这个例子中,要显示的不是值,而是一组活生生的对象。然后我们把agent列表作为Object2DDisplay对象的对象列表,并把它加入display surface。2. 我们需要让agents能够形象的显示出来,通过实现drawable的接口。drawable接口需要agents满足:a) 有get方法来获取x和y位置,名为getX()和getY(),它们返回int值b) 有draw方法,把SimGraphics对象作为参数并且提供一组说明来告知如何画对象很重要的一点,当对象被加到display surface时,需要按顺序来。先被加进去的是drawn,这样才能确保money space被画在背景上,其次是agents,它们才会被显示在上方。对于新加入的代码,请仔细看看实现接口的那一部分:21.加入时间表(schedule)和动作(action)如果你现在运行model,它将什么都做不了,弹出个窗口显示”No schedule!”。我们现在就来加schedule和一些action到model里去,好让它能够随时间步运行起来。现在我们把注意力集中到一个很无聊的元素上来:给agents年龄(age)。每走一个时间步,agents的stepsToLive变量就要递减1个单位。这是很简单的动作,并且现在我们暂时不管当它走到0的时候会发生什么;而是,我们将关注RePast是如何使用一个Schedule对象来管理仿真动作的。我们需要在CarryDropAgent里创建一个叫做step的方法;这很简单。到现在为止,它要做的就是帮stepsToLive变量递减1。稍微复杂一点的部分就是创建schedule变量。在setup方法里创建一个schedule对象的实例;这个例子当中我们告诉对象,我们想让它在运行时间步时,之间有1个单位的暂停间隔。下一步我们必须指定schedule对象要做什么。这通过创建一个内部类来实现就是说,一个类被定义在另一个类中间而不是单独提出来放在一个文件里并通过使用RePastd的内嵌函数把它加到Schedule对象里去。这些内嵌函数可以让你指定内部类定义的动作什么时候被执行。所有的这些都放在model对象的buildSchedule方法里。创建的内部类必须继承BasicAction对象,这个对象需要定义一个execute()方法,在execute()方法里你又可以指定哪些动作会被执行。至于我们说到现在的动作,就两个:1. 重新(shuffle)排列agent list2. 为list上的每一个agent调用step函数注意到我们shuffle的过程用到了另一个RePast工具,SimUtilities;RePast有一个内建的工具来自动重排ArrayLists,这样的话我们就可以用它来随机排序下一个将要走一步的agents顺序。注意到shuffle函数需要一个cern.jet.random库;你需要把建立路径告诉你的工程,也就是导入外部jar,名为colt.jar;这步就免了,译者一开始就让大家把所有你能看得到的jar都导入进去了,不会再出任何问题了,放心吧。除非你导入失败了!/*上面这句代码需要认真看一下。(1)是什么意思?这篇教程中有四个字红色加粗了*/上面最后一句的API注释是:还有:运行起来的效果并不让人兴奋,agents是蓝色的,工具条右边的时间一直在走。22.另一个schedule例子我们现在加另一个小的report函数,单纯为了演示使用schedule对象的另一个方法。Schedule对象是RePast的关键部分,但是任何model都不可能会大量使用schedule对象,因为不需要那么多功能;我们在这里加一个无关的功能只是为了告诉你它有多牛B多灵活。我们加两个东西:1. model类中一个新方法,用来计算还没死的agent的个数2. 一个新BasicAction来调用上面说的新方法然后把BasicAction对象加到schedule对象中去,这就要用到一个完全不同的方法了。在这个例子中我们加它进去以便于它不会一个时间步执行一次,而是每10个时间步执行一次。修改代码然后运行,注意,用单步运行,每10次,看看还有多少活着:23.Displays and Schedules (这个就不翻译了,觉得这样挺好)/*译者注:显示的更新也需要用schedule来规划,你想想是不是?刚才你单步运行就看到了吧?哦对,我想你肯定没有注意到。那你点击直接运行了吗?Output一会就显示0个agents或者,但是注意到了吗,蓝色的agents根本没有变化?所以,显示必须更新,就如同你正在看的这个Word文档,你点击最大化,最小化,或者移动窗口,凭什么你就能看到它移到了新的位置,老位置的窗口就消失了?你肯定会想这还用说么,必须的啊。哪有这么爽的事,必须要加更新函数:在execute()方法里*/ 为了让演示更加清晰,我们再加一些新的代码,让那些还有10步以上时间可以活的agents变成绿色,将死之agents会变蓝:还有在CarryDropAgent.java中:BTW,从这一节开始,建议大家每节结束都仔细看看运行的效果。24.生命的循环:agents死了又活了就目前来看,我们说一个agent死了,它也只是假死,因为只是stepsToLive小于0之后继续在递减而已。所以我们需要让agent真的死掉并把它移出仿真过程。为了避免产生过多的悲伤情绪,我们稍后立马来说新的agent如何诞生。死亡行为如下:1. 把agent从space对象中移走2. 把agent的钱散到space对象3. 把agent从agent list中移走一旦这些执行了,所有跟这个死掉的agent对象有关的指针都被回收了,这是Java一项比较好的机制。为了达到这些目的,我们加一个reapDeadAgents()方法到模型的execute()里去,还需要在space 对象里加一个removeAgentAt()方法。注意reapDeadAgents方法会返回一个死掉的agent总数的整数值,我们让相同数量的agents诞生(agent总数永不变)。运行,看一下效果吧!25.让agents知道它们自己的位置教程26,也就是下一步,agents将学会捡钱。所以这一步需要给它们一些细节上的帮助:agents必须知道它们在哪一个space对象里。现在它们还不知道;它们虽然知道它们的x,y坐标,space对象也有指向每一个agent的指针,但是agent没有指针指向space对象。agents需要这些小改变(请仔细体会下面三点):1. agent必须有一个CarryDropSpace变量2. agent必须有一个可以设置CarryDropSpace变量值的set方法3. space对象的addAgent方法必须修改为能够设置对象的CarryDropSpace变量换句话说,当agent被模型放到一个space里,它至少能知道我在哪个space里:在CarryDropSpace.java中26.让agents学会捡钱我们需要在space对象中创建take money函数:1. 返回在里面捡钱的格子的钱数2. 把格子的钱置为0配备了这些,我们只需要在agent的step函数里加一行代码,money + = 捡来的钱数:在CarryDropSpace.java的最后:运行之后,试着与上一节的运行效果比较,请仔细看看。27.让agents动起来到目前为止我们的agents还是不能动的。让它们动起来很复杂。原因之一就是一个agent一次只能待在一个网格里。这就意味着我们的代码中需要有协调的部分,来防止agents之间发生冲突。我们需要一些元素来让agents动起来:1. agents必须有一个方向direction变量。事实上我们会用两个变量,来分别指代x,y在方向上的变化,可以想象,这两个变量只能取三个值:-1, 0, 1。我们不允许二者同时为0,所以所有的agents都会移动。我们还将在agent被创建时初始化这两个变量2. 在space空间里会有一个方法来确确实实地移动agent。Agent将会告诉这个方法它现在在哪和它想去哪;space对象将检查agent想去的地方是不是已经被别的agent占据了,如果是就返回false,意味着移动将不能执行。如果移动可以执行,space对象将会重置agent位置变量并且将更新agent自己的space对象,那么agent就能移动了3. 面对冲突的反应是让agent重置其x,y速度(?原作这么写的)变量以便修改其方向代码的最后,我们命名x,y的速度变量为vX和vY。我们创建一个方法用来随机重置他们俩,这个方法会在构造器中被调用:在CarryDropSpace.java的最后:28.金钱易主我们的模型规定当冲突发生,即当两个agents同时想要移入一个网格中时(/*译者注:不要理解错了,不要理解成一个闯入另一个的领地*/),首先我们会用一个方法判定谁是真正的闯入者,虽然两个都是无辜的,但是我们还是得挑一个出来,然后如果这个闯入者有钱,那么就给对方1个buck,还需要加一个方法到space对象中去,用来返回在(x,y)位置的agent的指针。我们还需要在agent对象中加入一个方法用来接收别人给的钱。在CarryDropAgent.java中:在CarryDropSpace.java中:29.探测SPACE和AGENTS现在你运行,模型基本上完成仿真效果了。但是为了便于收集信息,我们还要加点东西。首先我们必须允许用户点击display surface来获得网格中的agent的信息或者是钱的数量。为了达到这个目的,我们只需要改掉把agent空间和money空间加到display中的命令就行了,从addDisplayable到addDisplayableProbeable。剩下的都由RePast自己搞定。现在你先把代码改掉。跟着我来试试看,运行模型,点击setup,让model显示出来但不运行;一直单步运行,知道你看到一个网格既没有钱也没有agent,但是不远处就有一个agent在缓缓朝它挪过来;此时双击那个agent,你会看到一个盒子显示出来,上面写着网格的值和x,y坐标。你可以在那个网格里输入一个新的值作为钱数,按回车键,关掉盒子;继续单步运行一次,显示会更新一次,你再看看那个网格里钱的数量(建议之前输入15,这样颜色会比较显眼);继续单步执行知道agent移入那个网格并最终

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论