黄金分割位移:0.618 这个数从哪来

曲线决定了「按什么顺序排队」,位移量决定了「队伍挪多远」。后者只用了一个数:黄金分割比。

曲线决定了像素按什么顺序排队,位移量决定了队伍整体挪多远。后者只由一个写死的公式给出,这一篇把这个公式拆开,讲清 0.618 这个数为什么被选中,以及它管得住什么、管不住什么。

位移量公式的三个组成部分

曲线把像素排成一条长度 N 的一维队列,下一步要让这条队列整体挪动一段距离,这个距离就是位移量 k。原方案给出的公式是 k = round((√5 − 1) / 2 × W × H),里面只有三个部分,逐个拆开都很朴素。

  • W × H:像素总数 N。位移量按整幅图的规模来定,图越大,挪动的绝对距离越大。
  • (√5 − 1) / 2:一个写死的数学常数,约等于 0.6180339887,也就是黄金分割比的倒数。
  • round:四舍五入到最近的整数,把乘积落到一个可用的下标上。

要注意 k 的单位是曲线上的步数,不是像素坐标上的距离,也不是「每行挪几个像素」。一个像素从曲线的第 i 步被搬到第 (i + k) mod N 步;曲线在平面上到处拐弯,同样的步数在不同位置对应着完全不同的方向。曲线本身如何做到不重不漏,见吉尔伯特曲线。

为什么不能取 N 的一半或四分之一

位移量必须让打散后的画面失去可辨认的结构,而取简单比例恰好做不到。以 1920×1080 为例,N = 2 073 600,如果取 k = N / 2 = 1 036 800,那么 gcd(k, N) 也是 1 036 800,置换被分解成一百多万个长度为 2 的循环——每个像素只和固定的伙伴对调一次。这种遍地两两互换看起来改动了整幅图,实际保留了太多原有的几何关系,而且连续混淆两次就回到原样,等于什么都没做。

取 k = N / 4 更明显:循环长度普遍只有 4,画面像是被划成几份顺序搬动;取 k = N / 3 则到处都是长度为 3 的小循环。共同点是位移量与 N 之间存在简单整数比,置换被切成一堆短环,打乱的层次非常浅。

另一种退化更隐蔽:把同样的公式套在按行扫描的顺序上,k 取成宽度的整数倍时结果就是整行搬移,条带结构一眼可辨。黄金分割比的作用正是让 k 与 N 的公约数尽量小,把置换拉成少数几条很长的循环。真实参数表里,2048×1536 的 gcd(k, N) 恰好是 1,整条队列就是一条长度 3 145 728 的环。

0.618 的数学性质

黄金分割比 φ = (1 + √5) / 2 ≈ 1.6180339887,公式里用的 (√5 − 1) / 2 正好是它的倒数 1 / φ ≈ 0.6180339887,两者相差恰好 1。这个数最出名的性质是:在无理数里,它最不容易被有理数逼近。它的连分数展开写成 [1; 1, 1, 1, …],全是 1,因此在同样的分母规模下找不到更接近它的分数。

这一点对位移量很关键。k / N 会稳定地贴在 0.618 附近,而 0.618 与任何分母不大的简单分数都保持可观的距离,于是位移不会和网格的周期结构对齐:既不会退化成整行搬移,也不会和宽高比例发生简单共振。换成容易逼近的数就麻烦了,比如 0.6 = 3 / 5,在某些宽高组合下 k / N 会精确或近似地落在简单分数上,位移就变成规律的搬运。

植物学里有一个同源的例子:向日葵种子的排列角是所谓的黄金角,约 137.5°,由 360° × (1 − 0.618) 得到。选这个角度的理由和这里一样——它不容易被简单分数近似,所以后长出来的种子不会和前几圈排在同一条辐射线上,花盘不会留下明显的空档。位移量取同一个常数,图的就是这种处处错开。

round 为什么必须有

N 是整数,0.6180339887 是无理数,两者相乘几乎不可能恰好落在整数上。而 k 在后续计算里的角色是下标偏移,要参与 (i + k) mod N 这样的取模运算,只能是整数。round 就是把乘积拉回整数域的唯一步骤。

用 2048×1536 验算一遍:N = 3 145 728,乘上常数得约 1 944 166.99,四舍五入后 k = 1 944 167。此时 k / N = 0.618034045,与理论常数 0.6180339887 的差距在千万分之一量级。这个偏差无法避免,也不需要避免:只要 k 与 N 的公约数足够小,循环结构就不会因为这点偏差发生质变。

四舍五入还带来一个副作用:不同分辨率的 k / N 会微微上下浮动。16×16 只有 256 个像素,k = 158,k / N = 0.6171875,偏离常数约 0.0014,是小图里偏差最明显的一档。不过 N 越小,网格本身能承载的混乱程度就越有限,实际使用中也很少拿 16×16 的图去谈混淆强度。

真实位移量表

下面这些数字由构建期脚本用与页面同一份核心代码算出,可以直接对照。表中 k 一律是 round 之后的结果,k / N 一列用来观察常数的作用。

常见分辨率下的位移量与循环结构
分辨率像素数 N位移量 kk / Ngcd(k, N)单个循环长度
16 × 162561580.61718752128
100 × 10010 0006 1800.61820500
640 × 480307 200189 8600.6180338542015 360
800 × 600480 000296 6560.6180333331630 000
1280 × 720921 600569 5800.6180338546015 360
1920 × 10802 073 6001 281 5550.61803385413515 360
2048 × 15363 145 7281 944 1670.61803404513 145 728
1000 × 10001 000 000618 0340.6180342500 000
200 × 15030 00018 5410.618033333130 000

