【杂谈】隔行如隔山:如何正确看待算法竞赛与软件工程
xyber_nova · · 科技·工程
在计算机科学的浩瀚海洋中,算法竞赛与软件工程往往被视为两座遥遥相望的岛屿。
一直以来,关于“OIer 的代码风格烂不烂”或者“工程派是不是只会调包”的争论从未停止。然而,近期社区和网络上发生的一些现象,让我不得不重新思考这个问题。我认为,争论的核心不在于谁的技术栈更高贵,而在于对他人的领域缺乏基本的敬畏之心。
正如一句老话所说:“任何人至少应当只在自己的专业领域内发表意见,而对于自己不懂的另一领域,应当保持谦虚、谨慎的态度。”
遗憾的是,我们见到太多反例。
拿着锤子,看什么都是钉子
算法竞赛的核心目标是在极短的时间内、极其有限的资源下,解决一个定义明确的数学或逻辑问题。代码的生命周期往往只有几毫秒(运行时间)到几小时(比赛时间)。
而软件工程的核心目标是构建可维护、可扩展、高可用的系统。代码的生命周期可能长达数年。
这两种目标决定了思维方式的巨大差异。然而,总有一些自诩拥有“工程视野”的人,喜欢居高临下地指点江山。
大家或许还记得洛谷讨论区曾出现过的 CleanIce 事件。
起因仅仅是一个关于 __int128 的简单科普。对于 OIer 来说,__int128 是一个在处理大数取模或中间溢出时非常好用的 GCC 扩展特性,只要评测机支持(现在的 NOI Linux 2.0 均支持),它就是一把利器。
然而,CleanIce 却在讨论区中大谈特谈软件工程术语。他从兼容性讲到 C++ 标准委员会的决策,从可移植性讲到工程规范,甚至对广大 OIer 的计算机科学素养表示鄙夷,认为自己有义务给这群“只会写算法”的小白“科普”什么是真正的编程。
专业计算机考试都不建议使用,因为它不在 C++ 标准内。
而 CSP 和 NOI 属于非专业计算机考试。事实也如此,大多数 oier 除了会点算法以外,对计算机科学一窍不通。
(引自 CleanIce 关于
__int128)
结果呢?被管理员批判,被社区群嘲。
为什么?因为他脱离了语境。在算法竞赛的语境下,我们不需要考虑这段代码是否能在嵌入式设备上运行,也不需要考虑五年后谁来维护这段 __int128。我们只需要它在此时此刻,帮我们通过那 10 个测试点。用企业级的代码规范来约束竞赛代码,就像要求短跑运动员必须穿着西装皮鞋上跑道一样荒谬。
误人子弟的“专家”
如果说 CleanIce 只是傲慢,那么活跃在小红书等平台的某些“信奥赛名师”,则是纯粹的无知且误人子弟。
典型的例子便是那位自称拥有深厚软件工程背景、大厂经验丰富的博主“信奥赛编程李老师”及其簇拥者。
这位“李老师”虽然标榜自己工程经验了得,却连 NOI 系列赛事的基本常识都匮乏得令人发指。他甚至不知道 CSP、NOIP 和 NOI 之间的晋级关系和赛制区别,搞出来所谓“NOIP 一等就是省队大名单,后面还有冬令营,最后才是省选等等”,但这并不妨碍他向家长和学生兜售他的“高端编程理念”。
最令人啼笑皆非的,是他教导学生在比赛中使用的那些“工程化写法”:
-
教学生用
std::print:C++23 才引入的
std::print,确实是现代 C++ 的进步。但在算法竞赛中,绝大多数评测环境(包括 NOI Linux 2.0 默认环境)根本不支持 C++23 标准。教学生在考场上写一个必然会导致 CE (Compilation Error) 的函数,这无异于谋杀选手的成绩。 -
return EXIT_SUCCESS:在算法竞赛中,
return 0;是刻在 DNA 里的肌肉记忆,简洁明了。而李老师坚持认为要写return EXIT_SUCCESS(定义在<cstdlib>中),理由是“更具可读性”、“符合规范”。在分秒必争的赛场上,让选手多敲这十几个字符,还要多引入一个头文件,除了满足所谓的“工程洁癖”外,对解题没有任何帮助。
更何况,软件工程上虽然反对
MAGIC_NUMBER(魔法数字,指未在上下文语境内明确表明自身含义的特殊数字,容易导致代码丧失可维护性)的使用,但是return 0;完全是一个语义明确的表达方式,在 Redis^1 等知名项目中也广泛使用。
这种行为,本质上是用错误的领域知识来指导另一个领域的实践。他们用软件工程的“政治正确”来掩盖自己对算法竞赛规则和环境的无知,最终受害的是那些信任他们的学生。
隔行如隔山,请保持谦卑
写这篇文章,并不是要挑起 OIer 和 软件工程师 的对立。相反,优秀的 OIer 往往在进入业界后能快速掌握工程技能,而优秀的工程师也懂得算法的重要性。
问题的关键在于:上下文。
- 对 OIer 来说:不要因为自己会写线段树就看不起 CRUD,工程中的架构设计、并发控制、系统稳定性同样博大精深。
就比如说,知名的数据库 MySQL 和 PostgreSQL 中也大量用到了 AC 自动机、B+ 树等高级数据结构。软件工程的难点在于要如何将这些算法和数据结构高效且易于维护地组织起来,自然要诞生各种繁琐的代码规范。
- 对工程派来说:请尊重算法竞赛的特殊性。这里的代码是为了 AC (Accepted) 而生,不是为了 code review 而生。
当你想要跨界发表意见时,请先问自己几个问题:
- 我了解这个领域的规则吗?(比如 NOI Linux 的编译器版本)
- 我的建议在这个场景下实用吗?(比如
std::print能过编译吗?) - 我是抱着交流的心态,还是抱着“降维打击”的傲慢?
真正的强者,从不吝啬对他人的尊重,也从不随意在自己未知的领域指手画脚。
愿我们在追求技术的道路上,少一些 CleanIce 式的傲慢,少一些“李老师”式的笑话,多一份对技术本身的敬畏。
注
声明:本文在写作完成后使用 Gemini 3 Pro Preview 进行润色并填充细节,作者为本文中所述事实的准确性负责。