ValueError: Sample larger than population or is negative的原因以及解决办法

1.出现

这几天重构kook-valorant-bot的代码的时候,遇到了这个问题

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
Job "vip_roll_task (trigger: interval[0:01:20], next run at: 2023-01-25 19:59:21 CST)" raised an exception
Traceback (most recent call last):
File "/home/muxue/.local/lib/python3.10/site-packages/apscheduler/executors/base_py3.py", line 30, in run_coroutine_job
retval = await job.func(*job.args, **job.kwargs)
File "/home/muxue/kook/val-bot/code/main.py", line 793, in vip_roll_task
ran = random.sample(range(0, len(VipRollDcit[msg_id]['user']) - 1), vnum)
File "/usr/local/lib/python3.10/random.py", line 482, in sample
raise ValueError("Sample larger than population or is negative")
ValueError: Sample larger than population or is negative
error raised during task
Traceback (most recent call last):
File "/home/muxue/.local/lib/python3.10/site-packages/apscheduler/executors/base_py3.py", line 30, in run_coroutine_job
retval = await job.func(*job.args, **job.kwargs)
File "/home/muxue/kook/val-bot/code/main.py", line 793, in vip_roll_task
ran = random.sample(range(0, len(VipRollDcit[msg_id]['user']) - 1), vnum)
File "/usr/local/lib/python3.10/random.py", line 482, in sample
raise ValueError("Sample larger than population or is negative")
ValueError: Sample larger than population or is negative

这部分代码是用来生成抽奖结果的随机数的,在网上百度了报错之后,得知是random生成随机数时产生的报错

2.复现

找到对应的代码

1
ran = random.sample(range(0, len(VipRollDcit[msg_id]['user'])-1), vnum) # 生成n个随机数

这个代码的作用是,生成vnum个从0到len(VipRollDcit[msg_id]['user']) - 1的随机数,不包含len(VipRollDcit[msg_id]['user'])-1

咳咳,这里又发现了另外一个bug:我以为是包含右边界的,所以手动-1了;现在导致最后一个参加抽奖的用户永远都抽不到奖了😥

正确的range应该是(0, len(VipRollDcit[msg_id]['user']))

但是,如果参与抽奖的人数少于vnum,就会出现上面的报错

1
2
import random
ran = random.sample(range(0, 2), 3)

可以来简单测试一下,这里我想在0到2之间(其实就是0,1)生成3个随机数

1
2
3
4
5
6
7
$ py3 test1.py
Traceback (most recent call last):
File "/home/muxue/kook/test/global_val_test/test1.py", line 16, in <module>
ran = random.sample(range(0, 2), 3)
File "/usr/local/lib/python3.10/random.py", line 482, in sample
raise ValueError("Sample larger than population or is negative")
ValueError: Sample larger than population or is negative

此时运行,就会有这样的报错。

因为这个写法的作用是在0-2之间产生不重复的随机数,你的目标数量都大于这个集合之中已有数据的数量了,那要怎么生成不重复的随机数呢?

3.解决

解决办法很简单

  • 右边界不能等于或者小于左边界
  • 产生随机数的数量要小于边界之中数据的数量

比如上面的代码,我们修改一下边界,就能获得正确的结果

1
2
3
import random
ran = random.sample(range(0, 3), 3)
print(ran)

运行

1
2
$ py3 test1.py
[0, 2, 1]

对于我的抽奖代码而言,则需要重新操作一番。即可能出现奖品数量大于参与抽奖的人数的情况;

这时候就需要进行判断了

  • 奖品数量少于抽奖人数,使用random生成随机数
  • 奖品数量大于抽奖人数,抛弃多余奖品,直接生成一个从0到总人数的列表
  • 奖品数量等于抽奖人数,上面两种情况都可以用

代码如下

1
2
3
4
5
# 人数大于奖品数量
if len(VipRollDcit[msg_id]['user'])>vnum:
ran = random.sample(range(0, len(VipRollDcit[msg_id]['user'])), vnum) # 生成vnum个随机数
else: # 生成一个从0到len-1的列表 如果只有一个用户,生成的是[0]
ran = list(range(len(VipRollDcit[msg_id]['user'])))

这时候就ok了,没有问题了