校验文件或签名用sha256.sum256,算法竞赛或子串快速比对用自定义多项式哈希;前者抗碰撞强但慢,后者快但模数小易碰撞,需据场景选型。

Go里用sha256.Sum256还是手写多项式哈希?
选哪个,取决于你干啥:校验文件或签名用sha256.Sum256,算法竞赛或子串快速比对用自定义多项式哈希。前者抗碰撞强但慢,后者快但模数小就容易撞。
常见错误是拿sha256去算上百万个短字符串的哈希然后存进map——没必要,也浪费CPU;反过来,用单模1e9+7哈希去防恶意输入(比如Codeforces被hack),大概率一试就撞。
-
sha256.Sum256输出32字节固定长度,理论碰撞概率约2⁻²⁵⁶,实际中可视为“不撞” - 手写多项式哈希(如
codeforces-go里的stringHashSingleMod)依赖模数M,冲突概率近似1-exp(-n²/(2*M)),n=10⁵时,M=1e9就已有~4%概率撞 - 字符集越小(比如只含'a'-'z')、字符串越短(如长度≤10),碰撞越容易发生,此时双模哈希(两个不同M)几乎是必须的
crypto/md5还能不能在内部系统里用?
能,但得清楚代价:它快,128位输出,正向计算开销极低;但它已被公开碰撞攻击,只要有人能控制输入,就能构造出不同内容但md5值相同的字符串。
典型误用场景:用md5做API请求签名、用户密码存储、或对外暴露的资源ID生成——这些地方哪怕一次碰撞都可能被利用。但如果是本地日志行去重、构建临时缓存key、或离线数据批处理中间标识,md5仍可接受。
-
md5.Sum比sha256.Sum256快约2.5倍,内存占用少一半 - Go标准库中
crypto/md5和crypto/sha1都未被移除,但文档明确标记为“不推荐用于安全敏感场景” - 若只是避免偶然重复(非对抗场景),
md5够用;一旦输入来源不可信,立刻换sha256或更高
为什么codeforces-go默认用双模哈希?
因为单模哈希在算法题里太脆——对手可以精心构造字符串让哈希值全撞在同一个桶里,导致O(n)退化。双模本质是把哈希结果变成一个二元组(h1, h2),相当于把空间从M扩大到M₁×M₂,冲突概率直接降到乘积量级。
看copypasta/strings.go里的实现:mod1 = 1_000_000_007,mod2 = 1_000_000_009,两个大质数相乘≈1e18,对10⁶级别字符串,碰撞概率压到1e-6以下。
- 双模不等于“两次独立哈希”,而是同一套前缀计算逻辑跑两遍,共享
base但用不同mod - 不要自己乱选两个接近的模数(比如
1e9+7和1e9+9),它们的差太小,某些构造性输入仍可能同步撞 - 如果性能真卡得紧,可用一个大模数(如
2^61-1)替代双模,但Go里int64溢出风险高,需手动用uint64和模乘优化
哈希值比较时,==和bytes.Equal怎么选?
对sha256.Sum256这类结构体,直接用==安全且最快;对[]byte切片(比如你自己hex.EncodeToString出来的字符串),必须用bytes.Equal,否则空字节或编码差异会导致误判。
最容易踩的坑:把sha256.Sum256转成[]byte再比较——既慢又多余。Sum类型是固定大小数组,==语义清晰、编译器能内联、无内存分配。
-
sha256.Sum256是[32]byte,支持==;sha512.Sum512同理 -
md5.Sum是[16]byte,也支持== - 但
hex.EncodeToString(h[:])返回string,不同编码方式(大小写、前导零)会导致==失败,此时必须统一格式或改用bytes.Equal
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











