
正 65537 边形是尺规作图历史上一个非常有名的极端案例。高斯从理论上证明了它能用尺规作出来但真正把完整构造步骤整理出来是后来数学家 Hermes 花了大量时间才完成的工作。用 manim 给这个过程做动画表面上是“多画一个多边形”实际上是要把一套几万步级别、带有严格依赖关系的几何构造流程变成可以播放、可以验证、不会在渲染中途崩溃的视频工程。适合看这篇内容的读者包括正处于 manim 学习阶段的同学、对数学可视化感兴趣的开发者以及想挑战长流程动画工程的人。最值得关注的不是“65537”这个数字本身而是从“理论可作图”到“完整动画”之间数据、场景、资源、渲染这一整条链路到底怎么拆。先说一个很容易误会的点很多人看到这个标题以为难点在 manim 语法。其实 manim 在这个项目里只是“最后一公里”。真正的问题是尺规作图正 65537 边形并不是“调用一个 polygon(65537) 把顶点连起来”而是要展示直线、圆、交点、连线之间一层一层叠出来的构造过程。这个过程一旦开始走向完整复现场景里的历史对象会越来越多渲染负担会快速上升动画编排也会从“写代码”变成“管理数据”。下面按我理解中一个完整项目应该走的路线把数学背景、数据准备、环境搭建、渲染取舍、验证排查这几个部分拆开讲。1. 先搞清楚正 65537 边形到底难在哪个环节1.1 尺规作图可行性的数学判断尺规作图有一套很严格的规则只允许用没有刻度的直尺和圆规通过有限次操作来完成图形。什么样的正多边形能作出来高斯在《算术研究》里给出了一个著名的判定条件。简单说正 n 边形能尺规作图的必要且足够条件是n 可以写成 2 的幂与若干个互不相同的费马素数的乘积。费马素数长这样F_n 2^(2^n) 1。前几个费马素数分别是 3、5、17、257、65537。也就是说正三边形、正五边形、正十七边形、正二百五十七边形、正六万五千五百三十七边形理论上都能用尺规作出来。费马素数数值对应的正多边形F03正三边形F15正五边形F217正十七边形F3257正二百五十七边形F465537正六万五千五百三十七边形到了 F5也就是 2^(32)1这个数其实不是素数所以正 4294967297 边形不能用尺规作图完成。这一点恰好说明65537 这个数字不是随便挑的它是少数几个“理论上可行、步骤又特别多”的边界案例。1.2 可行不等于步骤少数学上证明了“能做”和真的把每一步尺规操作列出来中间隔着一道巨大的鸿沟。高斯证明了正十七边形可作图但他本人并没有一笔一笔去画完整流程。到了正 257 边形、正 65537 边形人类手算和手绘的复杂度已经完全不是同一量级。历史上Hermes 对手稿的整理工作持续了非常长时间。很多资料会提到他在这个项目上花了十年左右手稿体量非常大长期保存在哥廷根大学。这也说明了一个现实给这个构造过程做动画首先要面对的不是 manim而是“构造步骤数据”本身是否存在、是否完整、是否结构化。1.3 “画出多边形”和“尺规作图”是两个完全不同的任务如果只是想画一个看起来像圆的图形direct 用一个 65537 条边的多边形就够了。真正的尺规作图动画要展示的是如何用有限次“取两点画直线”“取圆心和半径画圆”“取直线与圆交点”这些基础操作慢慢确定出 65537 个顶点最终连成多边形。这两者的区别非常重要。前者是计算机图形学问题后者是复现一个历史上真实存在的几何构造流程。你在 manim 里看到的每一根辅助线、每一个圆都应该是“构造逻辑”的一部分而不是为了好看随手画的装饰。2. manim 不是瓶颈构造数据才是2.1 动画层只能做一件事把构造步骤翻译成画面如果你真的想复现 Hermes 的完整流程第一步不是打开编辑器写 Scene而是先问自己我手上的构造步骤是什么每一条直线基于哪两个点每一个圆以哪个点为圆心、半径来自哪条已有线段每个新交点是哪两个对象的交点manim 本身是一个很好的数学动画引擎它擅长把几何对象和动画效果表达得很清晰。但它不会替你回答“下一步该画什么”。动画代码只是解释器真正的进度逻辑在数据里。2.2 为什么不推荐手工堆几千行 self.play一个正 65537 边形的完整构造流程哪怕只做一部分也会有大量操作步骤。如果用最原始的方式一个一个写 self.play后期基本没法维护。改一个点的编号顺序可能牵动后面几十个对象。更稳妥的做法是把构造步骤存成结构化数据比如 JSON 或 Python 字典每条步骤记录自己的 id、类型、依赖对象、关键参数。动画代码只负责读取数据然后根据 type 字段分发给对应的 manim 绘制逻辑。示例结构不是 Hermes 原稿数据只是演示怎么组织 [ { id: p0, type: point, label: A, coords: [-4, 0, 0] }, { id: p1, type: point, label: B, coords: [4, 0, 0] }, { id: c0, type: circle, center: p0, radius_ref: [p0, p1], deps: [p0, p1] }, { id: i0, type: intersection, objects: [c0, l0], choose: upper, deps: [c0, l0] } ]这里最关键的是 deps 字段。它记录了这个步骤依赖哪些既有对象。有了依赖关系才能保证动画播放顺序不乱也才能在某个点缺失时快速定位问题。2.3 从纸面步骤到结构化数据的转化这是整个项目里最耗时的部分。原始材料往往是以论文、手稿、记录图等形式存在不会直接给出一份干净的 JSON。你需要把每一段文字描述翻译成标准操作为每个点、线、圆分配稳定 id再整理依赖关系。这一步强烈建议分阶段做。先处理“确定第 1 个点”的阶段再处理“确定第 2 个点”的阶段。哪怕中间有几处细节对不上也能保证前面一部分动画可以先渲染出来不会因为最后一步没整理完就什么都看不到。2.4 节点命名和编号要提前规划数据量一大最痛苦的问题就是编号混乱。建议从一开始就定一套规则点用 p 前缀圆用 c 前缀直线用 l 前缀交点用 i 前缀。每个 id 在全局唯一不允许重复。每个对象都要记录从哪些步骤产生方便定位。标签字段只用于显示不要依赖 label 做逻辑判断。不要等到写了几百条数据之后再回头统一命名那时候改起来非常痛苦。3. 先跑通一个最小 manim 工程再说3.1 环境准备和依赖清单Manim 社区版的安装不算复杂但依赖不是只有一个 pip 包就能全部解决的。你至少需要Python 3.8 以上建议用 3.10 或更高版本。manim 库通过 pip 安装。FFmpeg负责把动画帧合成为视频文件。如果用到 LaTeX 公式还需要安装 TeX 发行版比如 MiKTeX 或 TeX Live。中文字体如果动画里要显示中文说明需要确保系统里有可用字体。在 Windows 上FFmpeg 经常被人忽略。很多人 manim 装好了一运行就报找不到 ffmpeg其实不是 manim 的问题是 FFmpeg 没装或者没加入 PATH。安装命令很简单pip install manim如果你下载慢可以考虑使用国内镜像源pip install manim -i https://pypi.tuna.tsinghua.edu.cn/simpleFFmpeg 的安装方式根据系统不同有差异Windows 建议下载编译好的二进制包并配置 PATHmacOS 可以用 HomebrewLinux 一般用包管理器直接安装。3.2 写一个最基础的几何场景不需要一上来就写正 65537 边形。先跑通一条线段、一个圆、一个交点。from manim import * class MinimalScene(Scene): def construct(self): a Dot(LEFT * 2, colorBLUE) b Dot(RIGHT * 2, colorBLUE) line Line(a.get_center(), b.get_center(), colorWHITE) circle Circle(arc_centera.get_center(), radius4, colorYELLOW) self.play(Create(a), Create(b)) self.play(Create(line)) self.play(Create(circle)) self.wait()这个场景很小但已经包含了点、线、圆三种最基本的 manim 对象。跑通它能确认你的环境里 manim、FFmpeg、Python 路径都正常。渲染命令manim -ql -p minimal_scene.py MinimalScene其中 -ql 是 low quality渲染速度快-p 是在渲染完成后自动打开预览文件。第一次跑通之后再考虑画更复杂的构造。3.3 交点计算要放在 manim 外面manim 本身负责展示但直线和圆的交点坐标计算建议在绘制之前就完成不要在 Scene 里临时做复杂几何推导。你可以用数学公式自己算也可以用 sympy 这样的符号计算库在数据预处理阶段完成。比如求一条直线和一个圆的交点可以把直线端点、圆心、半径代入方程解出两个交点坐标再作为数据写入构造文件。manim 里只负责读取坐标生成 Dot 和 Line。这样有两个好处一是数据结构里直接保存了关键坐标排查问题方便二是渲染阶段不需要重新计算几何关系性能更好也避免在动画播放过程中出现计算错误导致画面跳变。3.4 单步验证要比完整渲染更早做先选一小段构造流程比如从初始点到第 10 个交点把它们渲染成一个短视频。看看画面里对象的位置对不对、顺序对不对、有没有重叠、有没有跑到画布外。这一步能提前暴露大量问题而且因为数据量小修改起来也快。如果直接一上来渲染完整流程一旦中间某一步坐标错误后面所有对象可能全部偏离排错成本非常高。4. 从单步构造到完整动画的数据组织和分镜4.1 不要把所有东西塞进一个 Scene完整流程的步骤非常多如果全部塞进一个 Scene后期几乎无法维护渲染时一旦某个动画异常整个视频都受影响。更合理的做法是按阶段拆成多个 Scene。每个阶段对应构造流程的一个逻辑段落比如“第一阶段确定初始线”“第二阶段生成第一批交点”“第三阶段完成第一批辅助圆”。各个 Scene 之间通过同一个数据文件和同一个公共绘制函数保持一致性。一个典型目录结构可以是这样construct_65537/ ├── data/ │ └── construct_65537.json ├── src/ │ ├── geometry.py │ └── draw.py ├── scenes/ │ ├── stage_01.py │ ├── stage_02.py │ └── stage_03.py └── media/data 目录放构造数据src 里放几何计算和绘制函数scenes 里按阶段写 manim Scenemedia 是 manim 自动生成的渲染输出目录。4.2 Scene 之间怎么保持连续性不同 Scene 之间是独立渲染的所以要保证每个 Scene 开局时画布上已经有前序步骤的完整状态。常见做法是在数据文件里额外记录“每个阶段的起始对象 id 列表”。启动动画时先瞬间 add 这些历史对象再播放本阶段的新操作。class Stage01(Scene): def construct(self): steps load_construct_data() add_existing_objects(self, steps, start_idsinitial_ids) for step in steps.get_stage(1): if step.type line: draw_line(self, step) elif step.type circle: draw_circle(self, step)这样可以避免每个 Scene 都从空白画布开始也能保持整段动画在视觉上的连续感。4.3 历史对象会越来越多必须设计清理策略这是长流程尺规作图动画最容易遇到的实际问题。画布上的点、线、圆不会消失随着步骤增加对象数量会持续累积。后面几步可能同时存在几百上千个可见对象。manim 每一帧都要对所有已添加对象做状态更新和渲染对象数量越大渲染越慢内存占用也越高。不要把所有历史对象都保持满透明度。建议把前序步骤里的辅助线、辅助圆统一降低透明度或者只在当前阶段开始时快速展示一遍历史全貌然后立刻淡出大部分辅助对象只保留当前阶段需要引用的少数关键点。更谨慎一点可以把已经完成使命、且以后不会再被任何步骤引用的对象直接移除。这个判断依赖 deps 字段如果某个对象没有被后续任何步骤引用那它就可以在动画里淡出甚至从场景对象列表里移除。4.4 相机视角也要分阶段设计完整流程最后的画面肯定接近一个圆但如果在每个阶段都用同一个相机视角观众看不到构造细节。建议在阶段开头先用小范围视角展示当前工作区域阶段结束时再退远到全局视角让观众看到整体进度。manim 里可以通过控制 self.camera.frame 的宽高和中心位置来实现缩放。实操时不要频繁缩放每个阶段保持在两三次以内否则动画观感会很乱。5. 渲染时长、输出和资源占用怎么取舍5.1 长流程动画的资源占用会明显高于普通数学动画一个 10 分钟的动画如果按 30fps 计算大约是 18000 帧。如果每帧画面上都保留了大量历史构造对象渲染时间和内存占用会成倍增长。不要随便拿低配笔记本直接渲染最终版。先在低分辨率、低帧率下跑完整流程确认没有报错、没有图形异常再逐渐升到目标分辨率。否则很容易出现渲染到一半内存占满、程序卡死、视频文件根本没有输出的情况。5.2 分块渲染比单条命令跑完更有保障完整视频建议分成多个片段分别渲染最后再用 FFmpeg 拼接。每个 Scene 本身就是独立视频片段这样天然支持分块。分块渲染有几个好处某个阶段出错只需要重渲染那一段不用从头再来。分辨率、帧率可以在不同阶段单独调整。在内容生成上可以局部替换而不影响已渲染好的素材。manim -qh scenes/stage_01.py Stage01 manim -qh scenes/stage_02.py Stage02最后拼接时FFmpeg 可以做一个简单的 concatffmpeg -f concat -i file_list.txt -c copy output.mp4file_list.txt 里按顺序写好每个片段的文件名。5.3 常用渲染参数怎么选参数预览阶段最终版质量参数-ql 或 -qm-qh分辨率1280x7201920x1080 或更高帧率24 或 3030 或 60预览打开预览便于检查不需要每次打开输出文件临时文件即可按阶段命名保存预览阶段的目标不是画面多细腻而是确认图形位置、播放节奏、依赖关系正确。最终版才去追求高分辨率、高帧率。5.4 不要期待单条命令跑完所有内容哪怕最终视频只有几分钟如果场景里每个步骤都用 Create 慢慢画出来运行时间也会非常长。更稳妥的做法是对每一个新对象播放时间控制在 0.1 到 0.4 秒。每完成一组操作使用 self.wait(0.1) 之类的短停顿而不是长时间停留。大段的历史回顾只做一次不要反复全部重播。实测时你会发现真正的瓶颈经常不是几何计算而是动画时长和渲染帧数。想在可接受时间内输出成片必须接受“部分过程快速带过、关键步骤放慢展示”的原则。6. 怎么验证动画“对”以及常见排错链路6.1 判断正确性不能只看“像不像圆”正 65537 边形的视觉结果就是一个圆。如果只按最终画面判断根本无法区分“构造成功”和“随便画了个圆”。正确的验证方式要回到几何约束上。每一条新线段应该通过面板数据检查它的两个端点是否存在每一个交点应该确认它确实落在两个输入对象的公共位置最终生成的 65537 个顶点应该逐个检查它们与圆心的距离是否一致顶点数量是否正好是 65537。如果数据推导速度比较快对每个新对象的坐标做一次距离误差检查比盯着渲染画面可靠得多。6.2 常见报错和对应处理现象优先排查方向manim 命令找不到Python 环境和 pip 安装路径报错找不到 ffmpeg系统里是否安装 FFmpegPATH 是否配置使用 Tex/MathTex 报错是否安装 TeX 发行版中文显示乱码或方块系统字体、manim 字体配置渲染中途内存占满画布对象是否过多历史对象是否未清理画面里没有出现新对象数据文件 id 是否引用错误步骤是否被意外跳过修改代码后画面没变化manim 缓存优先清理 media 目录或关闭缓存6.3 排查顺序遇到问题不要急着改参数。我自己的排查顺序一般是先复现现象。是直接报错还是动画卡住还是画面结果不对。看构造数据。报错经常来自 id 引用错误、坐标缺失、依赖关系不完整。看依赖环境。manim 版本、FFmpeg、LaTeX、字体。看渲染参数。分辨率、帧率、质量档位。看缓存。manim 有一些文件缓存旧缓存没失效时改了代码也可能渲染出旧结果。清理视频缓存最直接的方式是删除 media/videos 对应目录再重新渲染。调试阶段也可以关闭缓存避免出现“代码改了但画面没变”的诡异现象。6.4 完整复现原稿的工作量必须提前心里有数如果你是冲着完整复现 Hermes 流程去的那要提前知道原始材料的数字化过程可能比 manim 动画本身耗时更多。结构识别、步骤推导、坐标计算、依赖校验每一项都比较花时间。如果只是想做一个“呈现尺规作图思想”的演示动画可以不用逐条还原全部原稿步骤而是做一套简化版流程确保核心逻辑正确即可。这个边界想清楚能帮你避免在达不到的目标上浪费太多时间。7. 这类“极端可视化”项目的边界和扩展方向7.1 不要为了“完整”牺牲可看性完整还原几千步构造流程听起来很酷但观众实际观感可能很平淡。正 65537 边形最终就是接近一个圆如果整个视频从头到尾都在不停画辅助线普通观众很难跟上。更符合传播的做法是做分段设计前半段展示“这一步在做什么”后半段把历史步骤压缩成星轨、淡影、低透明度背景只让当前关键步骤保持高亮。这样既有数学严谨性又有可看性。7.2 几个值得尝试的扩展方向局部放大镜当前构造区域放大显示放在画面角落。构造历史时间轴底部显示当前已经进行到第几步以及总步数。高亮当前依赖新对象出现时高亮它依赖的点、线、圆。单步前进/后退适合做成交互展示而不是纯视频。多语言字幕把每一步的操作说明以文字形式放在画面上。这些方向里局部放大和时间轴对观众最友好也是相对容易实现的扩展。7.3 对 manim 学习阶段的建议如果你正处于 manim 学习阶段不建议一上来就挑战正 65537 边形。可以先从正十七边形的尺规作图流程练手。步骤少、结构清楚适合把数据驱动、分阶段渲染、依赖管理这一套跑通。跑通了正十七边形再去处理正 257 边形、正 65537 边形你会发现主要工作从“写动画”变成了“整理数据和管理资源”这才是这类项目真正的核心能力。最后留一个问题给你自查如果你现在要复现这个项目你手里最缺的是 manim 知识还是构造数据答案大概率是后者。把数据想清楚manim 部分反而会成为整个流程里最简单的一环。