[{"data":1,"prerenderedAt":306},["ShallowReactive",2],{"article:column:/column/math-beauty/pigeonhole-collisions":3},{"id":4,"title":5,"body":6,"description":12,"extension":295,"meta":296,"navigation":301,"path":302,"seo":303,"stem":304,"__hash__":305},"column/column/math-beauty/pigeonhole-collisions.md","有限的编号，为什么一定会发生碰撞",{"type":7,"value":8,"toc":281},"minimark",[9,13,16,19,23,26,29,32,35,38,42,45,48,51,58,61,64,74,77,80,83,86,89,92,95,98,101,104,107,110,113,116,119,125,128,131,134,137,140,144,147,150,159,162,166,169,172,175,178,181,185,188,191,197,206,209,212,215,218,224,227,230,233,238,241,244,247,250,275,278],[10,11,12],"p",{},"你整理文件时，发现两份文件各自算出了一串摘要，长度一样，字符也完全一样。可以直接删掉其中一份吗？",[10,14,15],{},"先别让“数字指纹”这个比喻替我们作决定。指纹听起来像唯一身份，摘要却是按规则计算出来的短编号。要判断同编号是否说明同内容，得先问：可能的内容有多少种，编号又有多少种？",[10,17,18],{},"上一章用乘法数清了二元开关的状态。本章继续用计数，不过目的变了：不再列出所有选择，而是证明某种区分必定做不到。我们会发现，碰撞可以无可避免，具体碰撞却未必已经知道；工程系统也可以在承认碰撞的前提下，正确地保存和查找数据。",[20,21,22],"h2",{"id":22},"先约定怎样给内容编号",[10,24,25],{},"把当前讨论的不同输入组成集合 A，把允许使用的编号组成集合 B。规定一套规则 h，让 A 中每个输入恰好得到 B 中一个编号，写作 h：A → B。这叫作从 A 到 B 的映射，也叫函数；h(x) 表示输入 x 得到的输出。这里先讨论两个集合都有限的情况。",[10,27,28],{},"“恰好一个”很重要。一个输入不能一会儿得到甲号、一会儿得到乙号。实际算法如果带有参数，我们就固定同一算法和同一组参数再比较。对于文件，本章把内容理解为完整的字节序列，文件名和存放路径不算在内。",[10,30,31],{},"如果两个不同输入 x、y 得到同一编号，即 x ≠ y，而 h(x)=h(y)，就发生了一次碰撞。同一文件算两次得到相同摘要，不叫碰撞，因为这里缺少“输入不同”这个条件。",[10,33,34],{},"这里数的是不同内容，而不是磁盘上有几个副本。同一段内容被复制十次，可能出现十个文件，却仍只有一种输入。若要用对象数量证明不同内容碰撞，必须先保证被数进去的输入彼此不同；否则十个副本共用一个摘要，完全符合预期。",[10,36,37],{},"把编号想成盒子的标签，每个输入沿着规则进入一个盒子。碰撞就是某个盒子里装进了至少两个不同输入。盒子的名称、颜色和摆放次序都不会改变这个事实。",[20,39,41],{"id":40},"五个对象四个编号","五个对象，四个编号",[10,43,44],{},"假设有五个不同对象，却只允许使用 00、01、10、11 四种编号。怎样分配才能让每个对象独享一个编号？",[10,46,47],{},"试着让前四个对象各用一个，第五个就没有新编号了。换一种顺序也没有帮助。为了把这个直觉说完整，我们用一次反证：先假设可以不发生碰撞，再看它会带来什么结果。",[10,49,50],{},"如果没有碰撞，每个编号至多分给一个对象。四个编号合起来至多容纳四个对象。但我们要求五个对象全部获得编号，矛盾。因此，没有碰撞的假设不成立，至少两个对象必定同号。",[10,52,53],{},[54,55],"img",{"alt":56,"src":57},"图1：五个对象映射到四个编号，甲乙同号而其他对象可以独占编号；无碰撞假设只能容纳四个对象","/collections-assets/math-beauty/ch08/figure-01-pigeonholes.png",[10,59,60],{},"还有一个前提藏在“全部获得编号”里。如果允许拒绝第五个对象，或者给它一个约定之外的新编号，当然可以绕开眼前的冲突，但任务也随之改变了。我们的论证针对的是：每个候选都要处理，而且输出始终落在同一组有限编号中。",[10,62,63],{},"将“四”和“五”换成 n 和 n+1，证明一字不差地成立：n 个盒子，每盒至多装一个，合起来至多装 n 个，装不下 n+1 个。这就是鸽巢原理。对象更多时，从中先取 n+1 个，也已经足以推出碰撞。",[10,65,66,73],{},[67,68,72],"a",{"href":69,"rel":70},"https://ocw.mit.edu/courses/6-042j-mathematics-for-computer-science-spring-2015/mit6_042js15_textbook.pdf#page=581",[71],"nofollow","MIT《Mathematics for Computer Science》第14.8节","把它表述为有限集合之间的映射结论：输入集合的元素数大于输出集合的元素数时，必有两个不同输入映到同一输出。应用时，要说清楚对象、盒子，以及分配规则，不能只看到两个大小不同的数字就套用。",[20,75,76],{"id":76},"固定长度到底提供多少编号",[10,78,79],{},"现在把四个编号推广到 b 位二进制摘要。b 是正整数；每位只能是 0 或 1。按上一章的乘法计数，长度恰为 b 的二进制串共有 2^b 种。",[10,81,82],{},"因此，b 位摘要至多提供 2^b 种输出。有的算法未必用得到全部输出，但这只会减少可用编号，不会增加容量。",[10,84,85],{},"输入需要多大，才足以证明碰撞？不必讨论所有可能长度。只考虑长度恰为 b+1 的全部二进制串，就有 2^(b+1) 种输入。只要算法接受这些输入并都输出 b 位摘要，就有：",[10,87,88],{},"2^(b+1) = 2 × 2^b > 2^b",[10,90,91],{},"输入比编号多，鸽巢原理立即适用。这里两个集合都是有限的，已经完成了证明。",[10,93,94],{},"如果文件接口只接受整字节输入，可以改取固定的L字节，使8L＞b。只要算法接受这一长度的全部文件，就有2^(8L)＞2^b种不同内容，仍然必有碰撞。",[10,96,97],{},"例如，输出只有两位时，编号最多四种；三位输入却有 000、001、010、011、100、101、110、111 八种。无论计算过程多复杂，都无法把这八种输入分到四个编号中，同时保持彼此不同。",[10,99,100],{},"事实上，不必凑齐那一整组输入；只要拿到 2^b+1 个彼此不同且被算法接受的输入，也必有碰撞。这个数量给出了一条保证重复的界限，不表示在它之前绝不会重复。余下多少空编号，与某两个对象是否已经同号，是两件事。",[10,102,103],{},"这也解释了为什么“把算法写得更巧”不能消除容量限制。算法可以改变哪些输入同号，却不能凭空创造第五种两位二进制串。增加输出位数可以增加容量；在新的容量之上，同一个计数问题仍然存在。",[20,105,106],{"id":106},"亲手算一次碰撞",[10,108,109],{},"为了看清编号怎样丢掉区别，我们用一个很小的规则：h(x)=x mod 4。这里 x 是非负整数，mod 4 表示除以 4 后取余数，所以输出只能是 0、1、2、3。",[10,111,112],{},"把 1、5、9 分别写成：",[10,114,115],{},"1 = 4×0+1，5 = 4×1+1，9 = 4×2+1",[10,117,118],{},"三个数的商不同，余数却都是 1。因此 h(1)=h(5)=h(9)=1。这个规则保留了余数，舍去了“包含多少个完整的四”。看见输出 1，我们已经无法仅凭它判断原来是哪个数。",[10,120,121],{},[54,122],{"alt":123,"src":124},"图2：0到11按除以4的余数进入四个桶，重点展示1、5、9的商不同但余数相同","/collections-assets/math-beauty/ch08/figure-02-mod-four.png",[10,126,127],{},"再算 h(6)=2。因为 1 和 2 是不同输出，可以确定输入 1 与输入 6 不同。为什么这一步成立？如果两个输入相同，在固定规则下就应得到同一输出；输出不同，便排除了输入相同的可能。",[10,129,130],{},"联系第02章的条件方向，可以把结论压成两行：",[10,132,133],{},"输出不同 → 输入不同",[10,135,136],{},"输出相同 ↛ 输入相同",[10,138,139],{},"符号 ↛ 表示“不能据此推出”。第二行不是说同号输入一定不同，而是说它们可能相同，也可能不同，现有信息不足以判断。第一行同样要求使用同一个确定规则，不能拿两个算法的结果直接套用。",[20,141,143],{"id":142},"证明存在还没有回答哪些问题","证明存在，还没有回答哪些问题",[10,145,146],{},"图1中只有甲、乙共用 00，丙、丁、戊各自独占一个编号。五个对象到四个编号已经有碰撞，却不能据此说“丙一定有另一个同号对象”。“至少有一对”与“每一个都有伙伴”，是不同强度的陈述。",[10,148,149],{},"存在也没有自动变成容易寻找。上面的余数规则很简单，我们能直接写出 1 和 5；对于另一个规则，鸽巢原理仍可证明有碰撞，却没有告诉我们应该尝试哪些输入，更没有给出寻找所需的时间。",[10,151,152,153,158],{},"这一区分也适用于技术名称。用于分桶的一般散列规则，与密码学散列函数的设计要求不能混为一谈。",[67,154,157],{"href":155,"rel":156},"https://csrc.nist.gov/glossary/term/collision_resistance",[71],"NIST 对碰撞抵抗性的定义","关心的是找到碰撞在计算上是否可行。仅凭本章的存在证明，既不能评价某个具体算法安全，也不能宣称它已经被破解。",[10,160,161],{},"同样，我们还没有回答“抽取多少对象通常会碰撞”或“撞上的机会有多大”。这些问句增加了选择对象的方式，属于之后的概率问题。不要把本章的必然性证明读成随机实验的预测。",[20,163,165],{"id":164},"编号有限并不意味着小范围内必有碰撞","编号有限，并不意味着小范围内必有碰撞",[10,167,168],{},"标题里的“必定”，有明确前提：不同候选输入多于可用编号。若只允许输入 0、1、2、3，仍采用除以 4 取余，它们的输出依次是 0、1、2、3，就没有碰撞。",[10,170,171],{},"在约定的输入范围内，不同输入总得到不同输出，这样的映射叫作单射。如果有限候选数不超过编号容量，可以给每个候选分配不同编号，构造一个单射。",[10,173,174],{},"但“可以构造”不是“任意规则都行”。只允许输入 1、5、9 时，三个候选少于四个编号，余数规则却仍把它们全送到 1。容量够用说明没有被数量逼入死角，实际规则是否保留区别，还得检查。",[10,176,177],{},"还可以试着改造那五个对象的任务：在原来编号后面加一位，就有八种编号可选，可以给五个对象各分一个。这样做确实解决了这组对象的区分问题，但用的是更大的输出集合。若有人仍把它说成“四个编号装下五个不同身份”，便把新增的一位从账本里漏掉了。检查容量时，凡是用来区分对象的额外标记，都应算进输出。",[10,179,180],{},"因此，一个系统声称编号唯一时，要先问它对哪一组对象承诺唯一。对一个已知的小集合逐个分配号码，与把所有允许内容压成同样长度的摘要，承担的任务可能很不一样。",[20,182,184],{"id":183},"允许同桶仍然能够正确查找","允许同桶，仍然能够正确查找",[10,186,187],{},"现在做一个小字典：用整数作为 key，也就是查询时提供的键；每个键旁边保存一条记录。我们把键按除以 4 的余数分到四个桶，但保留完整的键，不只保存桶号。",[10,189,190],{},"假设 1 号桶内依次有键 1、5、9。查找键 9 时，先算 h(9)=1，到 1 号桶；再检查原始键：1 不等于 9，5 不等于 9，直到遇到 9，才取出对应记录。假如查询的是 13，虽然同样进入 1 号桶，但逐个检查后没有相等的键，就应回答“没找到”。",[10,192,193],{},[54,194],{"alt":195,"src":196},"图3：查询9先定位到1号桶，再分别比较原始键1、5、9；只有键相等才能返回记录","/collections-assets/math-beauty/ch08/figure-03-key-check.png",[10,198,199,200,205],{},"散列值在这个设计里负责缩小候选范围，原始键的相等判断负责确认身份。真实实现可以采用不同的存放和查找办法，不能把图中的逐个检查当成所有字典的内部结构。",[67,201,204],{"href":202,"rel":203},"https://doc.rust-lang.org/std/collections/struct.HashMap.html",[71],"Rust 的 HashMap 官方文档","同时要求键支持相等判断和散列，并要求相等的键有相等的散列值；这个方向与我们刚才的逻辑一致。",[10,207,208],{},"回到文件去重：如果要求严格按字节内容判断，可以把摘要相同的文件列为候选，再核对完整内容；仅凭相同摘要就合并，是额外采用的一项判断规则。是否接受这种规则，要看系统要求和具体设计，不能把它冒充为“同摘要必定同文件”的数学定理，也不能假定每个现成工具都逐字节复核。",[10,210,211],{},"有人会想到再核对文件长度。这可以继续排除候选，却不能自动补成证明：前面用于计数的输入本来就具有同样长度；对整字节文件，也已选定同一个L字节长度。即使它们的摘要和长度都相同，仍可能是不同内容。每增加一个核对条件，都应问它究竟排除了哪些情况，而不是把检查项的数量当作正确性的保证。",[10,213,214],{},"有限编号的价值因此仍然很实在。它可以帮我们组织对象、排除不可能相同的候选；而每当我们想让它独自证明身份，就需要重新检查，它保留下来的区别是否足够。",[20,216,217],{"id":217},"顺手试一下",[10,219,220],{},[221,222,223],"strong",{},"题目1：三位编号够不够",[10,225,226],{},"一个系统要给九种不同状态分配恰好三位二进制编号，每种状态都必须得到一个编号。能否保证不同状态绝不同号？若只剩七种状态，是否任何分配规则都不会碰撞？",[10,228,229],{},"**提示：**先数编号，再区分“存在一种好分配”和“所有分配都好”。",[10,231,232],{},"**参考答案：**三位二进制共有 2³=8 种编号。九种状态多于八种编号，若每个编号最多分给一种状态，至多容纳八种，与九种矛盾，所以必有碰撞。七种状态时，可以依次使用 000 到 110 这七个不同编号，因而存在无碰撞分配；也可以把两种状态都分给 000，因此不能保证任何规则都无碰撞。",[10,234,235],{},[221,236,237],{},"题目2：同桶后该返回什么",[10,239,240],{},"沿用图3的小字典，1号桶保存键 1、5、9，记录分别是“甲”“乙”“丙”。查询键 13 时，一个程序只比较桶号便返回“甲”。它错在哪里？如果两个文件的摘要不同，又可以得出什么结论？",[10,242,243],{},"**提示：**把“桶号相等”和“原始键相等”分别列为判断条件。",[10,245,246],{},"**参考答案：**h(13)=1 只能说明应查1号桶，不能推出 13=1。逐个比较后，13 与 1、5、9 都不相等，应回答未找到。对于两个文件，若摘要由同一确定算法、同一参数、完整且未变化的内容正确算出，摘要不同就能推出字节内容不同；相同内容不可能在这些条件下产生不同输出。",[20,248,249],{"id":249},"来源与延伸阅读",[251,252,253,261,268],"ul",{},[254,255,256,260],"li",{},[67,257,259],{"href":69,"rel":258},[71],"MIT《Mathematics for Computer Science》，第14.8节，印刷页572—574","：鸽巢原理的有限映射表述与适用对象",[254,262,263,267],{},[67,264,266],{"href":202,"rel":265},[71],"Rust 标准库 HashMap 文档","：散列与键相等判断的约定；本章分桶图为独立教学模型",[254,269,270,274],{},[67,271,273],{"href":155,"rel":272},[71],"NIST CSRC 术语表 Collision resistance","：区分碰撞存在与寻找碰撞的计算要求",[276,277],"hr",{},[10,279,280],{},"作者：Jiancheng Wang · 数学之美（独立原创）",{"title":282,"searchDepth":283,"depth":283,"links":284},"",3,[285,287,288,289,290,291,292,293,294],{"id":22,"depth":286,"text":22},2,{"id":40,"depth":286,"text":41},{"id":76,"depth":286,"text":76},{"id":106,"depth":286,"text":106},{"id":142,"depth":286,"text":143},{"id":164,"depth":286,"text":165},{"id":183,"depth":286,"text":184},{"id":217,"depth":286,"text":217},{"id":249,"depth":286,"text":249},"md",{"collection":297,"order":298,"slug":299,"author":300},"数学之美",8,"pigeonhole-collisions","Jiancheng Wang",true,"/column/math-beauty/pigeonhole-collisions",{"title":5,"description":12},"column/math-beauty/pigeonhole-collisions","dO378CIWgcsoNVN3lmoA3qrb2xlq9vGrTbzIMKOVn9E",1791935829091]