计算机二级Python中的random库:从题型解析到上机实操全攻略
每年计算机二级考试一到考前冲刺阶段,问得最多的问题就是"random库到底要掌握哪些函数""洗牌和抽样用的都是哪个方法"——说实话,这个库在Python标准库里属于"看着容易、考起来很刁钻"的类型。它不像print、for循环那样一看就懂,但因为它出题组合性强,选择题一道、编程题经常作为功能模块被嵌套在应用题里,所以备考阶段花半天时间把它彻底吃透,性价比其实非常高。
这篇文章我把random库在二级考试里可能出现的所有考察角度过一遍:先看清楚考纲到底圈定了什么范围,再把核心函数的用法逐个拆开,结合考试常见题型给出可以直接背诵的代码模板,最后分享几个我在上机练习时踩过的坑。不管是刚开始复习还是临近考试想突击,按这个顺序过一遍都能把random相关的分拿稳。
1. 先摸清二级考试对random库的"圈题范围"
很多同学备考标准库时有个误区:看到官方文档一大堆函数就慌,觉得每个都要背。实际上计算机二级Python科目(考试代码Python,大纲对应《全国计算机等级考试二级Python语言程序设计考试大纲》)对random库的考察范围非常明确,不会超纲到让你手写Mersenne Twister算法,也不会考random模块底层实现的数学原理。你得先知道它的边界,复习才不会扔进大海捞针。
1.1 考纲规定的必备函数清单
我对照大纲和历年真题,把random库在二级考试中的高频函数整理如下:
| 函数 | 功能 | 出题频率 | 备注 |
|---|---|---|---|
random.random() | 生成[0.0, 1.0)的随机浮点数 | 极高 | 选择、程序填空都常客 |
random.uniform(a, b) | 生成[a, b]或[a, b)区间的随机浮点数 | 中 | 注意两端点的开闭规则 |
random.randint(a, b) | 生成[a, b]的随机整数 | 极高 | 两端都是闭区间 |
random.randrange(start, stop[, step]) | 按步长在某范围内选整数 | 高 | 与range规则完全一致 |
random.choice(seq) | 从序列中随机选一个元素 | 高 | 序列可为列表、字符串、元组 |
random.choices(seq, weights=None, k=1) | 可带权重地选k个元素(可重复) | 中低 | 新大纲边缘考点 |
random.sample(seq, k) | 从序列中不重复地抽k个元素 | 中 | 返回新列表,不修改原序列 |
random.shuffle(seq) | 原地打乱序列顺序 | 高 | 会修改原列表 |
random.seed(a=None) | 初始化随机数种子 | 极高 | 上机题复现结果的关键 |
这9个函数就是复习主线。其余的random.getstate()、random.setstate()、random.getrandbits()在二级大纲里基本不考,备考时可以先放一边。有精力的同学了解名字就行,不要在边上耗时间。
1.2 二级真题里的random库到底怎么出
从真题分布来看,random库在考试里的出场方式大概有三种:
- 选择题直接考函数含义与参数:比如给一段代码问输出结果是什么,或者给一个需求让选"能实现这个功能"的函数。这种题考察的是你对函数边界条件的理解,比如
randint(1, 5)到底包不包括5,random()的范围到底是[0,1)还是[0,1]。 - 程序填空题考函数调用与参数搭配:给你半个程序,让你填出缺失的random函数调用。这类题通常埋在模拟类、抽奖类、抽样统计类的小程序里。
- 程序设计题把random作为功能模块:比如让你生成随机验证码、随机点名器、掷骰子模拟、抽样求平均值等。这种情况下一般不会单独考random,而是要求你综合运用列表、循环、条件判断,random只管"产生随机性"这一步。
所以结论很直接:备考策略不是把random库文档背下来,而是把上面9个函数的"输入参数、输出范围、是否原地修改、是否可重复"这四个维度的边界记牢,然后通过刷题把函数组合使用的感觉练出来。
2. 核心函数逐个拆解:参数边界比函数名更重要
random库的函数用法,说穿了没有什么高深的东西,但考试最喜欢在"边界条件"上做文章。我对每个核心函数做一个带陷阱标注的拆解,这些细节都是历年考生栽过跟头的地方。
2.1 random.random()与random.uniform():浮点数里的开闭区间
先看最简单的。
import random # random.random():生成[0.0, 1.0)之间的随机浮点数 x = random.random() print(x) # 可能输出 0.0,但永远不可能输出 1.0random.random()不接收任何参数,每次调用返回一个float类型的数据,范围是左闭右开——0.0可能被取到,1.0永远取不到。这个"右开"设计不是因为偷懒,而是和计算机表示概率区间的习惯一致:当你需要生成某范围内的小数时,[0, 1)这个区间做线性变换更不容易越界。
random.uniform(a, b)则是在两个指定数之间生成随机浮点数:
y = random.uniform(2, 3) # 生成[2, 3]之间的浮点数,也可能等于3.0这里有个很容易混淆的点:官方文档写的是"返回a与b之间的随机浮点数,包含两端点(取决于浮点舍入)"。也就是说,理论上uniform(2, 3)有可能返回2.0,也有可能返回3.0,但实际上由于浮点精度问题,取到端点的概率极小。考试判断题如果写"uniform产生的数不包含端点",那是错的;如果写"包含端点",在官方语义上算对。复习时知道它和random()的区别:random()是固定的[0,1)半开区间,uniform(a,b)与端点关系更宽松。
我个人建议大家记忆时用这样一个口诀:random()看成一个标准的电子骰子——最小为0,永远掷不出1;uniform(a,b)就是把这个骰子搬到a和b之间,端点理论上可能碰到。这样在选择题里判断范围时就不容易出错。
2.2 randint与randrange:整数取值的两副面孔
整数随机是考试出题率最高的点,randint和randrange经常混着考。
# randint(a, b):闭区间[a, b],两端都包含 a = random.randint(1, 6) # 模拟掷骰子:1, 2, 3, 4, 5, 6 都算合法输出 print(a) # randrange(start, stop, step):与range规则一致,包含start,不包含stop b = random.randrange(0, 10, 2) # 可能输出0, 2, 4, 6, 8,不可能输出10 c = random.randrange(10) # 等价于randrange(0, 10):输出0~9关键差异在于:randint(1, 6)的右边界6是闭的,能输出6;而randrange(0, 10)的右边界10是开的,永远输出不了10。类比一下:randint像是"抽中头和尾都算数"的福利彩票,randrange更像是range()那个序列生成器——写range(0, 10)的时候你也知道生成的数不到10。
另外randrange支持步长参数,这在模拟等差数列随机抽取时很实用。二级考试里出现过这样一道选择题:
random.randrange(1, 10, 3)问可能的输出是哪个。分析一下:1到9(因为右开)之间步长为3,所以可能值是1, 4, 7——10不可能,2不可能,0也不可能。这种题考的就是"你知不知道它和range的规则完全一致"。
2.3 choice、choices与sample:抽一个、抽多个(可重复)、抽多个(不重复)
这三个是"抽样家族",但它们之间最容易搞混。
# choice:从一个非空序列中随机选一个元素 seq = ["一等奖", "二等奖", "三等奖", "谢谢参与"] prize = random.choice(seq) print(prize) # 四者之一 # choices:可带权重地抽k个,允许重复 results = random.choices(["红", "绿", "蓝"], weights=[5, 3, 2], k=10) print(results) # 可能输出 ['红', '蓝', '红', '红', '绿', ...] # 注意:列表长度为k,很可能出现重复元素 # sample:不重复地抽k个,返回新列表 sampled = random.sample(range(100), 10) print(sampled) # 从0~99中抽取10个不重复的编号三者区别用生活化类比解释:
choice是"从碗里拿一颗糖";choices是"从碗里拿糖,拿一颗记一下口味,放回去,再拿一颗,又放回去……于是可能连续两次拿到同一个口味";sample是"一把抓走k颗糖,碗里带走的不可能重复,而且原碗里的糖总数会变少"。
考试中最常见的出题点是:sample抽取后原序列不变,只是返回一个新的列表;choices的权重参数是weights,传入的权重大小对应每个元素被选中的概率权重。注意choices的k不写默认为1,此时返回的是一个列表,而不是单个元素——random.choices(seq)的结果是[x]这种单元素列表,和random.choice(seq)返回x不同。这个细节在判断题、程序输出结果题里容易被设坑。
2.4 shuffle:会"原地改原列表"的破坏性操作
random.shuffle(seq)的作用是把列表顺序随机打乱。它的关键点在于:直接修改原列表,且返回值为None。
cards = [1, 2, 3, 4, 5] result = random.shuffle(cards) print(cards) # 顺序可能变成 [3, 1, 5, 2, 4] 之类 print(result) # 输出 None很多初学者在这里踩坑:写了new_list = random.shuffle(old_list),然后发现new_list是None——这是因为shuffle是原地操作,不是返回新列表。如果你需要保留原列表,得先复制再打乱:
import copy original = [1, 2, 3, 4, 5] temp = copy.copy(original) # 或 original[:] random.shuffle(temp) print(original) # 不受影响 print(temp) # 打乱后的副本另外,shuffle只对可变序列有效,比如列表;字符串和元组是不可变类型,无法原地shuffle。如果想打乱字符串,正确做法是先转成列表、打乱、再重新拼接。真题里有道程序填空题就是把字符串转列表、shuffle、再join,这个套路背下来。
s = "abcd" lst = list(s) random.shuffle(lst) s_shuffled = "".join(lst) print(s_shuffled) # 四字母随机重排2.5 seed:让"随机"变成"可复现",上机考试的关键点
random.seed(a)是设置随机数种子的函数。同一台机器上,只要种子相同,随后生成的随机数序列就完全一致。这个特性在二级考试里的作用极其重要——因为上机评卷需要可重复性。
random.seed(10) print(random.randint(1, 100)) # 第一次运行输出某个值 print(random.randint(1, 100)) # 第二次运行输出另一个值 random.seed(10) # 重新设定同样的种子 print(random.randint(1, 100)) # 又回到了第一次运行时的第一个值 print(random.randint(1, 100)) # 第二次运行时的第二个值这个机制背后的原理是:random库用的是伪随机数生成器,伪随机并不是"真正不可预测",而是由一个初始种子通过确定性的数学运算不断迭代产生数列。种子就是这条数列的"起点",起点一样,后面的数列必然一样。
二级考试常见的做法是:在程序开头给定random.seed(x)这样的固定种子(评卷时侯的测试用例里有指定),然后你后面的代码在同一个种子的约束下输出结果应该是确定的。这就意味着代码逻辑必须严格按评卷程序期望的调用顺序来写,不能随意在中间多加一次random调用——你多调用一次,序列就整体往后跳一格,最终结果就和答案对不上了。这个坑我在第4节会展开讲。
3. 随机种子与伪随机原理:理解这个,很多选择题就不用背
random库考来考去,绕不开"伪随机"这三个字。如果你只记函数签名而不理解伪随机机制,遇到稍微变形的题目就会懵;理解了机制以后,那些选择题基本就是送分题。
3.1 为什么是"伪随机"而不是"真随机"
计算机里的标准库random本质上是确定性算法:它从初始种子出发,通过一个递推公式不断生成看起来杂乱无序、但规律完全确定的数。也就是说,只要种子固定,生成的数列在世界任何一台电脑上都是一样的(除非Python版本不同)。这个数列在统计学上具有良好的均匀性和独立性,足以模拟日常场景里的随机需求,所以叫"伪随机"。
我在给初学者讲这个概念时经常用这样一个比喻:伪随机数生成器就像一本有十万页的"乱数表",种子决定了翻开第几页;从这一页开始,每往后读一行就是一个"随机数"。同一页开头读出来的序列永远一样。你做实验时设定random.seed(42),就相当于告诉程序"从第42页开始读"。
理解这一点对考试的意义是什么?
- 选择题里如果问"random库的随机数是真随机还是伪随机",答案一定是伪随机;
- 如果问"种子相同则随机序列是否相同",答案是一定相同;
- 如果问"random.seed()不带参数会怎样",答案是基于当前系统时间等随机源来自动生成种子,每次运行序列完全不同。
这些考点不用背,理解了机制自然就能选对。
3.2 种子在二级上机考试中的隐藏作用
二级Python的上机编程题有一部分是结果可验证的,这就要求考生的程序在给定的输入条件下必须产生确定输出。如果题目里用自己的随机过程而不设种子,不同考生会得到不同结果,评卷就没法统一了。所以历年真题里,只要程序设计题涉及到随机,几乎都会在题目开头或者题目叙述中给出种子设置:
例如某年真题模拟"大乐透号码生成",题目会写"在某行代码中设置随机种子为N"之类的提示。这时候你务必要在同一位置调用random.seed(N),并且保持后面的random方法调用顺序和官方标准答案一致。
这里要强调一个我见过的经典翻车案例:程序里需要生成5个随机数和1个额外随机数,有的考生先把5个数放循环里调用了5次randint,出来后又额外调用了1次random()来生成小数——但由于多加的这次调用改变了后续随机序列,前面的5个数已经变了。如果评卷程序是按"连续调用6个特定函数后比对结果"来判分的,这种代码分数就会被扣掉。
所以在写调用了random库的编程题时,需要遵循一个纪律:先规划好一共要调用几次random函数、以什么顺序调用,然后一次也不要多、一次也不少地写完。平时练习时养成这个习惯,上机才稳定。
3.3 从源码层面看一眼生成器:Mersenne Twister的"考试友好"特性
random库的默认生成器是Mersenne Twister(梅森旋转算法)。它的周期长达2的19937次方减1,分布均匀性和随机性都很好,而且算法实现高效、稳定。二级考试不会让你实现这个算法,但你至少要知道一件事:Mersenne Twister的输出质量足以模拟抛硬币、掷骰子、随机抽样这些日常随机场景,所以考试程序里的随机结果即使不设定种子,单独看也是"够随机"的。这给考生的启发是——在不需要复现结果的普通练习题里,你可以放心使用random库做模拟实验,不用担心输出分布明显偏差。
不过有一个小细节注意一下:Python 3.9之后,random.shuffle等函数内部实现有微小变化,但考试环境一般固定某个Python版本(通常是3.8或3.9),所以实际运行结果和评卷基准一致即可,不要在你的本地用新版本Python强行对齐某道真题的输出,容易出偏差。平时练习建议直接使用与考试环境相近的Python版本。
4. 历年真题高频题型与可直接抄用的代码模板
接下来这部分是实战环节。我结合自己在备考和辅导过程中反复遇到的高频题型,整理出几个可以直接背下来、考试时改改参数就能用的代码模板。建议每段代码都亲手敲一遍,别只是看。
4.1 模板一:随机验证码生成(字符串与random的经典组合)
生成n位随机验证码是二级考试老熟人,最常见的是生成四位、六位由数字加字母组成的验证码。
import random import string def generate_code(length=6): # 数字 + 大写字母 + 小写字母 chars = string.ascii_letters + string.digits code = "".join(random.choices(chars, k=length)) return code random.seed(2025) print(generate_code(6))这个模板用到了random.choices的k参数,一次性抽取length个字符,再用字符串的join拼起来。注意如果把choices换成sample,验证码里就不可能出重复字符;用choices则允许重复。题目要求"可重复"还是"不可重复"要看清,一字之差逻辑完全不同。
如果题目要求不使用string模块,也可以自己定义字符池:
import random def generate_code(length=4): chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789" code = "".join(random.choice(chars) for _ in range(length)) return code这里用到了生成器表达式,每循环一次取一个字符,取length次。两种写法都要会,尤其是第二种在考试环境里更稳——因为有的考场可能不允许导入string模块(其实标准库允许,但为了稳妥,会手写字符池更好)。我自己更喜欢第一种,因为它简洁,但教学生时我会强调第二种的容错性。
4.2 模板二:随机抽样与分组(sample和shuffle的应用)
抽样类题目在二级里经常伪装成"随机抽取几个幸运观众""随机分组""随机抽取若干题重做"等场景。核心区分在于:要不要重复、要不要改变原本顺序。
不需要重复,且不改变原列表 -> 用sample:
import random students = [f"学生{i}" for i in range(1, 31)] random.seed(5) # 从30人中不重复抽取5人 winners = random.sample(students, 5) print(winners)需要把顺序完全打乱(比如分组前洗牌)-> 用shuffle:
import random students = [f"学生{i}" for i in range(1, 31)] random.seed(5) random.shuffle(students) # 打乱后第1组取前10人,第2组取下10人,第3组取最后10人 group1 = students[:10] group2 = students[10:20] group3 = students[20:] print(group1, group2, group3, sep="\n")注意用shuffle时原students列表已经被修改了,所以group1、group2、group3里的人是互不重复且恰好覆盖全体的。有些同学喜欢用sample(students, 10)重复抽三次来分三组,这种方式在逻辑上是错的,因为sample每调用一次都是独立抽样,三组之间可能有人重复。
4.3 模板三:掷骰子、抛硬币等蒙特卡洛模拟
模拟类程序题通常要你统计大量随机实验的结果频率,核心套路是循环+计数。
import random random.seed(42) # 统计掷骰子10000次,各点数出现的次数 counts = [0] * 7 # 下标0不用,1~6对应用点数 for _ in range(10000): point = random.randint(1, 6) counts[point] += 1 for i in range(1, 7): print(f"点数{i}:{counts[i]}次,概率{counts[i]/10000:.4f}")这里[0] * 7是二级备考里非常实用的初始化技巧——需要统计1到6的点数,列表长度开7个,下标0白白空着,让下标和点数一一对应。很多新手喜欢用[0, 0, 0, 0, 0, 0, 0]写7个0,效果一样,但前者更简洁。
抛硬币统计正面次数的模板更有代表性:
import random random.seed(100) n = 1000 heads = 0 for _ in range(n): if random.random() < 0.5: heads += 1 print("正面次数:", heads) print("正面概率:", heads / n)值得注意的是:判断条件写random.random() < 0.5还是<= 0.5,理论上会略微影响结果(差异极小,单次运行看不出来),但在考试中你只需要保证逻辑自洽即可。评卷通常考察算法思想,而不是精确到某一次随机的结果,除非题目指定种子并要求完全一致的输出。
4.4 模板四:经典游戏化题目"猜数字"
猜数字几乎每年都会以某种姿态出现在二级复习资料里。它同时考察randint(生成目标数字)、循环(反复猜测)、条件判断(猜大猜小)。
import random random.seed(88) target = random.randint(1, 100) guess = -1 count = 0 while guess != target: guess = int(input("请输入你猜的数字(1~100):")) count += 1 if guess > target: print("猜大了") elif guess < target: print("猜小了") else: print(f"恭喜!你用了{count}次猜中")程序题中如果出现input(),你在本地测试时要手动输入数字,比较麻烦;有些模拟软件能自动输入。我更推荐的练习方式是预先构造一个猜数列表来模拟用户输入,比如for g in [50, 25, 80, 71]:,这样可以避免反复手动敲键盘。
这个模板看起来简单,但它有一个很多考二级的同学会忽略的考点:randint(1, 100)的取值范围是1到100闭区间,如果题目要求"随机生成一个1到99之间的整数",就必须用randrange(1, 100)才行。一字之差,答案边界完全不同。
4.5 模板五:随机密码生成与字符池权重定制
如果题目要求生成带一定规则的密码,比如"必须包含至少一个大写字母、一个小写字母和一个数字",只靠一个choices就不够严谨,需要分步保证:
import random import string def strong_password(length=8): lower = random.choice(string.ascii_lowercase) # 保证有1个小写 upper = random.choice(string.ascii_uppercase) # 保证有1个大写 digit = random.choice(string.digits) # 保证有1个数字 remaining = "".join(random.choices(string.ascii_letters + string.digits, k=length-3)) # 把4部分拼起来后打乱顺序,避免"大写字母永远在开头"这种规律性 password_list = list(lower + upper + digit + remaining) random.shuffle(password_list) return "".join(password_list) random.seed(7) print(strong_password(8))这里最容易被忽略的点是最后一步shuffle。如果不打乱,生成的密码永远是"小写+大写+数字+随机字符"这个固定结构,题目一旦要求"随机排列"就得不到满分。把字符串拆成列表、shuffle、再拼接,是二级考试里"字符串+random"组合题的万能收尾操作。记住这个三步走:list()->shuffle->"".join()。
5. 上机实战:环境准备、典型报错与得分细节
会写代码模板只是第一步,上机考试时能否顺利运行、能否拿全分,还取决于一些很容易被忽略的细节。我这部分把实际练习时的经验浓缩起来,按"考前准备 -> 调试排错 -> 检查提分"三个阶段来梳理。
5.1 考场环境与本地练习环境的一致性
计算机二级Python考试的环境通常是Windows系统上的Python 3.8或3.9(不同考区可能略有差异),考试系统自带IDLE或者特定的考试客户端。你在自己电脑上练习时,建议提前确认:
- Python版本:尽量装3.8或3.9,不要用Python 3.12/3.13这种很新的版本。新版本虽然语法向下兼容,但某些标准库的随机序列生成细节若有微调,可能导致真题输出难以复现。
- 不需要额外安装任何第三方库:random是标准库,二级考试范围也只考标准库,考试机器上不会给你装numpy、pandas这些东西。平时练习如果装了Anaconda,要注意别在代码里顺手import不在考纲内的模块。
- 中文编码:上机考试环境可能涉及文件读取,如果代码里出现中文字符串,务必保证文件头有
# -*- coding: utf-8 -*-或确保编辑器默认UTF-8。有些考场客户端默认编码不是UTF-8,会导致读取含中文的文本文件时报UnicodeDecodeError。
用本地命令行验证一下环境:
python --version python -c "import random; print(random.randint(1,10))"两条命令能正常输出,说明random库没问题,可以拿真题开练。
5.2 经典报错场景与修复:实战排错清单
我在辅导过程中见过大量新手在上机时反复报错的场景,集中列出来,每个都是真实的坑:
场景一:TypeError: 'NoneType' object is not iterable / 对shuffle结果继续操作
import random lst = [1, 2, 3, 4, 5] new_lst = list(range(len(lst))) # 错误示范 # 先写 new_list = random.shuffle(lst) # 然后又遍历 new_list,结果 new_list 是 None —— 报错!修复方式在第2.4节已经说过:shuffle是原地操作,重新赋值给变量会拿到None。要么直接使用原列表,要么先复制再shuffle。
场景二:IndexError: Cannot choose from an empty sequence
import random empty = [] print(random.choice(empty)) # 报错:IndexErrorchoice要求序列非空。考试题里如果从一个筛选后的列表里抽元素,先判断len(lst) > 0再加choice,否则遇到空列表就崩。这个坑常在"从符合某种条件的数据里随机抽一个"的题目里出现。
场景三:ValueError: Sample larger than population or is negative
import random lst = [1, 2, 3] print(random.sample(lst, 5)) # 报错:样本数超过总体数sample的k不能大于序列长度(不允许重复抽取的前提下)。如果题目要求"可能有重复地抽5个",就改用choices;如果题目要求"不重复地抽5个但序列只有3个元素",必须在抽取前扩容序列或改换思路。
场景四:因为随机种子设置时机不对导致结果不对
import random print(random.randint(1, 100)) # 第一次调用 random.seed(10) # 种子设在调用之后,前面已经产生随机数,种子对它无效 print(random.randint(1, 100)) # 这已经是种子的第一个随机数很多考生习惯在程序中间任何位置写random.seed(10),导致面对真题时前面已经消费了几个随机数、后面结果对不上。正确做法是:一旦题目指定种子,程序开头、所有random调用之前先执行seed,确保第一个随机数就是评卷程序期望的第一个随机数。
5.3 考试检查清单:交卷前5分钟该看什么
上机考试时间紧凑,但交卷前留几分钟检查random相关代码绝对值得。我把检查要点列成清单,你可以对照自查:
- [ ] 是否在所有random调用之前设置了
seed(如果题目要求)? - [ ] 程序中
random调用次数是否和标准思路一致?有没有多写或少写? - [ ] 使用
shuffle后是否误用返回值?是否正确操作了原列表? - [ ] 使用
sample抽取的k是否不大于序列长度? - [ ]
randint的右边是闭区间,确认题目要的是闭还是开;如需要开区间请改用randrange。 - [ ] 字符串拼接随机字符时,字符是否先转成str类型?特别是用
choice选出的数字,直接拼接会报TypeError。 - [ ] 如果用了
input(),输入的数据是否做了int类型转换? - [ ] 程序有无额外导入非标准库模块?
其中第六点特别值得展开提醒:用random.choice("0123456789")选出来的是字符'5',直接拼接没问题;但如果用random.randint(0, 9)选出来的是整数5,直接拼字符串就会报错。正确写法是str(random.randint(0, 9))。这个细节至少值一分。
5.4 一个完整的交叉综合例题:真题级别的实战演练
为了把上面的知识点串起来,我最后给出一道和真题难度相当的综合性练习题,建议你先自己写一遍,再对照我的参考实现。
题目:模拟一次年会抽奖程序。参加人数共50人,编号从1到50。要求:
- 先随机选出3名三等奖;
- 再从剩下的人中随机选出2名二等奖;
- 最后从剩余的人中选出1名一等奖;
- 输出每个奖项的中奖编号。
核心考点:sample不重复抽取的特性、seed的设定、列表差集操作。
参考实现:
import random random.seed(2025) people = list(range(1, 51)) third = random.sample(people, 3) # 三等奖:3人 # 从原列表中移除三等奖获得者 remaining = [p for p in people if p not in third] second = random.sample(remaining, 2) # 二等奖:2人 # 再移除二等奖获得者 remaining = [p for p in remaining if p not in second] first = random.sample(remaining, 1) # 一等奖:1人 print("三等奖:", third) print("二等奖:", second) print("一等奖:", first)这里面用列表推导式做差集是最直观的解法。你可能会想到用set去重后再sample,但set会把顺序打乱,而且不同Python版本下set迭代顺序可能不同,会影响seed复现,所以不推荐。
这道题考察的正是random库+列表操作的组合能力:sample每次都在原序列上做不重复抽取,因此只要每轮更新剩余人员列表,逻辑就不会乱。如果误用choice或choices,中奖编号大概率会重复,整道题覆灭。
6. 备考期间刷random题的三个心得
最后聊点备考策略以外的体会,算是我自己练习random相关真题时总结出的几个"非技术"经验。
第一,不要死记函数官方文档,而是把所有函数的边界条件画成一张小卡片。我的卡片上就写着一句话:random()半开区间,randint闭区间,randrange开区间+步长,choice返回单值,choices返回列表(k默认为1),sample不重复返回新列表,shuffle原地返回None。考试前翻一眼,比翻一小时文档管用。
第二,遇到真题里的random题,一定要动手敲一遍而不是只看答案。因为random类题目的"程序填空"往往考察你能否理解代码在若干次调用之后的状态——只看答案你永远不知道shuffle返回None这个坑会导致什么,也不知道多调用一次random会把整个序列推到哪里去。哪怕只是把答案代码抄一遍然后运行一下,你对"随机序列的推进感"都会比纯阅读深刻得多。
第三,上机练习时尽量给自己设定"禁看答案"的模拟条件。我备考时规定自己每道题先花10分钟独立完成,再对照标准答案。错得越多,印象越深。尤其是那种"老觉得自己写对了,结果输出和答案差一点"的情况,基本都能定位到random调用次数或边界范围上——这些错题记录,就是考前一周最宝贵的复习资料。
希望这篇梳理能帮你把random库这块的分数稳稳攥在手里。计算机二级的题目其实并不难,难的是细心和边界感。把上面这些容易踩的坑挨个排掉,random相关的内容在考场上对你来说就只剩下"套模板"这一件事了。