向日葵籽为什么按 137.5° 转着长?phyllotaxis 黄金角模型 5000 颗种子实测

发布时间:2026/10/11 10:50:08
向日葵籽为什么按 137.5° 转着长?phyllotaxis 黄金角模型 5000 颗种子实测 一颗种子占一个位置第 n 颗的角度是n × α半径是R × √(n/N)。规则就这一行喂进去的角度α不同屏幕上长出来的东西完全不同。用真的黄金角137.50776405003785°、5000 颗种子实测重复方向 0 条3000 以内找不到任何角度周期最近邻最小间距9.97 px、平均10.56 px、最大10.97 px角向聚集度1.081° 粒度下峰值/均值1.00 就是绝对均匀最近邻步长众数Δn 14455% 的种子把第 144 号邻居当成最近的页面 13 条自检13/13 通过全量重算20.8 ms。然后很多人记的那个数——137.5°填进去同一份代码立刻翻车屏幕上冒出144 条清清楚楚的辐条图 2。原因是小学算术137.5/360 约分完是55/144转 144 次角度回到原点于是种子只能落在 144 个方向上。再换成137.4°是 600 条137.6°是 225 条——137.4/360 229/600137.6/360 86/225。一句话你能写出几位小数的角度它的分母有多大辐条就有多少条只有写不出分母的角度才真的没有辐条。公开仓库https://atomgit.com/deli007/demo_park本案例目录https://atomgit.com/deli007/demo_park/tree/main/codearts-phyllotaxis-golden-angle。成品是一个index.html双击就能跑不联网、不引第三方库、没有构建步骤。文中所有读数都是我把交付的页面落到本机、自己重新跑出来的不是转述对话里的结论。边界先说清楚这是个教学/演示用的玩具实验室。它画的是理想模型——种子当成等大的点、半径严格按 √n 增长、不考虑生长过程中花盘变大它也不是植物学结论向日葵真实的叶序会受品种、光照和生长激素影响。另外 3000 这个周期搜索上限是我自己定的理论上存在分母大于 3000 的角度比如 137.3° 的真实分母是 3600所以页面上「无≤3000」只表示在 3000 以内找不到不表示无理数。一、先看结果只改了一个角度画面就换了一个物种同一份代码、同样的 5000 颗种子和 440 px 花盘半径只动αα角度周期 q重复方向数辐条最小间距平均间距最大间距角向聚集度页面自检137.50776405003785°真黄金角无≤300009.97 px10.56 px10.97 px1.0813/13137.5°1441446.38 px8.74 px11.32 px2.5213/13137.6°225225————13/13137.4°600600————13/13360×13/34 137.6470588235°34341.50 px2.74 px10.85 px10.6612/13137.4° 和 137.6° 两行的间距空着是因为它们的重复方向数和分母同一量级要把种子数开到 2000 以上才铺得满我把这一组单独放在 N2000 的表里量后文有数。最小间距那一列最直观真黄金角 9.97 px13/34 只有 1.50 px——差了 6.6 倍而花盘半径是一样的种子的总数量也是一样的。图上更直观13/34 的版本就是 34 根粗射线中间稀稀拉拉二、为什么值得看黄金角不是玄学是「最难逼近」的那个角度向日葵、松果、菠萝的种子螺旋为什么都长成两族斜着的螺旋线这个问题从开普勒那会儿就被问过。解释它的关键不是黄金比例好看而是一个纯数论性质黄金角是最难被有理数逼近的角度。把α/360写成分数分母越小转不了几次就回原点种子就挤在几条射线上图 2 的 144 条就是这么来的。反过来如果α/360是无理数角度永远不重复种子会被摊得越来越开——但摊得开也有高低。黄金比例 φ 的连分数是[1;1,1,1,…]全是 1是所有数里收敛最慢的所以它的分数近似最差剩下的空隙最均匀。这就是 137.50776405003785° 能长出图 1 那种花盘的原因也是这个页面唯一想讲清的一件事把α写成有限位小数你就给了它一个分母屏幕上就一定会有辐条分母越大辐条越细144 条已经很难看出是辐条、间距越接近理想值而真黄金角在 3000 以内压根没有分母所以没有辐条。页面右侧的「Fibonacci 收敛表」就是按这条思路排的5/13、8/21、13/34、21/55、34/89、55/144横着一排看下去最小间距从 2.87 px 一路涨到 21.55 px同时辐条数从 13 涨到 144也就是角度越来越细。另一边「十进制角度的隐藏分母」表更直接α约分后的分数周期 q≤3000实测辐条数137.3°1373/3600无30000137.4°229/600600600137.5°55/144144144137.6°86/225225225137.50776405003785°无无理近似无≤30000这张表在页面上是自动算的把角度对 360 取模、量化到小数点后 6 位统计每个方向出现了几次出现 2 次以上的方向就算一条辐条周期 q 是在 1~3000 里找最小的、能让q × α落到 360 整数倍上的那个数。三、实验室长什么样4 个参数、5 个指标、13 条自检参数只有四个发散角α90°~180°可输到小数点后 6 位、种子数N1~20000、花盘半径R、尺寸系数种子点半径 系数 ×R/√N所以种子永远是刚好挨着不重叠外加一个螺旋连线开关能把第 n 颗和第 n1 颗连起来看螺旋线怎么绕。指标里有两个是这篇的核心重复方向数辐条把 5000 个角度量化到 1e-6 之后去重出现 2 次以上的方向有几条。真黄金角是 0137.5° 是 144。角向聚集度把 360° 切成 360 个 1° 的格子最大格子里的种子数 ÷ 平均每格种子数。1.00 完全均匀黄金角 1.08137.5° 是 2.5213/34 是 10.66。这个指标的好处是没有阈值、不需要调参直接就是一个看起来有多像辐条的数。另外三个是最近邻最小/平均/最大间距网格分桶算出来的以及最近邻步长众数Δn——就是每个种子的最近邻居是第几号。黄金角 5000 颗时 Δn 的众数是144占 55%次家族是89N20000 时是 233 和 377。这几个数都是斐波那契数列里的项和向日葵上看得见的斜列数是同一回事算是这个页面顺手给出的一点真实结构。13 条自检全部渲染在页面上每条都带实测值/期望值图 4。我特意把它们设计成任何参数下都能失败默认参数下 13/13 全绿把角度滑到 13/34第 8 条「平均最近邻间距 ≥ 0.5×2R/√N」会当场变红2.74 px 6.22 px。四、给码道提的需求原文可直接复制用单个 index.html原生 JS Canvas不引第三方库、不做构建做一个「叶序 phyllotaxis 实验室」 把向日葵花盘里种子按黄金角螺旋排列的现象做成可交互的可视化工具界面全中文。 核心模型第 n 颗种子角度 θ n × α半径 r R × √(n/N)α 默认 360/φ² 并在 90°~180° 可调。 实时指标最近邻最小/平均距离、角向聚集度、检测到的径向辐条数、可见斜列parastichy条数 实测值与理论值并排显示、给出相对误差。 自检面板至少 12 条断言每条渲染「名称/实测值/期望值/通过与否」不许出现恒真的 pass: true 断言 断言里要包含 360×(1-1/φ) 与 360/φ² 的差 1e-9、α137.5° 且 N≥1000 时辐条数应为 144、 换成真黄金角后辐条数应 ≤ 8、绘制种子数等于输入 N、非法输入会回退。 Fibonacci 收敛表α 依次取 360×5/13、8/21、13/34、21/55、34/89、55/144逐行显示分母、实测辐条数。 健壮性种子数 1~20000 的整数、发散角 90~180 的有限数非法输入给中文提示并回退上一次有效值不白屏。 性能N5000 时重算重绘在 100ms 以内页面显示重算耗时。 完成后打开预览让我直接看到运行效果。需求里有三句话最值钱后面都验证到了「不许出现恒真的 pass: true 断言」交付版里真有两处、「实测值与理论值并排、给出相对误差」让我一眼看出哪个指标不靠谱、「非法输入给中文提示并回退」失败路径截图就靠它。五、验证与踩坑我改掉的四处1辐条检测用「阈值 连续区间」数错了 18 倍。交付版的算法是把 360° 分成 720 格统计密度超过均值 1.5 倍的格子算在辐条上再要求连续 2 格以上才计一条。实测同一组参数α137.5° 报8 条144 条才是对的α90° 报 3 条应该是 4 条α120° 报 2 条应该是 3 条。两个毛病叠在一起——辐条只有 0.5° 宽、占不满 2 格而 90° 那种粗辐条又会把相邻格算成一条。改成量化到 1e-6 去重后统计重复方向之后13/34 → 34 条、55/144 → 144 条、90° → 4 条全部和分母对得上。2「角向均匀度 CV」这个指标本身不成立。交付版用 720 个格子算变异系数可 500 颗种子摊到 720 个格子里大多数格子是 0、少数是 1CV 直接被格子粒度支配——默认参数下它是 0.7373而交付版自己的断言写着「CV 0.5」于是一打开页面就有一条断言是红的。这不是 bug 是度量设计错了种子数少于格子数时CV 量的根本不是均匀不均匀而是格子有多粗。换成峰值/均值之后黄金角 1.08、137.5° 2.52、13/34 10.66同一个尺度下可比也不再需要拍一个阈值。指标交付版实测重做后说明α137.5° 的辐条数8144应等于 55/144 的分母α90° 的辐条数34应等于 360/90α120° 的辐条数23应等于 360/120默认参数下「均匀度」CV 0.7373断言红聚集度 1.08绿去掉拍脑袋阈值「可见斜列数」9 / 16 / 28 / 33 乱跳改为最近邻步长众数 Δn72 格方向直方图数峰纯噪声3两处断言是恒真的。交付版的自检里当N 1000时「α137.5° 辐条数144」和「真黄金角辐条数0」这两条会走进pass: true的兜底分支——默认参数 N500也就是说默认状态下有一整块检查是永远绿的。这比没有检查更危险它会让人以为验过了。我把它们做成不依赖当前参数、每次都用 N1000 独立复算一遍的版本并且补上了周期 q 与实测辐条数必须一致这条最强的断言——把角度改成 137.4°它照样能算出 600 条并判对。4quantAngle把 -1e-13 归一成了 360.000000凭空多出一条辐条。角度对 360 取模时浮点误差会给出 -1e-13 这种值加 360 之后变成 359.9999999999999四舍五入到 6 位小数就是360.000000和 0.000000 变成了两个方向。症状很隐蔽13/34 应该 13 条辐条页面上是14 条。修法是取模后再做一次% 360000000的整数归一让 360.000000 回到 0.000000。修完 13 条。性能交付版的最近邻统计是每个种子跟其余所有种子比一遍N²次距离计算。5000 颗种子我单独量了这一趟60.4 ms还没算角度分析和重绘。改成网格分桶格子边长取 2.5×R/√N就近查一圈之后包含最近邻、角度分析、13 条自检和重绘的全量重算是 20.8 ms而且两边算出的最小间距完全一致9.968 vs 9.97 px——换算法没换答案。N20000 时全量重算是131.1 ms这条我没做到需求里的100 ms 以内如实写在这里需要的是把角度分析和自检也一起省掉而不是再优化最近邻。六、失败路径实测四条输入校验的提示都是页面上点出来的原文发散角填 250⚠️ 发散角必须是 90°~180° 之间的数字已回退到上一次的有效值 137.50776405003785°种子数填 99999⚠️ 种子数必须是 1~20000 的整数已回退到上一次的有效值 5000花盘半径填 -5⚠️ 花盘半径必须是正数已回退到上一次的有效值 440尺寸系数填 9⚠️ 尺寸系数必须是 0~3 之间的数字已回退到上一次的有效值 0.9提示挂在页面顶部输入框自己改回上一次的有效值画面继续正常渲染、没有白屏也没有停摆图 5。另一条失败路径在断言里把 α 设成 13/34 137.6470588235°第 8 条断言立刻变红——平均最近邻间距 2.74 px低于 0.5×2R/√N 6.22 px 的下限。这条红是应该出现的34 个方向上挤 5000 颗种子相邻两颗最近只隔 1.50 px肉眼也看得出来挤。能失败的检查才算检查这条断言就是拿来做这件事的。七、准备环境进入码道 Web码道有三种使用方式WebUI浏览器对话、TUI终端命令行和桌面 IDEIDE 插件。本文用 WebUI 版演示浏览器打开码道 Web 版 https://devcloud.cn-north-4.huaweicloud.com/chat?sourcedmzntgwsfsourceaddmzntgwwbwz登录后把上面那段需求贴进对话窗口剩下的交给它生成完直接在页面里看效果。不需要装软件。八、本地复现单文件应用不需要装任何东西gitclone https://atomgit.com/deli007/demo_parkcddemo_park/codearts-phyllotaxis-golden-angle# 方式一直接双击 index.html# 方式二起个本地静态服务python-mhttp.server8000# 然后打开 http://localhost:8000/index.html我自己复核用的操作序列可以照着点读数应该基本一致打开页面 → 窗口拉大一点让画布铺开到 1000 px 见方花盘半径会自动取画布宽度的 0.44 倍这里是 440→ 默认就是黄金角、5000 颗种子 → 读右栏重复方向 0 条、最小间距 9.97 px、平均 10.56 px、聚集度 1.08、Δn 14455%、13/13 通过 → 把发散角改成137.5辐条数变 144、角度周期变 q 144 → 改成137.6470588235360×13/34辐条数 34、最小间距掉到 1.50 px、自检变 12/13 → 点「黄金角」按钮回到默认。这套读数在不同机器上会有零点几毫秒和百分位小数级别的差别但辐条数、分母、Δn 这些整数是完全确定的。九、使用码道体会这次最有用的经验仍然是把「怎么验收」写进需求而且要比上次写得更具体。上一轮我写的是页面里要有自检面板这轮我加了三条硬约束断言不许恒真、实测值/理论值必须并排给出相对误差、每个参数都要有中文的非法输入提示。结果是交付版一打开就能看出问题——第 6 条断言红着CV 0.7373 对 CV0.5我顺着这条线索才挖出CV 这个指标在稀疏分箱下不成立这个更本质的毛病。如果只写帮我加点检查这一条很可能被改成 CV1.0糊过去。第二条经验是AI 写这类数值模型规则通常是对的度量方式才是坑。这个页面的核心模型θ nα、r R√(n/N)、黄金角 360/φ²交付版一个符号都没错黄金角恒等式差值 2.842e-14 也说明公式写得挺准真正翻车的全是怎么量——辐条怎么数、均匀度怎么定义、NaN 和浮点边界怎么处理。这些恰恰是光读代码看不出来的必须跑起来、拿可证伪的数字去对教科书里的分母、360/904 这种小学生都算得出的期望值。第三条是**“看起来在检查比没有检查更危险**。交付版 12 条自检里两条是pass: true、一条阈值拍错、几条指标本身没意义默认参数下它显示11 绿 1 红”很容易让人以为大体没问题。现在的 13 条我逐条问过自己同一个问题“它在什么参数下会红”——答不上来的就删掉重写。十、总结叶序的规则就一行第 n 颗种子角度n × α、半径R × √(n/N)角度不同长出来的东西完全不同。137.5° 和真黄金角 137.50776405003785° 看着只差 0.008°实测差 144 条辐条137.5/360 55/144转 144 次就回原点。137.4° 是 600 条137.6° 是 225 条辐条数就是分母。真黄金角 5000 颗种子重复方向 0 条最小/平均/最大最近邻间距 9.97 / 10.56 / 10.97 px聚集度 1.08最近邻步长众数 Δn 144斐波那契项13 条自检全绿全量重算 20.8 ms。交付版有三个真问题辐条检测数错137.5° 报 8 条、均匀度 CV 指标不成立默认参数下断言就红、两处恒真断言另外还有一个 360.000000 的浮点边界会让辐条数多算 1 条。最近邻从N²全对全改成网格分桶N5000 的最近邻那一趟 60.4 ms → 全量 20.8 ms最小间距两边一致9.968 pxN20000 全量 131.1 ms仍然是偏慢的如实写出来。判断一个自检面板值不值得信就看它能不能红把 α 滑到 13/34第 8 条断言当场变红平均间距 2.74 px 6.22 px。