MongoDB 按题型保持一定比例抽题 - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
V2EX    MongoDB

MongoDB 按题型保持一定比例抽题

  •  
  •   dream4ever 2021-07-29 20:19:26 +08:00 2533 次点击
    这是一个创建于 1597 天前的主题,其中的信息可能已经有所发展或是发生改变。

    后端技术栈用的 Node.js + Mongoose + MongoDB,数据库中的每道题目分属不同的题型(单选 /多选 /判断),现在需要实现的抽题功能如下:

    1. 抽到的各类型题目数量保持在一个比例上,比如每抽 10 道题,就会有 5 道单选,3 道多选,2 道判断。
    2. 能够实现连续抽题不会抽到同一道题。比如用户 A 回答最初的 10 道题都正确,接着再抽取 10 道题,这新的 10 道题不应当和之前的 10 道题有重复的题目。

    以上两点需求,该如何实现呢?谢谢先。

    12 条回复    2021-08-12 10:01:51 +08:00
    xycost233
        1
    xycost233  
       2021-07-29 21:49:06 +08:00 via iPhone
    groupby?
    no1xsyzy
        2
    no1xsyzy  
       2021-07-30 01:05:40 +08:00
    不是很懂 MongoDB 对此问题产生了什么影响,我觉得你把题目的 id 全在内存里存一份也没问题
    至于 2. 你要存下之前的 10 道题,不断地随机抽取直到有 10 个与之前不同的。这是基础问题,大概是在编程零基础第三堂课的时候就会讲清楚了(循环)。
    xuanbg
        3
    xuanbg  
       2021-07-30 04:50:12 +08:00
    @no1xsyzy 如果每个人都能正确区分自己面临的是哪几个问题,知乎或者本论坛上的问题数量起码少 10 倍。如果再能搞清楚什么是问题的核心和本质,什么是问题的表征的话,就几乎不会有什么问题了。
    dream4ever
        4
    dream4ever  
    OP
       2021-07-30 09:17:40 +08:00
    @no1xsyzy 嗯,第二点是我没表述清楚。我是希望对于不同的用户,抽到的题目各不相同,比如多个用户抽到的最初的 10 道题,尽量保证互相之间不一样,各个用户再继续抽 10 道题,互相之间还是不一样,所以才会有这个问题。
    Dganzh
        5
    Dganzh  
       2021-07-30 09:45:04 +08:00
    描述还是不够清楚,怎么算连续抽?是一个小时内连续答题算连续抽,还是一天,还是一周?
    no1xsyzy
        6
    no1xsyzy  
       2021-07-30 09:46:12 +08:00
    @xuanbg 并不涉及『每个人』的问题,我比较扭曲,别人把问题错误地表述得过于简单的话,我会突出描述一个「小学生都会做」。
    @dream4ever 如果不是严格要求的话,用现成的随机抽样就可以了。
    另一种方案,就是把整个题库打乱,然后顺序每次取若干题,定期重新打乱。或者模仿杀戮尖塔那样,耗尽时 shuffle,但可能造成 1st percentile 的性能问题。
    xuanbg
        7
    xuanbg  
       2021-07-30 12:13:29 +08:00
    @no1xsyzy 我不过是对不会提问题这个事感概一下罢了。其实楼主这个随机抽题的问题是一个非常好的问题。

    如果需要严格保证同一用户每轮抽到的题都不同,并且每轮抽题每个用户的题也都不相同。那么你需要把用户抽题的轮次也保存起来。在抽题的时候进行两个维度的唯一性判断,一个是用户维度,一个是轮次维度。具体的做法可以先取一个随机数,然后取大于这个随机数的 m 道题。然后在这个结果里面迭代并判断两次唯一性,通过的加入结果集,并且加入轮次集合和用户集合,直到结果集的数量符合输出要求。如果迭代完还不够数,就再来一次,二次,n 次,直到结果集的数量满足要求。
    no1xsyzy
        9
    no1xsyzy  
       2021-07-31 13:41:05 +08:00
    @xuanbg 其实你这样有点复杂了,应当只需要建立一个「跨用户的全局轮次维度」,也就是说,抽题的顺序无论是 AABBCC 还是 ABCABC,抽出来的题都是(预先打乱并切分好的题组) 123456
    具体受抽题顺序影响,组合可能是 A1 A2 B3 B4 C5 C6 或者 A1 B2 C3 A4 B5 C6
    只要事先洗牌,也就不需要去判断唯一性,只需要接着上一次抽到的位置之后继续抽就行。
    dream4ever
        10
    dream4ever  
    OP
       2021-08-11 17:36:47 +08:00
    @xuanbg
    @no1xsyzy

    我这几天又思考了一下这个随机抽题的需求和实现思路,整理后的内容如下,欢迎指正:

    前提:
    1. 题库中一共有 N 道题。目前这个 N 为四位数,且在可以预期的未来,也不会有大量的增长。
    2. 每道题目均为单选题、多选题、判断题中的一种。
    3. 单选题、多选题、判断题的数量之比为 N1 : N2 : N3,且 N1 + N2 + N3 = N 。

    需求:
    1. 对每个用户来说,在每一轮游戏中,系统会将题库中的这 N 道题目,最多只有一次地、随机地呈现给用户,让用户回答。
    2. 如果用户答对了题库中的所有题目,或者答错了 1 道题,则本轮游戏结束。
    3. 为减轻系统负担,对于题库中的 N 道题,每次从中抽取 M 道题,全部抽完假设共需 L 次。对于每次抽取到的 M 道题,单选题、多选题、判断题的比例,尽量保持在 N1 : N2 : N3 这个比例上,也就是和这三类题目在总题库中的比例尽量相同。
    4. 对同一个用户而言,各轮游戏的题目出现顺序应当不同,比如某一轮最开始拿到的题目是 1 、5 、9 、7,下一轮就不能也是这个顺序了。对于不同用户则没有要求。

    大致实现思路:
    1. 由于题目数量不多,可以在数据库中给题目增加一个序号字段,用自增的正整数来标记每一道题目的序号。
    2. 在每个用户的每一轮游戏开始前,将所有题目的序号按题目类型进行分组,发给用户。例如用户收到的数组是
    arr = [[1, 5, 7, 10, ...], [2, 3, 4, 8, ...], [6, 9, 11, 13, ...]],那么 arr[0]、arr[1] 、arr[2] 分别是所有单选题、多选题、判断题的序号。
    3. 由于在一轮游戏中,每一次需要抽取 M 道题目,那么可知需要抽取单选题的数量为 Q1 = M * N1 / N,多选题为 Q2 = M * N2 / N,判断题为 Q3 = M * N3 / N 。
    4. 前端在 arr[0] 中随机抽取 Q1 个元素为单选题的序号,在 arr[1] 中随机抽取 Q2 个元素为多选题的序号,在 arr[2] 中随机抽取 Q3 个为判断题的序号,并将这三组序号分别从 arr[0]、arr[1]、arr[2] 中移除。
    5. 前端用这三组序号,从后端抽取 Q1 + Q2 + Q3 共 M 道题目并呈现给用户,让用户答题。
    6. 如果这 M 道题用户全部回答正确,则重复第 4 、5 两步,继续抽取新的 M 道题给用户,直到用户答对所有题目,或者答错 1 道题目。

    上面这个思路把抽题功能的主要部分交给了前端来做,感觉这样后端的负担可以小一些,也是对后端不够熟悉,就选择了这么一个相对比较取巧的办法。

    PS:在整理这个需求和实现思路的时候,发现自己的表述的确不够清晰准确,就上面这段文字,来来回回修改了好几遍,用了两个多小时才完成,就这还是感觉表达得不够好,知易行难呐。
    no1xsyzy
        11
    no1xsyzy  
       2021-08-11 23:32:51 +08:00
    其实就是分层抽样。
    在一堆 1-based 中混进了一个 0-based
    放进前端做算是可以对选题作弊吧

    恭喜你学会了小黄鸭调试法。
    dream4ever
        12
    dream4ever  
    OP
       2021-08-12 10:01:51 +08:00
    @no1xsyzy 小黄鸭调试法很久以前就看到过,不过一直都没有很好地践行这个方法,工作好几年了,感觉工作的方法和习惯还是很原始 @_@
    关于     帮助文档     自助推广系统     博客     API     FAQ     Solana     923 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 34ms UTC 23:18 PVG 07:18 LAX 15:18 JFK 18:18
    Do have faith in what you're doing.
    ubao msn snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86