ARTICLE DETAIL

资讯详情

深耕编程入门与网站建设的一线实战洞察。

豹纹和斑马纹是怎么长出来的:调两个参数,花纹自己浮现

豹纹和斑马纹是怎么长出来的:调两个参数,花纹自己浮现 同一套化学反应为什么豹子长的是斑、斑马长的是条纹1952 年图灵给过一个反直觉的答案两种化学物质只要扩散快慢不一样本来均匀的状态就会自己失稳长出斑图Turing pattern。扩散在直觉里是「抹平差异」的在这里却反过来制造了差异。静态图回答不了两个问题为什么加了扩散反而更不均匀为什么 F、k 挪一点点花纹就从点变成条纹、再坍成一片所以我把这件事做成一个网页左边一块 553×387 的反应扩散场在跑右边五个滑杆六个经典预设鼠标拖过去就能播种点一下「质量守恒自检」还会把纯扩散下 ΣU 的变化量算给你看。几条关键信息先摆出来源码Demo Park 公开仓库 → https://atomgit.com/deli007/demo_park/tree/main/codearts-turing-patternsindex.html可直接下载生成物单个index.html纯前端、零依赖、不联网、不上传全部计算在本地完成本地运行双击文件即可或python -m http.server 8000本地验证页面自检里 U 总量相对变化 4.071e-10阈值 1e-61920×1200 视口下网格是 553×387 214011 个格点它不是严谨的化学仿真。U、V 只是两个抽象浓度反应项是最简的u·v²参数平面固定用 Du0.2097、Dv0.105 这一档预设按钮上的名字和实际长出来的花纹有几处对不上下面会如实写。为什么值得看扩散本来是抹平差异的为什么反而造出了花纹把两种东西放在一起直觉是「时间一长就混匀了」。图灵发现这个直觉有个前提两种东西的扩散速度一样快。一旦快慢不同均匀态就会被扩散本身推翻。这套网页跑的 Gray-Scott 模型就两条式子U 是「被消耗的原料」V 是「自我催化的产物」U/t Du·∇²U − U·V² F·(1 − U) V/t Dv·∇²V U·V² − (F k)·VU·V²V 会吃掉 U、同时复制自己——这是短程的「激活」Du·∇²U和Dv·∇²VU 扩散得比 V 快Du0.2097Dv0.105差两倍相当于长程的「抑制」F喂食率往系统里补 Uk死亡率把 V 带走。这两个数就是右边面板上那两个滑杆。短程激活、长程抑制这两件事凑齐均匀态就待不住了某个点的 V 稍微多一点它会就地扩张但扩张不出多远就被 U 的扩散压住于是「多」和「少」被固定成一定间距的花纹间距由扩散长度决定而不是由随机种子决定图 1 里几千个点的间距几乎一致就是这件事的直接后果。页面实现里有三个决定值得说清楚9 点拉普拉斯核要量纲对齐。用的是0.5×正交邻居 0.25×对角邻居 − 3×中心。把权重代进 Taylor 展开长波极限正好是∇²正交项贡献w_orth对角项贡献2·w_diag0.5 2×0.25 1.0。生成这页时它一开始用的是一套0.2/0.05/−1的核同一套展开算下来只有0.2 2×0.05 0.3等于把扩散系数悄悄乘了 0.3系统落进非图灵区跑 16 万步也不出花纹。换成量纲匹配的核之后经典花纹才复现。边界用零通量、全域积分。越界邻居直接取自身格点值Neumann每个格点都参与积分。这样核的权重和为 0对称求和后总质量严格不漂移——页面自检量的就是这件事。换预设先分帧热身。切参数时不是瞬间跳到终态而是先分帧跑一段每帧 600 步用requestAnimationFrame让出主线程所以切预设有连续演化的过程也不会卡白屏。也说几处真实的边界网格是按视口自动缩放的1920×1200 下是 553×387窗口变小格子会变多。同一组参数在不同分辨率下稳态覆盖率会有零点几个百分点的差别同一参数多次重跑来来回回稳定在 11.0%~11.2%。颜色只是 V 浓度的分段映射深蓝 → 青 → 耀白不是物理量。边界上美术上「能看」不代表这套离散化在别的 Du/Dv 组合下都稳定显式欧拉 dt1 在扩散系数更大时会炸所以才把滑杆的 Du 上限压到 0.5。任务描述需求是直接粘进码道 Web 输入框的原文如下请用单个 index.html 文件内联 CSS 和 JS不依赖任何外部库、不发任何网络请求做一个【图灵斑图反应扩散实验室】网页应用深色科技风、全屏自适应、左侧画布右侧控制面板。要求1) 用 Gray-Scott 反应扩散模型在 Canvas 上实时模拟化学物质 U、V 的浓度场9 点拉普拉斯算子 显式欧拉积分U 初始 1、V 初始 0随机撒若干种子点2) 控制面板提供滑杆调 F(feed 0.010~0.100)、k(kill 0.030~0.080)、Du、Dv、每帧迭代步数并提供 6 个经典预设按钮F0.035 k0.065 珊瑚迷宫、F0.054 k0.062 斑点、F0.098 k0.055 自复制点、F0.030 k0.062 稳定条纹、F0.026 k0.051 混沌、F0.014 k0.054 大斑块每个预设按钮上标注它会形成哪种花纹3) 鼠标或触摸在画布上拖动即可播种 V画笔半径可调空格暂停/继续R 键重置4) 顶部状态条实时显示迭代步数、斑图覆盖率(%)、U 总量、V 总量、当前 F 和 k5) 内置【自检】按钮临时把 F 和 k 都设为 0 跑 200 步验证纯扩散下 U 总量相对变化小于 1e-6质量守恒再恢复原参数并把这次自检的实测数值用文字显示在面板上6) 输入框校验F 或 k 输入超出范围或非数字时在输入框下方用中文红字提示「请输入 0.010 ~ 0.100 之间的数字当前保留上一次的有效值」并保留上一次的有效值继续运行不要卡住7) 页面底部固定一行小字说明本工具纯本地运行无联网、无上传、无第三方依赖。它实际长成了什么样六个预设的实测面板上给了六个经典预设我把每个都跑到稳态、记下顶部状态条的实测覆盖率和画面预设按钮名Fk跑到步数覆盖率实际画面珊瑚迷宫0.0350.0653570411.16%规则点阵图 1斑点0.0540.0624952435.46%迷宫条纹图 2稳定条纹0.0300.0624314012.35%圆点大斑块0.0140.054528761.29%大小不一的稀疏斑点图 3混沌0.0260.051431321.59%大片弥散、几乎没有结构自复制点0.0980.05542532100.00%全场饱和成均匀色块六个名字里只有「大斑块」基本对得上。这是真实偏差不是我写错了这套 Du0.2097 / Dv0.105 的参数和公开实现常用的一档Du0.16 / Dv0.08 附近不同参数平面的位置整体平移了所以同一个 (F, k) 落到的花纹区跟着变。「自复制点」那一支最明显——它直接跑到 100.00% 覆盖也就是整个场被 V 铺满、没有点也没有条纹只能当「饱和态」的演示用。我保留了按钮上的原名改了名字就和这张表对不上了但把偏差点写在这里。踩坑两个真 bug和把它们抓出来的证据第一条最难发现因为它不影响数值、只影响画面。画布缓冲区没跟着窗口尺寸走。index.html里画布铺满窗口是用 CSS 撑的width:100%但代码里只改了 CSS 尺寸没有同步canvas.width/height——它一直停在 HTML 默认的 300×150。后果是模拟照样在全场 553×387 上跑、状态条数字全对可是drawImage的目标矩形是 1602×1120超出 300×150 的部分被丢掉实际能看到的只有左上角 103×52 个格点占全场 2.5%再被 CSS 横向拉 5.34 倍、纵向拉 7.47 倍。我用eval把几何打出来才抓到canvas.width300、canvas.clientWidth1602在画布中间 (800,600) 读像素是(0,0,0,0)——那里根本没有画布。修法是在rebuildGrid()里syncDisplaySize()之后补上两行canvas.width Math.round(dispW); canvas.height Math.round(dispH);。同一个参数改动前后差别极大改之前画面是几个被放大到糊掉的大团块改之后才是图 1 那种满屏细点。两个提示组件共用了一个类名。舞台底部那条操作提示和每个输入框下面的校验红字都叫.hint样式表里先定义.hint绝对定位、white-space:nowrap再定义一次.hintdisplay:none。结果是两个都坏舞台提示被后一条规则永久隐藏从来不会显示校验红字反过来继承了绝对定位和nowrap从输入框下面跑到控件左侧、被画布裁掉——非法输入时只能看到后半句「…当前保留上一次的有效值」。修法是把舞台提示改成#stageHint选择器元素上也不再挂hint类校验提示保留.hint。修完之后红字在面板里正常换行实测position:static、宽 289 px、高 32 px 两行。失败路径也照样验了。F 框里敲0.5超出 0.010~0.100k 框里敲abc不是数字红字提示分别是「请输入 0.010 ~ 0.100 之间的数字当前保留上一次的有效值」和「请输入 0.030 ~ 0.080 之间的数字当前保留上一次的有效值」两个输入框都回退到上一次的有效值当时是 0.035 / 0.065画面继续跑、不白屏、不报错。准备环境进入码道 Web浏览器打开码道 Web 版https://devcloud.cn-north-4.huaweicloud.com/chat?sourcedmzntgwsfsourceaddmzntgwwbwz登录后就能在对话窗口输入需求不需要装软件。码道有三种使用方式WebUI浏览器对话、TUI终端命令行和桌面 IDEIDE 插件。本文用 WebUI 版演示。本地复现生成物只有一个index.html复现没有依赖从 Demo Park 本案例目录下载index.html。双击打开页面默认是 F0.054、k0.062会自动热身 1800 步再进入实时迭代。想走静态服务也行python -m http.server 8000然后访问http://localhost:8000。点「质量守恒自检」应看到「✓ 通过 · 质量守恒成立」和一行U 总量相对变化 x.xxxe-10阈值 1e-6。想看清花纹的稳态把「每帧迭代步」拉到 16跑一两分钟覆盖率就不动了。下面这 5 项断言是我本地逐条跑出来的都能自己复验自检Fk0 跑 200 步纯扩散ΣU 从 119633.26 变成 119633.26相对变化 4.071e-10另几次重跑在 1.6e-10 ~ 5.5e-10量级一致阈值是 1e-6。覆盖率达到稳态F0.035、k0.065 跑到 35000 步后覆盖率在 11.0%~11.2% 之间不再上涨。网格规模1920×1200 视口下 W×H 553×387 214011 个格点状态条的 ΣUΣV 与逐格点求和一致。画布缓冲区 CSS 尺寸修复后canvas.width/height是 1602×1120与clientWidth/clientHeight相等画布正中读像素不再返回透明。非法输入F 填0.5、k 填abc两条红字提示都出现且模型继续用旧值运行。使用码道体会这轮下来能带走的主要是几条和模型打交道的习惯需求里把「物理量」和「量纲」写清楚。只写「用拉普拉斯算子」它可能给你一个系数不归一化的核这次踩到的那套0.2/0.05/−1核就是把有效扩散率乘了 0.3。写上「9 点核 显式欧拉、参数范围」之后它自己会去对照公开实现校准 Du/Dv。数值类的 bug 要它自己给「可读的数字」。需求里要求状态条实时显示迭代步数、覆盖率、ΣU、ΣV还要求自检把前后 ΣU 都打印出来——这两个要求让「图不对」和「数不对」能分开定位。没有状态条我大概只会看到一张糊图然后以为是自己拖错了滑杆。视觉 bug 得自己截图看。画布缓冲区那类问题代码逻辑完全正确、控制台一个错都不报只有把窗口尺寸和画布尺寸打出来、再读一次像素才现形。它给自己的结论是「已在本地用 Chromium 完成全部自动化验证」但自动化验证的是数值不是「画面是不是只画了 2.5%」。参数区的名字要留个心眼。「斑点」「稳定条纹」这类名字是它按公开资料的印象写的落到自己这套扩散系数上不一定成立验收时最好把每个预设都跑一遍、记下覆盖率名字对不上的地方在文档里说明别顺手改按钮名——改了反而和截图对不上。失败路径单独写一段。需求里写「给明确中文提示并保留上一次的有效值」它照做了不写默认往往是什么都不发生。Agent 说完成不算验收。自检那行数字是页面里点出来的非法输入是我自己敲的画布尺寸是我自己量的——这三样都对上了才敢在上面写「11.16%」这种具体数字。亲手点三下页面打开就能验三下够了看数字点「质量守恒自检」确认出现「✓ 通过 · 质量守恒成立」并顺手核一眼迭代步数、覆盖率、ΣU 在动。换参数点预设「斑点」等一分钟看它从种子长成迷宫条纹覆盖率会从个位数爬到 35% 左右再把「每帧迭代步」拉到 16演化明显变快。看失败路径F 框里敲0.5、k 框里敲abc必须看到两条中文红字提示、输入框退回上一次的有效值页面不白屏。直接下载试玩Demo Park 仓库https://atomgit.com/deli007/demo_park本案例目录https://atomgit.com/deli007/demo_park/tree/main/codearts-turing-patterns总结把「豹纹怎么来的」做成一个能拖的网页之后最关键的不是渲染也不是配色而是那两条式子里的Du ≠ Dv一旦两种东西扩散得一样快短程激活和长程抑制就分不开花纹立刻消失。想接着玩可以试试把 Du/Dv 做成滑杆扫一遍Du 调到接近 Dv 时花纹会整体退掉、把网格换成六边形、或者给 V 加一个缓慢的全局衰减看图案怎么在「点—条纹—斑块」之间切换。
返回列表