Java实现选择题选项乱序算法

文章目录

    • 前言
    • [一、 核心思路](#一、 核心思路)
    • 二、核心代码实现
      • [2.1 定义数据承载类 `Topic`](#2.1 定义数据承载类 Topic)
      • [2.2 乱序工具类 `TopicRandomUtil`](#2.2 乱序工具类 TopicRandomUtil)
    • [3. 实际业务场景模拟验证](#3. 实际业务场景模拟验证)
    • 总结

前言

在开发在线考试或问答系统时,经常会遇到一个痛点:考生容易通过背诵选项字母(例如"第一题选C,第二题选A")来作弊或应付考试。

为了解决这个问题,我们需要在系统渲染试卷时,将题目的选项顺序随机打乱。

这里的核心难点在于:选项顺序打乱后,系统必须依然能准确识别出哪一个是正确答案。

这就好比给房间里的物品换门牌号:物品本身没动,只是门上的字母标签换了,但我们需要时刻追踪那个存放"正确答案"的房间现在挂着什么字母。

一、 核心思路

原标签 (originalKey) 新标签 (newKey) 选项内容 (value) 是否为正确答案
A C JAVA2 EE
B A JAVA2 Card
C D JAVA2 ME
D B JAVA2 HE ✓ → 触发记录 keyNew = "B"

内容没有动,只是换了门牌号(字母标签)。正确答案的内容跟着走到了 B,所以正确的选项 Key 也同步更新为 B。

二、核心代码实现

为了实现这个功能,我们首先需要一个数据结构来承载选项和答案。

2.1 定义数据承载类 Topic

这个类非常简单,主要用于封装选项的 Map(选项字母 -> 选项内容)和正确的 key

Java 复制代码
import java.util.Map;

public class Topic {
    private Map<String, String> option;  // 选项映射,例如 {"A": "JAVA2 EE", ...}
    private String key;                  // 正确答案的选项字母

    public Topic() {}

    public Topic(Map<String, String> option, String key) {
        this.option = option;
        this.key = key;
    }

    public Map<String, String> getOption() { return option; }
    public void setOption(Map<String, String> option) { this.option = option; }
    public String getKey() { return key; }
    public void setKey(String key) { this.key = key; }
}

2.2 乱序工具类 TopicRandomUtil

这是整个乱序逻辑的引擎。利用 Collections.shuffle() 来实现"摇匀门牌号"的操作。

在 Java 中,HashMapkeySet() 是无序的,它既不保证插入顺序,也不保证字母顺序。它仅仅是根据 Key 的哈希值(Hash Code)计算出的数组下标来存放的。

Java 复制代码
import java.util.*;

public class TopicRandomUtil {

    /**
     * 乱序Map元素,记录对应答案key
     * @param option 原题目选项
     * @param key    原正确答案
     * @return Topic 乱序后的选项和新的正确答案
     */
    static public Topic random(Map<String, String> option, String key) {
        // 1. 获取原有的门牌号集合 (A, B, C, D)
        Set<String> keySet = option.keySet();
        ArrayList<String> keyList = new ArrayList<>(keySet);
        
        // 2. 将门牌号列表乱序 (例如变成了 C, A, D, B)
        Collections.shuffle(keyList);
        
        // 3. 准备一个新的Map来存放换好门牌号的选项
        HashMap<String, String> optionNew = new HashMap<>();
        
        int idx = 0;
        String answerNew = ""; // 用于记录新的正确答案门牌号
        
        // 4. 遍历原有的门牌号,依次贴上新的门牌号
        for (String originKey : keySet) {
            String newKey = keyList.get(idx++);
            
            // 5. 判断当前被替换门牌号的原选项,是不是正确答案
            if (key.equals(originKey)) {
                answerNew = newKey; // 如果是,记录它贴上的新门牌号
            }
            
            // 6. 将新门牌号和原内容存入新Map
            optionNew.put(newKey, option.get(originKey));
        }

        return new Topic(optionNew, answerNew);
    }
}

算法可视化流程:

3. 实际业务场景模拟验证

为了直观看到效果,我们编写一个简单的 main 方法来模拟这个过程:

Java 复制代码
import java.util.HashMap;
import java.util.Map;

public class ExamSimulation {

    public static void main(String[] args) {
        // 1. 模拟从数据库中读取了一道题
        Map<String, String> originalOptions = new HashMap<>();
        originalOptions.put("A", "JAVA2 EE");
        originalOptions.put("B", "JAVA2 Card");
        originalOptions.put("C", "JAVA2 ME");
        originalOptions.put("D", "JAVA2 HE");
        
        String originalKey = "D"; // 原正确答案是 D: JAVA2 HE

        System.out.println("====== 打乱前 ======");
        System.out.println("题目选项: " + originalOptions);
        System.out.println("正确答案: " + originalKey + " -> " + originalOptions.get(originalKey));

        // 2. 使用乱序器打乱选项
        Topic randomizedTopic = TopicRandomUtil.random(originalOptions, originalKey);

        // 3. 打印打乱后的结果
        System.out.println("\n====== 打乱后 ======");
        System.out.println("题目选项: " + randomizedTopic.getOption());
        System.out.println("正确答案: " + randomizedTopic.getKey() + " -> " + randomizedTopic.getOption().get(randomizedTopic.getKey()));
        
        // 验证:内容是否依然对应
        System.out.println("\n验证结果:答案内容是否改变? " + 
                (!originalOptions.get(originalKey).equals(randomizedTopic.getOption().get(randomizedTopic.getKey())) ? "改变了(错误)" : "没改变(正确)"));
    }
}

模拟运行输出结果示例:

复制代码
====== 打乱前 ======
题目选项: {A=JAVA2 EE, B=JAVA2 Card, C=JAVA2 ME, D=JAVA2 HE}
正确答案: D -> JAVA2 HE

====== 打乱后 ======
题目选项: {A=JAVA2 EE, B=JAVA2 ME, C=JAVA2 HE, D=JAVA2 Card}
正确答案: C -> JAVA2 HE

验证结果:答案内容是否改变? 没改变(正确)

总结

通过分离"标签(Key)"和"内容(Value)",利用 Collections.shuffle() 打乱标签集合,并在重新绑定的过程中通过内容比对找回正确答案的新标签。

相关推荐
jf加菲猫2 小时前
第15章 文件和目录
开发语言·c++·qt·ui
我登哥MVP2 小时前
【SpringMVC笔记】 - 11 - SpringMVC 执行流程
java·spring boot·笔记·spring·tomcat·intellij-idea
小鱼~~2 小时前
最小二乘&均方误差MSE&平均绝对误差MAE
python·算法·机器学习
执于代码2 小时前
python 环境知多少
开发语言·python
田梓燊2 小时前
力扣:138.随机链表的复制
算法·leetcode·链表
笨蛋不要掉眼泪2 小时前
面试篇-java基础上
java·后端·面试·职场和发展
不忘不弃2 小时前
皇后摆放问题优化求解法
算法
t***5442 小时前
如何在 Dev-C++ 中切换编译器至 Clang
开发语言·c++
王老师青少年编程2 小时前
csp信奥赛C++高频考点专项训练之贪心算法 --【区间贪心】:线段覆盖
c++·算法·贪心·csp·信奥赛·区间贪心·线段覆盖