试题组卷有没有啥好的算法?

比方说一个试卷,总共60题,每道题都由难度、章节、类型3个属性
按照难度 简单20题 一般30题 困难10题
按照章节 章节一 10题 章节二15题 章节3 20题,章节四15题
按照类型 文字题20题 图片题20题 视频题20题

每种维度题目数量是可以自行设置的,保证总题量等于60就行

不是每个最细分类(我这里称sku把)都有题目或者说有足量的题目,这种情况要考虑到

然后随机从数据库中取出这些题,有没有比较好的算法?

——————-分割线———————
一开始考虑的太理想,想保证每种题型选到的概率统一,且每个题目选到的概率统一,这样考虑就只能暴力算法列举出所有情况,再随机了,然而即使是这种3x3暴力算法,全部可能就有C(60,20)xC(40,10)xC(60,10)xC(50,15)xC(60,20)xC(40,20),(随便算一个数都有10^10+,总共6个数乘起来,想想就恐怖),然后这个里面还有很多重复的情况需要去重,估计给定现在最强的计算器都要算不了
那么现在只能考虑去掉每个题目选到的概率统一这个条件了,参考楼下的一些回复,可以想到的是分成3块,每种给定60个这种类型的题目进行随机,最后计算出来的组合,判断数据库是否存在足够的题目,否则就再进行随机,直到得到题目,这种算法在题目种类,每种充足且很多的时,很好用,但是很明显现实情况不可能会这么完美,缺题情况还是特别多的,比方说,一本书前面几章一般都没啥困难的题,全是理论的章节,肯定也不可能有视频题。这种情况下真正能组合起来的只占全部组合的很小一部分,这样就会出现随机很长很长时间才会命中一个的情况。
所以只能抛弃掉所有理想条件,回到先保证题目能组上这个出发点了,即把所有sku类型及其数目,全部列出,每次随机一种题目,算到60题,然后再比较所有属性和题目中给定的是否符合,如果不符合,就再进行循环,这种方法可以很大程度上提升命中概率(当然可能会存在一些极端情况导致组不了题),但是相对而言,每个题目选到的概率就不是很均匀,一些能保证组上卷的”关键题”,会经常出现在组的每套试卷里。
目前为了保证组题效率,只能考虑在第三种方案上改进(当然如果各位有更好的方案,还请不吝赐教),我能想到的是,每次随机完题目后,计算一下各种类型的组题进度,然后下一次再选题的时候,只选择当前组题进度最慢的那一些类题目进行随机,以增强当前组题的命中率,有点动态规划的意味,不知道各位还有啥高见?

《L01 基础入门》
我们将带你从零开发一个项目并部署到线上,本课程教授 Web 开发中专业、实用的技能,如 Git 工作流、Laravel Mix 前端工作流等。
《L03 构架 API 服务器》
你将学到如 RESTFul 设计风格、PostMan 的使用、OAuth 流程,JWT 概念及使用 和 API 开发相关的进阶知识。
讨论数量: 27

根据题目类型章节难度组装成一个redis set 类型的key

$key = 简单_章节1_文字

将题目的id放入set中

redis_cli>sadd $key 1 2 3 4 5.... 

生成考卷时 使用srandmember取出redis中随机id

redis_cli>srandmember $key 20 // 取出简单章节1文字题随机20个题目id

合并id并到数据库中查询

小白一枚 有问题请大佬指出

2年前 评论
poker_face (楼主) 2年前
码龍 (作者) 2年前

仅仅对你说的这些,使用随机数就行了。不过这块我觉得更看重数据库设计,设计不好后期查询会比较痛苦。比如需要本周组过卷的题目进行剔除,对易错题相同考点题目加权重等,没有特别固定算法。

