ARTICLE DETAIL

资讯详情

深耕网站建设、视觉设计与SEO优化的一线实战洞察。

元胞自动机实战:从生命游戏到交通流与传染病建模

元胞自动机实战:从生命游戏到交通流与传染病建模 1. 项目概述从“游戏”到“科学”的元胞自动机第一次接触元胞自动机很多人会联想到上世纪风靡一时的“生命游戏”。屏幕上黑白格子的生灭演化看似简单却蕴含着令人着迷的复杂性与秩序。但如果你认为它只是个有趣的数学玩具那就大错特错了。今天我们要聊的元胞自动机早已跳出游戏的范畴成为解决交通流模拟、森林火灾蔓延、传染病传播、晶体生长乃至城市演化等一系列复杂系统建模问题的利器。它用一套极其简单的局部规则驱动整个系统涌现出宏观的、难以预测的全局行为这正是复杂科学的精髓所在。简单来说元胞自动机是一个由离散的“元胞”构成的网格世界。每个元胞就像棋盘上的一个格子它在任一时刻都处于有限种状态中的一种比如“生”或“死”“空”或“有车”。整个世界按照离散的时间步向前演化。在每一个时间步所有元胞同时根据一个确定的规则更新自己的状态。这个规则只关心该元胞自身当前的状态以及它周围少数几个邻居元胞的状态。没有中央指挥没有全局信息仅仅依靠每个元胞“目光短浅”地观察身边并遵循同一套“邻里公约”整个系统就能自发组织起来形成波澜壮阔的图景。这篇文章就是为你——无论是正在备战数学建模竞赛的学生还是对复杂系统、算法仿真感兴趣的工程师或研究者——准备的一份实战指南。我们将彻底拆解元胞自动机的核心思想并用MATLAB和Python这两种在科研和工程领域最主流的工具手把手带你实现几个经典且实用的案例。我会分享从规则设计、代码实现到性能优化、结果分析的全流程经验以及那些官方文档里不会告诉你的“坑”和技巧。读完本文你不仅能理解CA的原理更能独立地将其应用于你自己的问题场景中。2. 核心原理与模型构建理解规则的力量要玩转元胞自动机关键在于吃透它的四要素元胞空间、状态集、邻居规则和演化规则。这四者共同定义了一个CA模型。2.1 元胞空间与邻居定义元胞空间通常是一个一维、二维甚至三维的规则网格。二维网格最常见我们可以用矩阵来完美表示它。矩阵中的每个元素i, j就对应一个元胞元素的值代表该元胞当前的状态。邻居的定义决定了信息的局部传播范围。最常见的有两种冯·诺依曼邻居只考虑上下左右四个方向的正交邻居。就像一个十字路口只关心东西南北四个方向来的车。摩尔邻居考虑包括对角线方向在内的周围八个邻居。这更像是一个方格的中心关心所有与之接壤的八个格子。选择哪种邻居模型取决于你模拟的物理过程。例如模拟火焰在平地上的蔓延可能用冯·诺依曼邻居就够了火苗主要向四个方向扩散而模拟人群在广场上的移动用摩尔邻居会更合理人可以斜向走。注意边界处理是个容易被忽视但至关重要的问题。一个无限大的网格在计算机中无法实现我们必须定义边界行为。常见方法有固定边界边界元胞状态不变、周期边界网格上下相接、左右相连形成一个环面、吸收边界边界外视为某种固定状态如“死亡”或“空”。在数学建模中周期边界常用于模拟一个无限延伸的、均匀的环境能有效避免边界效应带来的干扰。2.2 状态集与演化规则状态集是元胞所有可能状态的集合。在最简单的“生命游戏”中状态集就是{0死, 1生}。在交通流模型中状态集可能是{0空, 1有车, 2慢速车}等。演化规则是整个CA的灵魂它是一个函数新状态 F(当前自身状态 邻居状态集合)。规则必须是确定性的、同时作用于所有元胞的。以经典的“生命游戏”规则为例其规则描述很口语化对于一个活细胞状态为1如果周围活细胞数少于2个则死于“孤独”。如果周围活细胞数为2个或3个则存活。如果周围活细胞数超过3个则死于“拥挤”。对于一个死细胞状态为0如果周围恰好有3个活细胞则“繁殖”为活细胞。这个规则看似简单却能产生滑翔机、振荡器、太空船等复杂结构甚至能构建出通用图灵机。这深刻揭示了“复杂源于简单”的道理。在设计你自己的CA规则时核心是抓住所模拟现象最本质的局部相互作用。不要试图一开始就设计一个面面俱到的复杂规则。从最核心的一两条交互逻辑开始通过仿真观察涌现现象再逐步迭代和细化规则这才是正确的建模思路。3. 经典案例一森林火灾模拟MATLAB实现我们用森林火灾模拟作为第一个实战案例。这个模型直观地展示了CA如何模拟动态传播过程。3.1 模型定义与规则设计我们定义二维网格每个元胞有三种状态0空地- 没有树木。1树木- 健康的树木。2燃烧- 正在燃烧的树木。演化规则如下采用摩尔邻居燃烧 - 空地正在燃烧的树木在下一时间步变为空地烧光了。树木 - 燃烧健康的树木如果其8个邻居中至少有一个处于“燃烧”状态则它在下个时间步以概率p_ignite被引燃。此外即使没有邻居着火树木也有一个极小的自燃概率p_lightning。空地 - 树木空地以概率p_grow生长出新的树木。这几个概率参数控制了模型的动态p_grow影响森林密度p_ignite影响火势蔓延速度p_lightning影响火灾发生的随机性。3.2 MATLAB代码实现与逐行解析下面是一个完整的、可运行的MATLAB实现包含了可视化动画。%% 森林火灾元胞自动机模拟 clear; close all; clc; % 参数设置 grid_size 200; % 网格大小 (200x200) p_init_tree 0.6; % 初始网格中树木的占比 p_grow 0.01; % 空地上生长树木的概率 p_lightning 0.0001; % 树木自燃的概率非常小 p_ignite 0.8; % 邻居着火时树木被引燃的概率 n_steps 500; % 模拟的总时间步数 % 初始化网格 % 生成初始网格0-空地 1-树木 2-燃烧 % 首先按概率 p_init_tree 随机生成树木状态1 forest (rand(grid_size) p_init_tree); % 在树木中随机选择一小部分作为初始火源状态2 burning_init find(forest 1); init_fire_idx burning_init(randperm(length(burning_init), round(length(burning_init)*0.001))); forest(init_fire_idx) 2; % 创建图形窗口用于动态显示 figure; h_image imagesc(forest); colormap([0.2 0.8 0.2; 0 0.5 0; 1 0 0]); % [空地; 树木; 燃烧] 对应的颜色 colorbar; title(森林火灾演化 - 元胞自动机模拟); axis equal tight; % 主模拟循环 for t 1:n_steps % 复制当前网格用于基于当前状态同步更新 forest_new forest; % 遍历网格中的每一个元胞 for i 1:grid_size for j 1:grid_size current_cell forest(i, j); % 获取摩尔邻居8邻域的状态使用周期边界条件 % 利用 mod 运算实现上下左右索引的循环 neighbors forest(mod([i-1, i, i1]-1, grid_size)1, ... mod([j-1, j, j1]-1, grid_size)1); % 移除中心元胞自身只保留8个邻居 neighbors neighbors(:); neighbors(5) []; % 中心是第5个元素3x3拉平后 % 规则1: 燃烧的细胞变为空地 if current_cell 2 forest_new(i, j) 0; % 规则2: 树木细胞 elseif current_cell 1 % 判断邻居中是否有燃烧的细胞 if any(neighbors 2) % 有邻居着火以概率 p_ignite 被引燃 if rand() p_ignite forest_new(i, j) 2; end else % 没有邻居着火但有极小的自燃概率 if rand() p_lightning forest_new(i, j) 2; end end % 规则3: 空地细胞以概率 p_grow 生长树木 elseif current_cell 0 if rand() p_grow forest_new(i, j) 1; end end end end % 更新网格状态 forest forest_new; % 更新可视化 set(h_image, CData, forest); title(sprintf(森林火灾模拟 - 时间步: %d / %d, t, n_steps)); drawnow; % 可选每N步暂停一下以便观察或保存帧 % pause(0.01); % 简单终止条件如果没有树木在燃烧可以提前结束可选 if ~any(forest 2, all) fprintf(在第 %d 步火焰已全部熄灭。\n, t); break; end end代码关键点解析与避坑指南状态更新策略注意我们创建了forest_new作为新网格。所有元胞必须基于旧网格forest的状态来更新到forest_new。如果直接在forest上更新那么元胞的更新顺序就会影响结果先更新的元胞的新状态会影响到后更新的元胞对邻居的判断这就违反了CA“同步更新”的核心原则。这是新手最容易犯的错误之一。邻居索引与周期边界mod([...]-1, grid_size)1这个写法是处理周期边界的简洁技巧。它确保了网格的上下边缘和左右边缘是连接在一起的。例如对于最上面一行i1它的“上方”邻居就是最下面一行igrid_size。这种处理在模拟无限大或周期性空间时非常有用。性能考量上述代码使用了双重for循环遍历网格在MATLAB中对于大网格如500x500以上可能会较慢。一个常见的优化技巧是使用矩阵运算向量化来替代循环。例如可以用conv2函数快速计算每个位置周围燃烧邻居的数量。这里为了代码清晰易懂我们保留了循环版本。在实际数学建模竞赛中如果时间步数多、网格大务必考虑向量化优化。可视化技巧imagesc配合colormap可以快速将状态矩阵可视化为图像。drawnow命令强制刷新图形形成动画。调整pause中的时间可以控制动画速度。3.3 参数影响分析与实验设计运行上述代码你会看到火焰如何吞噬森林以及之后森林如何慢慢再生。你可以通过调整参数来研究不同场景p_grow的影响增大p_grow森林再生更快可能导致火灾更频繁但规模较小。p_ignite的影响增大p_ignite火势蔓延极快可能瞬间烧光整片森林形成“临界相变”。p_lightning的影响它决定了火灾的内生随机性。即使没有外部火源森林也可能因雷击而自发燃烧。一个有趣的建模问题是是否存在一个临界森林密度低于这个密度火灾无法蔓延开高于这个密度一点火星就能引发燎原大火。你可以设计实验固定p_ignite和p_lightning逐步改变初始的p_init_tree或动态的p_grow观察火灾燃烧面积占总森林面积的比例随密度的变化。这可以帮助理解真实生态系统中火灾的传播机制。4. 经典案例二交通流模拟NaSch模型Python实现交通流是CA应用的另一个经典领域。Nagel-Schreckenberg模型是一个简单而强大的模型它能再现真实交通中的拥堵形成、传播和消散。4.1 NaSch模型规则详解我们模拟一条单车道、离散化的环形道路。每个元胞代表一小段路状态为“空”或被一辆车占据。每辆车有一个速度0到v_max之间的整数。规则在每个时间步按顺序执行以下四步加速如果车速未达到最高限速v_max则车速加1。这反映了司机倾向于加速到期望速度。减速如果前方d个元胞内有车d为当前车速为了避免碰撞将车速减至d-1。这模拟了安全距离。随机慢化以概率p_slow将车速减1如果车速0。这模拟了驾驶行为中的不确定性如分心、路况微小变化等。移动车辆根据更新后的速度向前移动相应的元胞数。4.2 Python面向对象实现我们将用Python的面向对象方式实现使代码结构更清晰易于扩展如变为多车道。import numpy as np import matplotlib.pyplot as plt from matplotlib.animation import FuncAnimation class NaSchModel: Nagel-Schreckenberg 交通流元胞自动机模型 def __init__(self, road_length100, density0.2, v_max5, p_slow0.3, steps200): 初始化模型参数 :param road_length: 道路长度元胞数 :param density: 车辆密度车辆数/道路长度 :param v_max: 最大速度 :param p_slow: 随机慢化概率 :param steps: 模拟时间步数 self.road_length road_length self.density density self.v_max v_max self.p_slow p_slow self.steps steps # 初始化道路-1表示空非负整数表示车辆的速度 self.road np.full(road_length, -1, dtypeint) # 初始化车辆位置和速度 self._init_vehicles() # 用于记录每一时间步的道路状态用于后续分析和可视化 self.history [] def _init_vehicles(self): 根据密度随机放置车辆并初始化速度为0 num_cars int(self.road_length * self.density) # 随机选择车辆位置确保不重叠 positions np.random.choice(self.road_length, sizenum_cars, replaceFalse) for pos in positions: # 初始速度可以设为0也可以随机设为0到v_max之间 self.road[pos] 0 # 初始速度设为0 def _get_distance_to_next_car(self, current_pos): 计算从当前位置到下一辆车之间的距离以元胞计 注意是环形道路。 distance 1 # 从下一个位置开始循环查找 while self.road[(current_pos distance) % self.road_length] -1: distance 1 # 安全措施防止无限循环理论上不会因为密度0 if distance self.road_length: return self.road_length # 如果没有车返回道路长度 return distance def update(self): 执行一个时间步的更新 new_road np.full_like(self.road, -1) # 创建新的空道路 # 第一步遍历当前道路上的所有车辆计算它们的新速度 # 注意我们需要先基于当前道路状态计算所有车辆的新状态再统一移动 # 这里用一个临时列表存储(位置, 新速度) car_updates [] for i in range(self.road_length): if self.road[i] ! -1: # 当前位置有车 v_current self.road[i] # 1. 加速 v min(v_current 1, self.v_max) # 2. 减速避免碰撞 d self._get_distance_to_next_car(i) v min(v, d - 1) # 3. 随机慢化 if v 0 and np.random.random() self.p_slow: v - 1 car_updates.append((i, v)) # 第二步根据计算出的新速度将车辆移动到新道路上 for old_pos, new_v in car_updates: new_pos (old_pos new_v) % self.road_length # 检查新位置是否已被占据理论上不应该因为减速规则保证了安全距离 if new_road[new_pos] ! -1: # 如果发生碰撞通常意味着规则实现有误或边界情况这里简单处理不移动 # 在实际严谨模型中应避免此情况发生 new_pos old_pos new_v 0 # 发生碰撞速度降为0 new_road[new_pos] new_v self.road new_road return self.road def run_simulation(self, record_historyTrue): 运行完整模拟 if record_history: self.history.append(self.road.copy()) for _ in range(self.steps): self.update() if record_history: self.history.append(self.road.copy()) return np.array(self.history) def calculate_flow(self, historyNone): 计算交通流量单位时间通过某点的车辆数 简单估算平均速度 * 密度 if history is None: history self.history if not history: return 0 history_array np.array(history) # 找出所有有车的位置速度0 car_speeds history_array[history_array 0] if len(car_speeds) 0: return 0 avg_speed np.mean(car_speeds) flow avg_speed * self.density return flow # 模拟与可视化 if __name__ __main__: # 参数设置 road_len 200 v_max 5 p_slow 0.3 sim_steps 300 # 创建模型实例并运行 model NaSchModel(road_lengthroad_len, density0.15, v_maxv_max, p_slowp_slow, stepssim_steps) history model.run_simulation() # 1. 时空图可视化横轴-空间纵轴-时间 plt.figure(figsize(12, 6)) # 将历史数据转换为二维图像有车为1空为0 spacetime (np.array(history) 0).astype(int) plt.imshow(spacetime, cmapbinary, aspectauto, interpolationnone) plt.xlabel(Road Position (Cell)) plt.ylabel(Time Step) plt.title(fNaSch Model Traffic Flow (Density{model.density}, p_slow{p_slow})) plt.colorbar(labelVehicle (1present)) plt.tight_layout() # 2. 动态流量图可选绘制流量随密度的变化基本图 print(计算不同密度下的流量...) densities np.linspace(0.05, 0.9, 18) # 测试一系列密度 flows [] for d in densities: # 对每个密度运行一次模拟并计算平均流量 # 注意为了准确通常需要忽略初始瞬态取后段时间的平均 m NaSchModel(road_lengthroad_len, densityd, v_maxv_max, p_slowp_slow, steps200) m.run_simulation(record_historyTrue) # 取后100步计算平均流量避免初始状态影响 late_history m.history[100:] avg_speed np.mean([np.mean(step[step0]) for step in late_history if np.any(step0)]) flow avg_speed * d if avg_speed 0 else 0 flows.append(flow) print(f 密度 {d:.2f} - 流量 {flow:.4f}) plt.figure() plt.plot(densities, flows, o-, linewidth2) plt.xlabel(Vehicle Density) plt.ylabel(Flow (vehicles/time step)) plt.title(Fundamental Diagram of NaSch Model) plt.grid(True, alpha0.3) plt.tight_layout() plt.show()实现细节与心得更新顺序的重要性NaSch模型的四步规则必须按顺序执行且所有车辆的“加速-减速-慢化”计算必须基于同一时刻的道路状态。这就是为什么我们在update方法中先遍历所有车辆计算出它们的新速度存储在car_updates列表然后再统一移动。如果边计算边移动就会造成顺序更新错误。环形边界与距离计算_get_distance_to_next_car函数中的(current_pos distance) % self.road_length实现了环形道路。这是交通流模拟的常见设定避免了边界效应让你可以研究稳定状态下的交通特性。“基本图”分析代码第二部分计算了流量-密度关系图这是交通流理论的核心。你会发现随着密度增加流量先增加后减少存在一个最优密度使得流量最大。密度过高时拥堵导致流量下降。这个图是评价模型是否合理的关键。性能与扩展这个Python实现使用了列表存储更新对于中等规模模拟是清晰的。对于大规模仿真如万辆车可以考虑用NumPy的向量化操作来加速例如用np.roll等函数来高效计算车距。此外这个模型可以扩展为多车道规则需要增加换道逻辑。5. 进阶应用与模型优化掌握了两个基础案例后我们可以探讨更复杂的应用和优化技巧。5.1 扩展模型SEIR传染病模型元胞自动机非常适合模拟传染病的空间传播。我们可以构建一个SEIR模型S (易感者)可能被感染的健康个体。E (潜伏者)已感染但未发病、无传染性的个体。I (感染者)已发病且有传染性的个体。R (康复者)已康复并获得免疫力的个体。规则可以设计为S - E易感者若与感染者相邻摩尔邻居则以概率beta被感染进入潜伏期。E - I潜伏者经过固定的latent_period时间步后变为感染者。I - R感染者经过固定的infectious_period时间步后以概率gamma康复或死亡移出。R - S康复者经过immune_period时间步后可能再次变为易感者如果考虑免疫力减弱。通过调整参数beta,latent_period等和初始感染源位置可以模拟疫情在不同地理环境下的扩散过程直观展示隔离、封控等策略的效果。5.2 性能优化技巧以MATLAB为例当网格很大如1000x1000或时间步很长时双重循环会成为性能瓶颈。以下是一些优化策略策略一向量化与卷积运算对于规则只依赖于邻居状态的CA卷积是利器。例如在森林火灾模型中“计算每个位置周围燃烧邻居的数量”可以用卷积核来实现。% 定义燃烧状态为2 burning_cells (forest 2); % 定义一个3x3的卷积核中心为0周围8个为1用于计算8邻域和 kernel [1 1 1; 1 0 1; 1 1 1]; % 使用conv2计算每个位置周围燃烧邻居的数量same保持输出大小相同 burning_neighbors conv2(burning_cells, kernel, same); % 注意conv2默认使用0填充边界对于周期边界需要特殊处理可以使用imfilter并指定‘circular’选项 % burning_neighbors imfilter(burning_cells, kernel, circular, conv);然后基于burning_neighbors矩阵和forest矩阵可以利用逻辑索引进行批量更新完全避免循环。策略二使用find和索引操作对于状态种类不多的CA可以分别处理每一类元胞。% 找出所有燃烧细胞的位置 [burn_i, burn_j] find(forest 2); % 在下一时间步这些位置变为空地 forest_new(burn_i, burn_j) 0; % 找出所有树木细胞的位置 [tree_i, tree_j] find(forest 1); % 对于每个树木细胞判断其邻居需要一些技巧来高效获取邻居状态 % ... 此处可以结合策略一的卷积结果进行判断策略三编写MEX函数对于极度复杂的规则或对性能有极致要求可以用C/C编写核心更新函数编译成MEX文件供MATLAB调用。这是最高效但也是最复杂的方法。实操心得在数学建模竞赛中时间有限。优先保证代码的正确性和可读性。如果模拟规模不大清晰的循环代码比晦涩难懂的向量化代码更可取。只有当性能确实成为问题时才进行优化。通常先用循环实现一个正确的小规模版本验证模型逻辑然后再考虑优化。6. MATLAB与Python实现对比与选型建议通过上面的案例你应该对两种语言的实现风格有了体会。这里做个总结对比特性MATLABPython (NumPy/SciPy)语法与易用性矩阵操作语法极其自然内置函数丰富特别适合快速原型验证和算法开发。绘图函数强大且简单。语法更通用库生态系统庞大。通过NumPy也能进行高效的矩阵运算但需要一点学习成本。性能对于向量化和矩阵运算底层由高度优化的线性代数库支持通常很快。循环性能在新版MATLAB中已有很大改善但仍不如向量化。NumPy的向量化运算性能与MATLAB相当甚至更优。对于复杂逻辑的循环纯Python较慢但可使用Numba加速或换用Cython。可视化绘图函数集成度高生成出版级图表非常方便交互式工具丰富。Matplotlib功能强大且高度可定制但API相对复杂。Seaborn等库让统计绘图更美观。部署与成本商业软件授权费用高。打包独立应用相对方便。完全免费开源部署简单跨平台支持极好。社区与生态学术和工程领域用户多有大量专业工具箱。社区相对封闭。拥有全球最大、最活跃的开源社区任何领域几乎都能找到相关库。在数模中的角色绝对主力。国赛、美赛官方推荐/常用软件其快速建模、仿真、绘图一体化的工作流无可替代。强大辅助与扩展。适合需要复杂数据处理、机器学习、网络爬虫等MATLAB不擅长的环节。混合使用效果佳。给数学建模参赛者的建议核心建模与仿真首选MATLAB它的Simulink、各种工具箱优化、统计、图像处理以及本文演示的CA建模都能在MATLAB环境中流畅完成。学习成本低出图快。复杂数据处理或算法调用可辅以Python如果你的模型需要调用最新的深度学习模型TensorFlow/PyTorch或者需要从网上爬取大量数据那么用Python做这部分工作再将处理好的数据导入MATLAB进行建模和可视化是一个高效的策略。团队协作确保团队里至少有一人熟练掌握MATLAB一人熟悉Python。可以分工合作发挥各自优势。7. 常见问题与调试技巧在实现CA模型时你肯定会遇到各种问题。以下是一些常见坑点及解决方法模拟结果与预期不符或者出现“爆炸”/“湮灭”等异常检查边界条件这是最常见的问题之一。确认你使用的是固定、周期还是吸收边界并在代码中正确实现。周期边界下要确保索引计算正确使用mod运算。检查状态更新顺序务必使用“双缓冲”。即基于旧网格状态计算新网格状态全部计算完成后再用新网格替换旧网格。在任何情况下都不要边遍历边修改当前网格。检查规则逻辑特别是条件判断中的“大于”、“小于”、“等于”是否写错概率判断rand() p的方向是否正确。打印中间状态在关键时间步将网格状态打印出来或可视化看是否符合每一步的规则预期。从小网格如5x5开始调试。程序运行速度太慢向量化如前所述将基于邻居的规则用卷积 (conv2,imfilter) 或移位操作 (circshift) 实现。减少可视化频率不要在每一个时间步都更新图形。可以每10步或100步更新一次或者先计算完所有时间步的数据最后再生成动画。使用更高效的数据类型在MATLAB中对于只有0/1状态的网格使用logical类型比double更节省内存和计算资源。预分配数组在循环前预先分配好存储历史状态的数组如history zeros(grid_size, grid_size, n_steps)避免在循环中动态增长数组。如何将CA模型与数学建模竞赛结合明确问题对应CA适合研究具有局部相互作用和时空演化特性的问题。在选题时判断你的问题是否具备这些特征如人群疏散、谣言传播、生态竞争、城市规划。参数标定与验证CA模型中的概率参数如p_ignite,p_slow不是随意设定的。需要查阅文献或利用真实数据如历史交通流量、火灾统计数据进行参数标定使模型输出与实际观测相符。设计对照实验这是体现建模思想的关键。例如在交通流模型中对比不同p_slow司机行为随机性对整体通行效率的影响在传染病模型中对比“不干预”、“隔离感染者”、“限制移动”等不同策略下的疫情曲线。用清晰的图表展示结果。分析涌现现象不要只满足于运行出动画。要分析结果例如计算系统的序参量如交通流中的平均速度、森林火灾中的燃烧面积比例随时间的变化研究其相变行为并用统计方法进行分析。最后元胞自动机的魅力在于其“简单规则产生复杂行为”的哲学。它可能无法像微分方程那样给出精确的解析解但它提供了理解复杂系统动态的一种直观、可计算、且充满洞察力的视角。从“生命游戏”到模拟城市其边界只取决于你的想象力。希望这份指南能成为你探索这个奇妙世界的起点。在实际动手时记住从最简单的规则和最小的网格开始逐步增加复杂性并享受观察那些意想不到的图案在你代码中涌现出来的乐趣。
返回列表