很長,反著你也不看,點(diǎn)個(gè)贊意思下就行 ——前文再續(xù),書接上一回 ABM編程基礎(chǔ):元胞自動(dòng)機(jī)在介紹森林火災(zāi)模擬的代碼之前,我們先簡單介紹一下ABM編程的基礎(chǔ)。ABM是Agent-Based Modeling的縮寫,是一種基于多智能體的建模方法,它將現(xiàn)實(shí)世界中的實(shí)體(如人、動(dòng)物、物體等)抽象為計(jì)算機(jī)程序中的對(duì)象(Agent),并通過模擬這些對(duì)象之間的相互作用來研究復(fù)雜系統(tǒng)的行為。在ABM中,每個(gè)對(duì)象都有自己的屬性和行為,這些屬性和行為可以通過編程來模擬。通過模擬多個(gè)對(duì)象之間的相互作用,我們可以研究復(fù)雜系統(tǒng)的行為,例如生態(tài)系統(tǒng)、社會(huì)系統(tǒng)、金融系統(tǒng)等。另外,ABM實(shí)際上是元胞的一種衍生形式,元胞自動(dòng)機(jī)作為非線性模擬的奠基理論,在整個(gè)模擬計(jì)算領(lǐng)域中都起到了一個(gè)object的作用,ABM也是在元胞自動(dòng)機(jī)的基礎(chǔ)上進(jìn)行了擴(kuò)展和延生。本著磨刀不誤砍柴工的原則,我們先了解一下元胞自動(dòng)機(jī)的基礎(chǔ),這些基礎(chǔ)也可以用在ABM中。元胞自動(dòng)機(jī)元胞自動(dòng)機(jī) (CA)是一種離散的、基于網(wǎng)格的計(jì)算模型,它由一組規(guī)則定義,這些規(guī)則決定了每個(gè)單元的狀態(tài)如何在時(shí)間上演變。CA的主要特點(diǎn)是其簡單性和可擴(kuò)展性,它可以用于研究許多不同類型的系統(tǒng),例如生物、化學(xué)、物理、社會(huì)等。元胞自動(dòng)機(jī)最早是由約翰·馮·諾依曼在1940年提出的,它的主要思想是將一個(gè)復(fù)雜的系統(tǒng)分解成一組簡單的規(guī)則,這些規(guī)則可以通過計(jì)算機(jī)程序來模擬。最知名的元胞自動(dòng)機(jī)是康威的生命游戲(Game of Life),它由英國數(shù)學(xué)家約翰·何頓·康威在1970年提出。康威的生命游戲是一個(gè)零玩家游戲,它的規(guī)則非常簡單,只需要考慮三個(gè)因素:當(dāng)前細(xì)胞的狀態(tài)、周圍細(xì)胞的狀態(tài)和周圍細(xì)胞的數(shù)量。根據(jù)這三個(gè)因素,康威的生命游戲可以得出當(dāng)前細(xì)胞的下一個(gè)狀態(tài)。元胞自動(dòng)機(jī)的生命游戲非常簡單,但是卻可以模擬出非常復(fù)雜的行為,例如細(xì)胞的分裂、融合、消亡等。你的結(jié)果也會(huì)和你的初始條件有關(guān),系統(tǒng)會(huì)根據(jù)規(guī)則,自動(dòng)演化成各種形態(tài)。元胞自動(dòng)機(jī)建模的要是有如下四個(gè): 空間網(wǎng)格元胞在空間中分布的空間格點(diǎn)的承載介質(zhì),一般是個(gè)二維的網(wǎng)格,因?yàn)榫W(wǎng)格更容易實(shí)現(xiàn),也更容易計(jì)算后面的鄰域。鄰域在元胞自動(dòng)機(jī)中,每個(gè)元胞都有一定數(shù)量的鄰居,這些鄰居可以是同一行、同一列、同一斜線上的相鄰元胞,也可以是不同行、不同列、不同斜線上的相鄰元胞。鄰居的數(shù)量取決于元胞的形狀和拓?fù)浣Y(jié)構(gòu)。這些元胞的鄰居可以用來計(jì)算元胞的狀態(tài),也可以用來更新元胞的狀態(tài)。狀態(tài)集合在元胞自動(dòng)機(jī)中,每個(gè)元胞都有一個(gè)狀態(tài),這個(gè)狀態(tài)可以是一個(gè)數(shù)字、一個(gè)字母、一個(gè)顏色等等。狀態(tài)集合是元胞的狀態(tài)的集合,它可以是一個(gè)有限的集合,也可以是一個(gè)無限的集合。狀態(tài)集合的大小取決于元胞的形狀和拓?fù)浣Y(jié)構(gòu)。 例如在生命游戲里面,狀態(tài)集合就是0和1,0代表死亡,1代表存活。在森林火災(zāi)的模型里面,狀態(tài)集合也是0和1,0代表樹木,1代表被燃燒。演化規(guī)則最后一個(gè)是演化規(guī)則,這個(gè)是最重要的,也是整個(gè)模型的核心。在元胞自動(dòng)機(jī)中,演化規(guī)則是用來描述元胞狀態(tài)如何從一個(gè)狀態(tài)轉(zhuǎn)換到另一個(gè)狀態(tài)的。演化規(guī)則可以是一個(gè)簡單的規(guī)則,也可以是一個(gè)復(fù)雜的規(guī)則。例如在生命游戲里面,演化規(guī)則就是:元胞旁邊的8個(gè)元胞中,如果有3個(gè)元胞是存活的,那么這個(gè)元胞就會(huì)變成存活的(元胞繁殖)。如果有2個(gè)元胞是存活的,那么這個(gè)元胞就會(huì)保持原來的狀態(tài)。如果少于2個(gè)元胞是存活的,或者多于3個(gè)元胞是存活的,那么這個(gè)元胞就會(huì)變成死亡的(過少或者過多都會(huì)死亡)下面我來寫一個(gè)簡單的例子,來演示一下生命游戲:先看效果:是用一個(gè)50 * 50的網(wǎng)格空間,隨機(jī)取10%的元胞是存活的,然后演化100次。存活的細(xì)胞是250,然后按照生命游戲的規(guī)則,進(jìn)行演化,第一次演化,細(xì)胞就死了一多半……然后有一些位置的元胞,會(huì)進(jìn)入一個(gè)穩(wěn)態(tài),如下:大家看我圈出來的幾個(gè)結(jié)構(gòu),這幾個(gè)結(jié)構(gòu)是穩(wěn)定的,只要不發(fā)生外部元胞的入侵,本身就不會(huì)再發(fā)生變化。然后我們來看看后續(xù)的演變,發(fā)現(xiàn)外部的元胞入侵之后,就會(huì)發(fā)生變化,直接把之前的穩(wěn)定結(jié)構(gòu)給破壞了……經(jīng)過100步的演變之后,最終變成了這個(gè)樣子:整個(gè)過程還是比較有意思的,大家可以自己去試試。 全部演算過程如下: 注意,因?yàn)槲疫@里的初始化條件是隨機(jī)的,所以你運(yùn)行的結(jié)果在細(xì)節(jié)上可能和我的不一樣,但是整體的趨勢是一致的。
生命游戲的代碼實(shí)現(xiàn)這里采用的是Python來進(jìn)行實(shí)現(xiàn)的,用的是一個(gè)很強(qiáng)大的ABM包,叫做agentpy。這包不僅提供了abm的全套建模腳手架,而且還有可視化能力,老好用了,目的就是做出一個(gè)netlogo的平替。pip install agentpy
唯一問題是這個(gè)包自2021之后一個(gè)版本更新之后,就挺了兩年多了,雖然現(xiàn)在基礎(chǔ)功能足夠用了,但是沒有更新的東西的總是讓人覺得有點(diǎn)懸。不過我們也就是用他的engine,所以更新不更新就不重要了。 import agentpy as ap
import matplotlib.pyplot as plt import seaborn as sns import IPython # 解決繪圖中的中文亂碼問題 plt.rcParams['font.sans-serif'] = ['SimHei'] plt.rcParams['axes.unicode_minus'] = False
# 定義模型 class LifeGameModel(ap.Model): def setup(self): # 初始化參數(shù) self.p = { 'size': 50, 'density': 0.1, 'steps': 100, }
# 初始化細(xì)胞數(shù)量 cell_init_size = int(self.p['size'] * self.p['size']) # 創(chuàng)建agent cells = self.agents = ap.AgentDList(self, cell_init_size)
# 創(chuàng)建空間網(wǎng)格 self.space = ap.Grid(self, [self.p['size']] * 2, track_empty=True) self.space.add_agents(cells, random=True, empty=True)
# 初始化細(xì)胞狀態(tài) self.agents.condition = 0
# 隨機(jī)選擇一些細(xì)胞作為初始活細(xì)胞,選取數(shù)量為預(yù)設(shè)的密度 initial_live_cells = self.random.sample(self.agents, int(cell_init_size * self.p['density'])) for cell in initial_live_cells: cell.condition = 1
def step(self): # 計(jì)算規(guī)則:獲取所有細(xì)胞,然后計(jì)算每個(gè)細(xì)胞的鄰居的信息 #元胞旁邊的8個(gè)元胞中, # 如果有3個(gè)元胞是存活的,那么這個(gè)元胞就會(huì)變成存活的。 # 如果有2個(gè)元胞是存活的,那么這個(gè)元胞就會(huì)保持原來的狀態(tài)。 # 如果少于2個(gè)元胞是存活的,那么這個(gè)元胞就會(huì)變成死亡的。 # 如果多于3個(gè)元胞是存活的,那么這個(gè)元胞就會(huì)變成死亡的 live_cells = self.agents.select(self.agents.condition > -1) for live_c in live_cells: live_neighbors = self.space.neighbors(live_c) # 計(jì)算每個(gè)元胞的鄰居的信息,需要知道活著的鄰居的數(shù)量 live_neighbors_count = 0 for ne in live_neighbors.to_list(): if ne.condition ==1: live_neighbors_count += 1 if live_neighbors_count < 2 or live_neighbors_count > 3: live_c.condition = 0 elif live_neighbors_count == 3: live_c.condition = 1 else: live_c.condition = live_c.condition
if self.t >= self.p['steps']: self.stop() def end(self): # 在模擬結(jié)束時(shí),打印最終的細(xì)胞狀態(tài) print("Simulation ended. Final cell states:") for cell in self.agents: print(cell.condition, end=' ') print()
# 可視化過程,agentpy內(nèi)置了seaborn的擴(kuò)展,可以自動(dòng)生成動(dòng)態(tài)可視化效果 def animation_plot(model, ax): attr_grid = model.space.attr_grid('condition') color_dict = {0:'#F3F3F3', 1:'#d62c2c'} ap.gridplot(attr_grid, ax=ax, color_dict=color_dict, convert=True) ax.set_title(f"生命游戲模擬\n" f"模擬步數(shù): {model.t}, 存活的細(xì)胞數(shù)量: " f"{len(model.agents.select(model.agents.condition == 1))}")
fig, ax = plt.subplots() model = LifeGameModel() animation = ap.animate(model, fig, ax, animation_plot) IPython.display.HTML(animation.to_jshtml(fps=15))
我這里是用jupyter來進(jìn)行演示的,所以沒有main函數(shù),如下所示: 有同學(xué)推薦我在后面加福利,作為我的logo…… 真正的男生減速帶:
|