难倒AI的一道C++入门练习题-20250216
前言
本文目的:在日常中为孩子们介绍AI领域的最新进展,顺带『纠偏』一些错误的认知。
背景
快要开学了,学校信奥培训群里开启了【每日一练】模式。
学习重在日常积累,往往在一些很简单的细节,就看你能否从中挖掘出有价值的点。
虽然是非常简单的入门题,但稍微拓展一下就很有意思。
注意:教、学编程,重点是思维方法,而不是“语法”。
“语法”完全可以自学,也应该自学。
于是,考验了一下孩子,也顺带考验了一下几个 AI 模型。
如果对AI模型的情况不感兴趣,
可以忽略中间各AI模型回答的过程,
直接看结论。
入门练习 题目
这道题很简单,看题目中(L3)字样,貌似对应某个C/C++认证的 L3 级别。
题外,这些所谓认证好比参加围棋级位赛、段位赛
——得先从25级开始考,一个月一级,拿到1级证书才能考业余1段,以此类推。
(各地差异,有的省市只需要从15级或9级开始考)
表面说是“普及”,本质就是扩大剪羊毛的范围,吃相很难看。
——如果有这个能力,为什么不能跳级?反正达不到要求,过不了测评,对吗?
推测练习的目的是让学生熟练掌握 if-else 多条件分支。
在此之上,我们加点儿难度——先按题目要求实现,然后优化:
不能使用 if-else 条件分支、三目运算符、switch,不能使用数组,代码清晰、准确,尽可能优化。
——这要求其实不算高,但小小的考验代码能力和推理能力。
参赛选手
邀请了几位 AI 选手作为陪练:
DeepSeek v3 和 R1
Kimi
字节豆包
阿里千问
腾讯混元讯飞星火ChatGPT/GPT4o/mini 1 Preview
第一轮作答
第一轮都没问题,答案大同小异,中规中矩,选部分有代表性的选手的答案展示如下:
字节豆包
#include <iostream>
using namespace std;
int main() {
int student_id;
// 读取输入的学号
cin >> student_id;
// 获取学号的尾数
int last_digit = student_id % 10;
// 根据尾数判断组号
if (last_digit == 1 || last_digit == 4 || last_digit == 7) {
cout << 1 << endl;
} else if (last_digit == 2 || last_digit == 5 || last_digit == 8) {
cout << 2 << endl;
} else {
cout << 3 << endl;
}
return 0;
}
Kimi
#include <iostream>
using namespace std;
int main() {
int studentID;
cin >> studentID; // 输入学号
// 获取学号的尾数
int lastDigit = studentID % 10;
// 根据尾数判断组别
if (lastDigit == 1 || lastDigit == 4 || lastDigit == 7) {
cout << 1 << endl; // 输出第1组
} else if (lastDigit == 2 || lastDigit == 5 || lastDigit == 8) {
cout << 2 << endl; // 输出第2组
} else {
cout << 3 << endl; // 输出第3组(包含尾数为0、3、6、9的情况)
}
return 0;
}
其它选手就不展示了,完全一模一样,最多变量名和命名规则略有差异,比如豆包是 python 风格,kimi 是 c/c++ 的驼峰命名风格。
DeepSeek V3
#include <iostream>
using namespace std;
int main() {
int studentId;
cin >> studentId; // 输入学号
int lastDigit = studentId % 10; // 获取学号的最后一位数字
if (lastDigit == 1 || lastDigit == 4 || lastDigit == 7) {
cout << 1 << endl; // 第1组
} else if (lastDigit == 2 || lastDigit == 5 || lastDigit == 8) {
cout << 2 << endl; // 第2组
} else if (lastDigit == 3 || lastDigit == 6 || lastDigit == 9 || lastDigit == 0) {
cout << 3 << endl; // 第3组
}
return 0;
}
评论:中规中矩,与前两位 AI 同学差不多——但,最后一个 else if 条件判断是多余的。
DeepSeek R1 深度思考
一大串【心路历程】就跳过不粘贴了,太长了。直接上结果:
#include <iostream>
using namespace std;
int main() {
int num;
cin >> num;
int digit = num % 10;
int group[] = {3, 1, 2, 3, 1, 2, 3, 1, 2, 3};
cout << group[digit] << endl;
return 0;
}
好吧,也算聪明,用了数组的小技巧,第一轮中属于优秀。
人类选手
下面是人类选手作答:
先对10求模获得尾数(个位数字)——各位 AI 同学一样正确;
再用尾数进一步对3求模,初步获得分组数字——各位 AI 同学这里用条件分支,R1用数组;
最后用三目表达式修正余数为0的情况。
各位 AI 同学的代码——说实话,哪位老师这么教学生,我立马要求退费。
某人:不服,这是按 L3 的教学要求掌握 if-else !
打脸:L3 没教变量么?那么多输出不能合并成一句? cout << group << endl;
——偏偏看群里其它同学们的答案,显然老师就是这么教的。
——个别同学明显是在其它机构学习的,答案也一样。
记住:我们学编程不是为了成为【编程爱好者】,这方面关键是训练严谨性。
自学多好,锻炼学习能力,还节约时间。
#include <iostream>
using namespace std;
int main()
{
int id;
cin >> id;
if (id < 1 || id > 49) // 范围检查
return -1;
// 求学号的尾数(个位)
int lastDigit = id % 10;
// 对尾数求 3 的余数,作为组号
int group = lastDigit % 3 == 0 ? 3 : lastDigit % 3; // 利用三目运算符简化判断
cout << group;
return 0;
}
各位 AI 同学,不知道是不是一样的训练数据,必须上难度,进入第二轮。
第二轮作答
要求不能使用条件分支、switch、三目运算符和数组。
这时,个别 AI 模型出问题了——忘了刚才聊啥,直接答非所问。
显然,AI 的会话衔接出了问题。
可是,一年多了还这样,很不应该啊!
字节豆包刚出来的时候曾经这样,但最近几个月已经飞速成长。
但讯飞星火、腾讯混元,你们一直在摸鱼么?
字节豆包
#include <iostream>
int main() {
int student_id;
std::cin >> student_id;
int last_digit = student_id % 10;
int group = 1 + (last_digit + 2) / 3;
std::cout << group << std::endl;
return 0;
}
思路正确,答案错误。
而且,重新问,第7行有所变化,但依然是错的:
int group = ((student_id - 1) % 3) + 1;
Kimi
它先回答了一个临时答案,同时继续优化,尝试去掉三目运算符,给出最终答案。
整个过程有点儿“思维链”的意思,自问自答、自我否定,深入推导,最终达到目的。
但,前面的临时答案不仅使用了三目运算符,关键是数学计算还错了:
#include <iostream>
using namespace std;
int main() {
int studentID;
cin >> studentID; // 输入学号
// 获取学号的尾数
int lastDigit = studentID % 10;
// 计算组号
int group = (lastDigit % 3 == 0) ? 3 : (lastDigit + 2) / 3;
cout << group << endl; // 输出组号
return 0;
}
它继续优化,修改了关键的第 12 行,避免使用三目运算符,可惜数学部分还是错的:
int group = (lastDigit % 3 == 0) * 3 + ((lastDigit + 2) / 3) * (lastDigit % 3 != 0);
惜败。
清华智谱
网页版 chat 有些小毛病,输出时代码没有正确的放在 MarkDown 格式的代码块中,导致格式混乱。
但 VSCode 中的插件 CodeGeeX 就没问题。速度快,质量好,第一次第二轮完美作答:
#include <iostream>
using namespace std;
int main() {
int id;
cin >> id;
if (id < 1 || id > 49) // 范围检查
return -1;
// 求学号的尾数(个位)
int lastDigit = id % 10;
// 对尾数求 3 的余数,作为组号
int group = (lastDigit + 2) % 3 + 1;
cout << group;
return 0;
}
阿里通义千问
(通义千问自动转到『代码模式』)
很意外,第一轮,第二轮都很棒,甚至加了数据验证(我教小朋友必须做数据验证)。
#include <iostream>
using namespace std;
int main() {
int student_id;
cin >> student_id;
if (student_id < 1 || student_id > 49) {
cout << "Invalid input. Please enter a number between 1 and 49." << endl;
return 1;
}
int last_digit = student_id % 10;
int group_number = (last_digit + 2) % 3 + 1; // 将尾数映射到组别
cout << group_number << endl;
return 0;
}
但,第二次测试,答案错误:
#include <iostream>
using namespace std;
int main() {
int studentID;
cin >> studentID; // 输入学生的学号
// 获取学号的尾数,并将其调整到[0, 9]区间内
int lastDigit = (studentID - 1) % 10;
// 计算组号:每3个数字一组,加上1是为了将结果调整到[1, 3]区间内
int groupNumber = (lastDigit / 3) + 1;
// 特殊情况处理:如果尾数是0,应该属于第3组
if (lastDigit == 9) {
groupNumber = 3;
}
cout << groupNumber << endl;
return 0;
}
第三次测试,又正确了。
——不稳定。
——不过,不是一个AI模型如此,是目前阶段,所有AI模型,的通病。原因在最后会解释。
DeepSeek V3
DeepSeek V3 说它避免了三目运算符???
——幻觉?
——同样,幻觉不是一个AI模型的问题,是通病,不必苛责,更别盲目捧杀。
提醒它不能使用三目运算符,修正后的答案:
#include <iostream>
using namespace std;
int main() {
int studentId;
cin >> studentId; // 输入学号
int lastDigit = studentId % 10; // 获取学号的最后一位数字
// 利用数学公式计算组号
int group = (lastDigit % 3) + (lastDigit % 3 == 0) * 3;
cout << group << endl;
return 0;
}
——正确!👍👍👍🌹🌹🌹
DeepSeek R1 深度思考
#include <iostream>
using namespace std;
int main() {
int num;
cin >> num;
int digit = num % 10;
int group = ((digit % 3) + 2) % 3 + 1; // 数学转换公式
cout << group << endl;
return 0;
}
好吧,第8行其实接近了,显然还需要进一步简化:
int group = ((lastDigit % 3) + 2) % 3 + 1; // 数学转换公式
于是,引导它简化,可它又回到三目运算符的路线
——忘了前面的对话?这就有点儿低水平了。
不得已,又重复强调要求。
它经过一番复杂的思考,坚定地给了最终答案:
int group = (141 >> (digit % 3 * 2)) & 3;+ 2) % 3 + 1; // 数学转换公式
看看它的思考过程,才是哭死——过程中其实已经得到了两个正确答案,但 ... 都被自己否决抛弃了:
绿色框正确,蓝色框也正确但不是最优,红色框其实就是绿色框的答案,
它否决的原因是——它认为是原来的答案——又是幻觉?
其实,还是那个原因——AI并不真正理解“问题”和“答案”,不理解“知识”,甚至“不知道自己在干什么”。
经过引导的人类选手的答案
学习的本质是思考。
经过引导,人类选手采用了昨天新学的小技巧:利用 C++ 中 bool 值被视为整数0和1
(注意:可以这么做,但不推荐这种方法)进一步,利用余数的特性,省掉了逻辑判断。
下面的三行代码,将优化前后都放在一起,作为对比:
int group = lastDigit % 3 == 0 ? 3 : lastDigit % 3; // 利用三目运算符简化条件判断
int group = (lastDigit % 3) + 3 * (lastDigit % 3 == 0); // 利用逻辑值修正余数为 0 的情况
int group = (lastDigit + 2) % 3 + 1; // 利用数学规律简化计算
小结:个人观点
个别模型一年多了还在摸鱼
——连完整对话都无法继续下去的那两位。
清华智谱最让我觉得意外——当年的ChatGLM3很惊艳,尤其本地部署。
当时同期的Qwen等,甚至LlamA等等在本地部署时各种低智表现,相对来说ChatGLM3又快又好——可现在网页版居然连输出格式都控制不好。
——不过VSCode中的插件CodeGeeX表现良好,甚至第二轮第一次就正确作答,代码能力不错👍
DeepSeek 的确有优势,但不明显
在多次清空会话测试中,也经常出现离谱的答案;
同样,其它模型在多次测试中,也有偶尔正确回答的情况,比如豆包、GPT4o、o1-mini 和 o1-Preview,甚至通义千问和Claude 3.5 Sonnet 第二轮第一次就答对了👍——重新再问又错了😂
AI没有本质上的突破
这一轮热潮从 ChatGPT 出现时,当时我测试后的结论——【AI模型甚至不理解它自己“说”的内容,更别说真正理解这些内容背后的各种『知识点』】,只是【调优策略,让你觉得它像真人一样和你聊天,甚至能顽梗——“让 AI 聊天时更有人味儿”】,这是【取巧/讨好】去迎合所谓的体验感,并不是真正意义上的的【智能】。
所以,虽然真正人工智能的临界点可能随时到来——但还没有到来;
——这一切,与我二十五年前跟着老师尝试基于神经网络的人工智能时相比,没有本质上的突破。
DeepSeek 有进步有优势,值得点赞,但不要盲目夸大
Google/Meta等开源时,我曾经推测:在算法、训练/推理/调优甚至数据集方面,都有很大的优化空间。尤其国内有几个团队在这些方面深入去钻研,迟早会出爆发性的成果。
——但,这样的成果,并不是那些媒体、自媒体、营销号【人云亦云】甚至【夸大、吹嘘】的那样的飞跃、突破。反而,是【现象级】的。
——因为,无论其它公司,甚至 DeepSeek 自己,后续一定还会有新的、类似的突破。引爆了第一波的刚好是DeepSeek,不妨继续期待吧。
DeepSeek 究竟好不好?
DeepSeek V2发布没多久,我除了使用付费的 ChatGPT/Github Copilot,开始付费使用 DeepSeek 并推荐给朋友们——显然我很认可它。
它很接近同期的 GPT4,而且很便宜,但它很慢,很罗嗦(按token计费)。当时我推测,它的慢有两种可能:1. 投入的推理算力资源不够;2. 模型推理效率不够。具体哪一种情况或者两种皆有,不知道。
但因为反应慢,我使用 DeepSeek 的时候很少——先用免费的,它们解决不了的问题,或者表现不尽如人意的时候,再使用 GPT4 和 DeepSeek。
所以,我使用 DeepSeek 更多是作为与GPT对比的样本,毕竟国产模型中我认为它是最接近GPT的,甚至比我很曾经期待的ChatGLM3/4还要好。
另外,Qwen在本地部署中,出现了很多胡言乱语,表现还不如同级别的LlamA,可能是对推理算力要求较高的原因,在线版使用中没那么明显的问题。
市场影响
最近这波热潮,肯定极大的促进了某些机关国企采购 DeepSeek 的离线部署版本(及硬件)。说实话,以这些甲方的尿性,必然又要浪费很多经费了(上两轮大量闲置、淘汰的云存储、云计算,部分小县城甚至动辄几千万几亿砸在里面,却“通电即亏损”)。不过,其中拿来代替某些摸鱼的模型的,至少也算是进步吧。
“导致英伟达股价大跌”
这类的胡言乱语,又蠢又坏,徒增笑尔。