科普丨哪段代码最能代表程序员的暴力美学?
知乎日报 · 生活与娱乐
知乎日报 2026-11-05 20:00 北京 50 年了,从没被取代。 如果一台设备只能显示纯黑和纯白,怎样让人眼看见连续的灰度、光影与层次? 1976 年,两位计算机科学家用一个双重循环、四次加法和一次比较,给出了仅有 12 行代码的答案。 哪段代码最能代表程序员的暴力美学? 答主 丨 Asher (6800+人赞同) 一张灰度照片有 256 级亮度。现在你的输出设备只支持两种状态——黑或白。没有灰色。 问题来了:怎么用只有黑和白的点阵,让人眼看到连续的灰度? 1976 年,Floyd 和 Steinberg 给出了一个 12 行代码的答案: for (let y = 0; y < height; y++) { for (let x = 0; x < width; x++) { let old = pixels[y][x]; let neu = old > 128 ? 255 : 0; pixels[y][x] = neu; let err = old - neu; pixels[y][x+1] += err * 7/16; pixels[y+1][x-1] += err * 3/16; pixels[y+1][x] += err * 5/16; pixels[y+1][x+1] += err * 1/16; } } 12 行。一个双重循环,四次加法,一次比较。 输入:一张灰度照片,每个像素有 256 种可能的亮度。输出:一张只有纯黑和纯白两种颜色的图片。效果:远看和原图一模一样。 在线 Demo:Floyd-Steinberg 抖动 左:灰度原图(256 级亮度);右:Floyd-Steinberg 抖动后(仅黑白两色) 这段代码在干什么 逐像素扫描,从左上角到右下角。对每个像素做一个最暴力的决定: 你是黑的还是白的?没有中间选项。 如果原始亮度是 180(偏亮),强制变成 255(纯白)。但这个操作制造了一个误差:你把 180 说成了 255,多报了 75。 这个误差不能凭空消失。算法把它按比例甩给右边和下面的四个邻居: 当前 → 7/16 3/16 5/16 1/16 邻居接到这笔「债务」后,轮到自己被处理时,会带着这笔误差一起做判断。如果误差累积够大,本来应该变白的像素就会被翻转成黑——以此「偿还」之前的欺骗。 整张图扫一遍,误差一路向右下传播、扩散、抵消。最终的黑白点阵从远处看,和原始灰度图的亮度分布完全一致。 暴力在哪 第一,决策暴力。256 级灰度被强行压缩为 2 级。每个像素丢弃 87.5% 的信息。 第二,方法暴力。不做频域分析,不用傅里叶变换,不需要任何高等数学。就是一个 for 循环,你能想到的最原始的图像处理方式。 第三,哲学暴力。算法的核心假设是「误差可以转嫁」。我犯的错不用自己承担,甩给邻居就行。邻居再甩给邻居的邻居。只要每一步都甩得够快,最终的整体效果就是对的。 最后得到的结果是:「局部犯错、全局正确」。 美在哪 把这张图放大 4 倍看:全是锯齿和噪点,像一堆随机撒的黑白沙粒。 把这张图缩小到正常尺寸看:一张完整的照片,有光影、有层次、有细节。 这个「放大看是混沌,缩小看是秩序」的反差,就是它的美学核心。 为什么你在屏幕上看可能觉得「不像」 如果你直接在 Retina 屏上 1:1 看抖动结果,可能会觉得「这也不像原图啊,锯齿很明显」。 这不是算法的问题,是观看条件的问题。 Floyd-Steinberg 发明于 1976 年,当时的输出设备是 300 DPI 的激光打印机和 72 DPI 的显示器。在那种物理尺度下,单个像素点极小,人眼从正常距离根本分辨不出来。大脑被迫对一小片区域「取平均」——这正是抖动奏效的前提。 在你的屏幕上,如果抖动图的像素数小于显示的物理像素数,图片就被放大了——每个黑白点被拉成好几个物理像素那么大,人眼就能看清单个点了,幻觉立刻破裂。 解法很简单:让图片像素数 ≥ 显示物理像素数。换句话说,必须「缩小显示」才是抖动算法的正确观看方式。就像印刷品——报纸上的照片你贴着看全是墨点,退后一臂距离看就是一张正常照片。 在 Demo 里,试试把浏览器窗口缩小,或者退远一点看屏幕,抖动图会瞬间变成「正常灰度照片」。 人眼的空间分辨率是有限的。当黑白点的密度超过视网膜的分辨能力时,大脑会自动对一小片区域「取平均」。黑点密的地方你看到暗色,白点密的地方你看到亮色。 12 行代码利用了人类视觉系统的物理缺陷,用最粗暴的方式制造了精确的幻觉。 实际效果 我做了一个交互 Demo,可以上传自己的照片实时体验: Floyd-Steinberg 抖动:暴力美学的像素炼金术 在里面你可以: 1. 上传任意图片——拖进去就行,实时看到灰度原图 vs 抖动后的纯黑白结果。 2. 点击放大——看