读表时重点看 k / N 这一列:从 100×100 到 2048×1536,不论 N 怎么变,比值都稳定在 0.618 附近,偏离最大的仍是像素数最少的 16×16。后两列则说明置换的形状差异可以很大:同为百万像素级的大图,循环个数可能是 1,也可能是 135。另外,1920×1080 与 1080×1920 的 N 相同,因此 k 相同,但曲线形状不同,混淆结果也是两幅完全不同的图。

位移的循环结构

循环移位看起来是一次整体动作,拆开看却是一组互不相交的循环构成的置换:每个像素沿曲线向前走 k 步,走若干次之后回到自己原来的位置。循环的个数是 gcd(k, N),每个循环的长度是 N / gcd(k, N),两个数相乘正好等于 N。

两个例子对照最清楚。1920×1080 的 N = 2 073 600,k = 1 281 555,gcd(k, N) = 135,于是置换分成 135 条循环,每条长度 15 360;而 2048×1536 的 N = 3 145 728,k = 1 944 167 与 N 互质,gcd 等于 1,整个置换是一条长度等于全部像素数的大环——从任意一个像素出发沿环一直走,会依次经过画面上的每一个位置,走满 3 145 728 步才回到起点。

循环结构不只是抽象概念,它描述了操作的形状。知道置换是一组环,理论上就可以在原地完成搬移:沿一条环走一圈,用一个临时像素存住起点,后面的位置依次前移,省下输出缓冲。本站的实现没有走这条路,而是照旧准备输入与输出两个缓冲区、一次线性扫描写完,原因是逐字节直写更容易获得稳定的速度,多出来的 4N 字节内存换来实现上的简单。置换流程与伪代码里能看到实际写法。

位移量会影响可逆性吗

完全不会。对一条长度为 N 的队列做循环移位,只要位移量是整数,结果永远是双射:下标 i 映射到 (i + k) mod N,不同的 i 得到不同的结果,N 个元素一个不多一个不少。任何 k 都能被原样反解,解混淆只需要把方向换成 (i − k) mod N,这也是混淆与解混淆互为逆运算的全部理由。

k 的取值只影响视觉上的混乱程度与循环形状,不影响能不能还原。几种边界情况看得很清楚:k = 0 时置换退化成恒等变换,输出与输入逐像素相同;k = N 时 (i + N) mod N 等于 i,同样等于没做;k 比 N 大时只有 k mod N 有意义。所以实现里不需要为位移量做范围检查,模运算天然把事情收好。详细的逆运算推导与误差来源见可逆性与误差分析。

真正致命的是用错分辨率。曲线与 k 都由宽高算出,如果拿 1920×1080 的图当成 1080×1920 去解,曲线形状、位移量、写回位置全都不同,输出只会是另一幅被打乱的图。这也是判断处理方向时最需要注意的一点。

重要边界:k 是公开常量,不是密钥

k 由宽高和一个写死的常数算出,没有随机数、没有密钥、没有口令,任何人只要知道图片尺寸就能得到同一个 k。混淆后的图片不携带任何额外秘密,也抗不住针对性还原:把尺寸试一遍,按同一套规则解一次,内容就出来了。

这一点必须说清楚——k 提供的是错位,不是保密。它能提高自动化抓取与直接复用的成本,让随手转发的图片不容易被流水线程序当作正常素材处理;但它挡不住人工还原,更不能用来保护真正敏感的信息。需要保密时应该使用带密钥的加密方案,或者干脆不要发送。与同类方案的对比把混淆与加密的边界逐条列了出来。

把身份证、票据、聊天记录截图直接混淆后发出,并不能阻止别人还原它。真正的脱敏必须先把敏感区域永久遮挡或裁剪掉,再考虑要不要混淆。

局限与小结

把边界收成几句:位移量只与像素总数有关,同一尺寸的两张图使用完全相同的置换,只要尺寸被确定,规律就完全公开;k / N 稳定在 0.618 附近,意味着位移距离几乎总是接近全图的一半多,打散程度也基本固定,不随图片内容变化。

另一个容易忽略的限制是大图缩放:单张图超过 800 万像素或单边超过 4000 像素时,本站会先等比缩放再处理,缩放会改变像素值,这条路径不再是无损的,k 也随缩放后的尺寸重新计算。想要无损往返,必须把宽高保持在限制之内并使用 PNG 输出。耗时与内存的账目见复杂度与性能。

要点速记

  • k = round((√5 − 1) / 2 × W × H),单位是曲线上的步数。
  • 黄金分割比难以被简单分数逼近,位移不会和网格周期对齐。
  • 循环个数是 gcd(k, N),每个循环长度是 N / gcd(k, N),任意 k 都可逆。
  • k 由宽高公开算出,是固定规则而不是密钥,不提供保密性。

想直接看到某个分辨率下的 k、循环个数与循环长度,把宽高填进曲线可视化即可。

本文更新于 2026-10-14。文中算法说明基于本站实际运行的实现整理,行为可通过 _build/verify-core.mjs 复核。

内容为本站原创,转载或引用请注明来源「小番茄混淆解图」并保留原文链接。