编辑距离适合短文本比对,如用户输入纠错、ID校验、关键词模糊匹配;长文本(如几百字正文)不宜直接用Levenshtein,因计算慢且语义相关性差。

编辑距离:用 levenshtein 算字符串差异,但别直接比长文本
编辑距离适合短文本(比如用户输入纠错、ID 校验、关键词模糊匹配),对两段几百字的正文直接算 levenshtein 会非常慢,而且结果意义不大——“今天天气不错”和“今日气候良好”编辑距离可能高达十几,但语义很近。
实操建议:
- 只在
len(str1) 时考虑用 <code>levenshtein - Golang 没有标准库函数,推荐用
github.com/yuin/goldmark/util/levenshtein或轻量级实现(避免引入大依赖) - 注意:默认计算的是字符级距离;中文要确保字符串以
rune切分,否则一个汉字被当多个字节算,距离失真 - 如果想归一化到 [0,1],用
1.0 - float64(distance) / float64(max(len(str1), len(str2))),但仅作粗略参考
余弦相似度:必须先分词+向量化,不能直接喂原文
余弦相似度本身只是向量夹角计算,Golang 不像 Python 有 scikit-learn 一键搞定。你得自己走完「分词 → 去停用词 → 构建词频向量 → 点积归一」这整条链,漏任何一环结果都不可信。
常见错误现象:cosine([]float64{1,0,0}, []float64{0,1,0}) == 0,但原文明明都含“机器学习”,只是词序不同——说明没分词,或分词颗粒度太粗(比如按字切,而不是按词)。
立即学习“go语言免费学习笔记(深入)”;
实操建议:
- 中文分词必用
github.com/go-ego/gse(轻、准、支持自定义词典),别手写正则切 - 停用词表至少包含“的”“了”“在”“是”等高频虚词,否则
“Python 是一门语言”和“Java 是一门语言”相似度虚高 - 向量维度不要硬编码成 1000;用
map[string]int统计词频,再统一映射到相同 key 序列,避免索引错位 - 小文本(
实际项目中怎么选:看场景,不是看算法名字
没有“更优算法”,只有“更合适场景”。比如做日志异常检测,两行日志结构一致、只差几个字段值,levenshtein 快且有效;但做客服工单聚类,就得上余弦+TF-IDF,否则“退款”和“退钱”永远不相识。
使用场景对照:
- 接口参数校验、命令行拼写建议 →
levenshtein+ 编辑距离阈值(如 ≤3) - 新闻标题去重、FAQ 匹配 → 余弦相似度 +
gse分词 + 词干简化(如“运行”“运行中”→“运行”) - 长文档(>500 字)比相似?别硬算——先用 TF-IDF 提取关键词,再比关键词集合 Jaccard 距离,快十倍,效果不差
容易被忽略的坑:编码、大小写、空白符
Golang 字符串默认 UTF-8,但很多文本来源(比如 HTTP POST、CSV 文件)可能带 BOM、\r\n 混用、全角空格、零宽字符。这些不会报错,但会让 levenshtein 多算几步,让分词器切出奇怪 token。
实操建议:
- 预处理必加:
strings.Map(runeReplace, strings.TrimSpace(str)),其中runeReplace过滤 \u200b、\ufeff、\u3000 等 - 英文文本统一转小写再进余弦流程,否则
"Go"和"go"被当两个词 - 测试时用真实数据跑,别只用
"hello world"和"hello world!"—— 那种 case 编辑距离=1,掩盖了中文分词失效的问题
事情说清了就结束。真正难的不是调哪个函数,而是判断“这段文本到底该用什么粒度比、比什么、比到什么程度才算够”。


















