AI 写的代码很漂亮,但时间复杂度可能已经爆炸
最近这半年,身边越来越多同事开始用 AI 写代码。效果确实惊艳:函数命名规范、注释齐全、代码风格统一,甚至比很多老手写得还整洁。
但用得多了,我发现一个挺隐蔽的问题——代码好看,不代表代码好用。尤其是时间复杂度这块,AI 经常在你完全没意识到的地方,埋下一颗性能炸弹。
一、为什么”好看”和”高效”是两回事
AI 生成代码的训练目标,本质上是”像人类会写的代码”,而不是”性能最优的代码”。这两者在很多场景下是重合的,但在涉及算法复杂度的地方,往往会分道扬镳。
举个最常见的例子:判断两个数组有没有重复元素。AI 很可能会写出这样的代码:
function hasDuplicate(arr1, arr2) {
for (let i = 0; i < arr1.length; i++) {
if (arr2.includes(arr1[i])) {
return true;
}
}
return false;
}
这段代码读起来非常顺畅:命名清晰,逻辑一目了然,甚至连边界情况都不用担心。
但如果你稍微想一下 includes 是怎么实现的——它每次都要把 arr2 从头扫到尾。也就是说,外层循环跑 arr1 的每一项,内层又要扫一遍 arr2,这就是一个双重循环,时间复杂度是 O(n×m)。
如果两个数组各有一万条数据,这就是一亿次比较。放在本地跑个 demo 感觉不出来,但一旦搬到生产环境处理真实数据量,页面直接卡死、接口超时,都有可能。
而这段代码只要改成用 Set 存一份 arr2,查找就能从 O(m) 降到 O(1),整体复杂度降到 O(n+m),性能能差出几个数量级。
但从”代码好不好看”的角度,两种写法几乎没有区别——都很整洁,都能过审查。
二、AI 为什么会写出这种代码
这不是 AI”不会”高效算法,而是有几个结构性原因,导致它更容易给出”能跑但不够快”的答案。
第一,训练数据的分布问题。 互联网上能被抓取到的代码,大部分是教程、博客示例、面试题解答,甚至是刻意为了”讲清楚逻辑”而牺牲性能写的示范代码。这类代码天然就倾向于用最直观、最容易理解的写法,而不是最优写法。AI 学的就是这个分布。
第二,AI 不知道你的数据规模。 同样一段查找逻辑,如果数据量只有几十条,双重循环完全没问题,用 Set 反而是过度设计。AI 在生成代码时,通常只能看到函数签名和局部上下文,它并不知道你线上跑的数据到底是一百条还是一百万条。没有这个信息,它只能给一个”通用但不一定优”的答案。
第三,需求描述往往只提功能,不提约束。 我们让 AI”写一个查重函数”时,很少会补一句”注意这里数据量很大,请优化时间复杂度”。AI 会默认满足功能正确性就是任务完成,性能是一个隐藏的、需要额外提出的需求,而不是默认包含在”写好代码”里的。
第四,复杂度问题往往藏得很深。 单看一个函数,可能只是一个双重循环,看起来还好。但如果这个函数被嵌套在另一个循环里调用,或者被高频触发(比如列表渲染、实时搜索),复杂度会被指数级放大。AI 在生成单个函数时,很难预判到它未来会被怎样使用。
三、这些”隐藏炸弹”通常长什么样
除了刚才的双重循环查重,实际开发中还有几类很典型的复杂度陷阱,AI 特别容易踩:
在循环里做数组查找或删除。 比如用 array.indexOf()、array.includes()、array.filter() 放在 for 循环内部,每次调用看起来都很轻量,但循环次数一多,总体开销会迅速累积。
递归没有做记忆化。 比如经典的斐波那契数列,AI 经常给出最直观的递归写法,看着简洁优雅,但没有缓存中间结果的话,复杂度是指数级的,n 稍微大一点程序就卡住不动了。
字符串拼接在循环里反复进行。 在某些语言里,字符串是不可变对象,每次拼接都要重新分配内存、复制一遍旧内容。如果循环次数很多,累积开销会很可观。AI 生成的代码经常忽略这一点,直接用 += 一路拼下去。
对象/数组的深拷贝被滥用。 为了”保险起见避免修改原数据”,AI 有时会在每次操作前都做一次深拷贝。单次操作看不出问题,但如果这个操作被频繁调用,拷贝的开销会成为真正的瓶颈。
这些代码有个共同特点:单独拿出来看,语法没错、逻辑没错、风格也挑不出毛病,唯独藏着一个只有在数据量上去之后才会暴露的性能陷阱。
四、怎么用得更放心
不是说 AI 写代码不能用,而是要清楚它的能力边界在哪,然后用几个简单的习惯把风险管住。
明确告诉 AI 数据规模和使用场景。 让它写查找逻辑时,顺手补一句”这个数组预计有几万条数据””这个函数会在滚动加载时高频调用”,AI 拿到这些约束后,给出的方案通常会好很多。
看到循环嵌循环,多问一句复杂度。 只要代码里出现循环嵌套、递归、或者循环体内调用了数组的查找类方法,养成习惯去估算一下最坏情况下要跑多少次,尤其是这段代码处理的数据量在未来可能增长的情况下。
直接让 AI 自己分析复杂度。 把生成的代码贴回去,问一句”这段代码的时间复杂度是多少,有没有优化空间”,AI 通常能给出准确的分析和替代方案——它不是不懂,只是需要你主动问。
关键路径务必做压测。 对于会被频繁调用、或者处理数据量较大的核心逻辑,不要只满足于”功能测试通过”,找真实量级或者接近真实量级的数据跑一下,复杂度问题在小数据量下几乎不会暴露,只有放大之后才现原形。
写在最后
AI 写代码这件事,已经从”能不能用”变成了”怎么用得更好”。它确实把很多重复性、格式性的工作解放了出来,代码可读性也普遍在提升。
但复杂度这个维度,恰恰是最容易被”看起来很专业”所掩盖的地方——毕竟一段代码好不好看,一眼就能看出来;而它到底跑得快不快,往往要等数据量真正上去之后才见分晓。
说到底,AI 现在更像一个经验丰富但不了解你业务全貌的助手,它能把代码写得很规范,但没办法替你判断这段代码将来会跑在什么量级的数据上。这部分判断,还得靠写代码的人自己兜底。