2年前 评论
poker_face (楼主) 2年前
$tmp = [];
for ($i=1;$i<=60;$i++){
    if($i <= 20) $item = ['简单'];
    if($i <= 50 && $i>20) $item = ['一般'];
    if($i <= 60 && $i>50) $item = ['困难'];
    $tmp[] = $item;
}
shuffle($tmp);
foreach ($tmp as $key => &$value){
    if($key < 10) array_push($value,'章节一');
    if($key < 25 && $key>=10) array_push($value,'章节二');
    if($key < 45 && $key>=25) array_push($value,'章节三');
    if($key < 60 && $key>=45) array_push($value,'章节四');
}
shuffle($tmp);
foreach ($tmp as $key => &$value){
    if($key < 20) array_push($value,'文字题');
    if($key < 40 && $key>=20) array_push($value,'图片题');
    if($key < 60 && $key>=40) array_push($value,'视频题');
}
dd($tmp);

Laravel

2年前 评论

或者再进一步处理一下

$tmp = [];
for ($i=1;$i<=60;$i++){
    if($i <= 20) $item = ['简单'];
    if($i <= 50 && $i>20) $item = ['一般'];
    if($i <= 60 && $i>50) $item = ['困难'];
    $tmp[] = $item;
}
shuffle($tmp);
foreach ($tmp as $key => &$value){
    if($key < 10) array_push($value,'章节一');
    if($key < 25 && $key>=10) array_push($value,'章节二');
    if($key < 45 && $key>=25) array_push($value,'章节三');
    if($key < 60 && $key>=45) array_push($value,'章节四');
}
shuffle($tmp);
foreach ($tmp as $key => &$value){
    if($key < 20) array_push($value,'文字题');
    if($key < 40 && $key>=20) array_push($value,'图片题');
    if($key < 60 && $key>=40) array_push($value,'视频题');
}
$qa = [];
foreach ($tmp as $v){
    $index = implode('-',$v);
    $qa[$index] = isset($qa[$index]) ? $qa[$index] + 1 : 1;
}
dd($qa);

Laravel

2年前 评论
poker_face (楼主) 2年前
win27149 (作者) 2年前
poker_face (楼主) 2年前
win27149 (作者) 2年前
win27149 (作者) 2年前
poker_face (楼主) 1年前

先按每种题目都足量处理,不够再从有多出的类型抽取补齐不就行了

2年前 评论
poker_face (楼主) 2年前

array_fill填充数组,array_rand取,如果数字大的话不想生成大数组耗内存可以使用数字的大小比较来进行定位

$origin = [
    'level' => [
        ['text' => '简单', 'total' => 20],
        ['text' => '一般', 'total' => 30],
        ['text' => '困难', 'total' => 10]
    ]
];
$data = [];
for ($index = 0; $index < 60; $index++) {
    $item = [];
    foreach ($origin as $type => $values) {
        $total = array_sum(array_column($values, 'total'));
        $random = rand(1, $total);
        foreach ($values as $key => $value) {
            if ($random <= $value['total']) {
                $item[$type] = $value['text'];
                $origin[$type][$key]['total']--;
                break;
            } else {
                $random -= $value['total'];
            }
        }
    }
    $data[] = $item;
}
2年前 评论
poker_face (楼主) 1年前
Siam (作者) 1年前
poker_face (楼主) 1年前

以前弄过简单的,先手动组装试卷,然后再以试卷为单位,随机抽试卷的方式来弄。

2年前 评论
poker_face (楼主) 1年前

组卷不应该是一次性的,先按难度和章节筛选出题目列表,告诉用户各个题型有多少道题,用户可以把题目加入待选列表,然后变更条件重新筛选,列表需要标注是否已选择,直至满足组卷需求

2年前 评论

建议先研究一下题库是怎么做的,一般会有学科、年级、教材版本、知识点、章节、难度、题型等多个维度的

2年前 评论

如果你想要模糊条件组合查询选题,用户每做一个选择,你都要将查询当前符合要求的题目数量显示出来, 而不是用户都选择完了就直接去计算是否符合需求

2年前 评论
laravel_denghy (作者) 2年前

看看 WPS 的稻壳有个试卷中心,可以组卷,参考借鉴一下去哈哈哈

1年前 评论

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!