基本情報技術者 突破確率計算

Run Settings
LanguageJava
Language Version
Run Command
import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Random; import java.util.Set; import java.util.HashSet; import java.util.stream.Collectors; // Stream API用に追加 /** * 基本情報技術者試験の合格確率をモンテカルロシミュレーションで計算するシミュレーター。 * 各問題の配点設定に基づいた「素点計算」により合格率を算出します。 * 本シミュレーターはIRT(項目応答理論)採点を再現するものではありません。 */ public class FeSimulator { // 基本情報技術者試験の定数(素点計算に基づく) private static final int TOTAL_QUESTIONS_A = 60; // 科目A 総問題数 private static final int CHOICES_A = 4; // 科目Aの選択肢数 (固定) private static final int SCORE_PER_QUESTION_A = 1; // 科目Aの各問題の配点 (素点) private static final int REQUIRED_RAW_SCORE_A = 36; // 科目Aの合格に必要な素点 (60点満点中36点) private static final int TOTAL_QUESTIONS_B = 20; // 科目B 総問題数 private static final int SCORE_PER_QUESTION_B = 1; // 科目Bの各問題の配点 (素点) private static final int REQUIRED_RAW_SCORE_B = 12; // 科目Bの合格に必要な素点 (20点満点中12点) private static final int TOTAL_EXAM_QUESTIONS = TOTAL_QUESTIONS_A + TOTAL_QUESTIONS_B; // 試験全体の総問題数 private static final int DEFAULT_TRIALS = 10000; // 各シナリオでのデフォルト試行回数 /** * 各問題の定義。問題番号(※実際にはインデックス)、選択肢の数、配点を保持します。 */ public static class Problem { private final int no; // 問題番号(デバッグ用など) private final int choices; // 選択肢の数 private final int score; // 配点 public Problem(int no, int choices, int score) { this.no = no; this.choices = choices; this.score = score; } public int getNo() { return no; } public int getChoices() { return choices; } public int getScore() { return score; } } // 全問題データ(科目Aと科目Bを結合して、通し番号で管理) private static final List<Problem> PROBLEMS = new ArrayList<>(); static { // 科目Aの問題 (No.1-60): 4択1点問題 for (int i = 0; i < TOTAL_QUESTIONS_A; i++) { PROBLEMS.add(new Problem(i + 1, CHOICES_A, SCORE_PER_QUESTION_A)); } // 科目Bの問題 (No.61-80, 元のNo.1-20): 選択肢数変動1点問題 // 科目B選択肢数リスト = [6, 5, 7, 4, 7, 4, 6, 4, 4, 6, 4, 4, 4, 8, 4, 9, 10, 10, 5, 7] int[] choicesB = {6, 5, 7, 4, 7, 4, 6, 4, 4, 6, 4, 4, 4, 8, 4, 9, 10, 10, 5, 7}; for (int i = 0; i < TOTAL_QUESTIONS_B; i++) { PROBLEMS.add(new Problem(TOTAL_QUESTIONS_A + i + 1, choicesB[i], SCORE_PER_QUESTION_B)); } } /** * ランダム解答方式を定義する列挙型。 * 各方式で、元の選択肢数から計算される有効な選択肢数を保持し、正答率を決定します。 */ public enum AnswerMethod { // 完全ランダム(不正解を0%消す力) FULL_RANDOM("0%の不正解を消す力(完全ランダム)", 0.0), // 不正解を25%消す力 ELIMINATE_25_PERCENT("25%の不正解を消す力", 0.25), // 不正解を50%消す力 ELIMINATE_50_PERCENT("50%の不正解を消す力", 0.50); private final String name; private final double eliminationRate; // 除外する不正解の割合 AnswerMethod(String name, double eliminationRate) { this.name = name; this.eliminationRate = eliminationRate; } public String getName() { return name; } /** * 指定された問題の有効な選択肢数を計算します。 * Math.round() を使用し、最小値は1とします。 * @param originalChoices 元の選択肢の数 * @return 計算された有効な選択肢の数 */ public int calculateEffectiveChoices(int originalChoices) { // 除外後の選択肢数を計算 double remainingChoices = originalChoices * (1.0 - eliminationRate); // Math.round で丸め、小数点以下を四捨五入して整数にする int effectiveChoices = (int) Math.round(remainingChoices); // 少なくとも1つの選択肢は残るようにする return Math.max(1, effectiveChoices); } /** * ランダムに正解するかどうかを判定します。 * @param random Randomインスタンス * @param problem 問題オブジェクト * @return 正解であればtrue、そうでなければfalse */ public boolean isCorrect(Random random, Problem problem) { int effectiveChoices = calculateEffectiveChoices(problem.getChoices()); // 0からeffectiveChoices-1までの乱数を生成し、0であれば正解とみなす return random.nextInt(effectiveChoices) == 0; } } /** * シミュレーション結果を保持するクラス。 */ public static class SimulationResult { private final int knownCorrectQuestionsOverall; private final double successRate; public SimulationResult(int knownCorrectQuestionsOverall, double successRate) { this.knownCorrectQuestionsOverall = knownCorrectQuestionsOverall; this.successRate = successRate; } public int getKnownCorrectQuestionsOverall() { return knownCorrectQuestionsOverall; } public double getSuccessRate() { return successRate; } } private final Random random; /** * FeSimulator の新しいインスタンスを生成します。 */ public FeSimulator() { this.random = new Random(); } /** * 指定されたシナリオでモンテカルロシミュレーションを実行し、合格確率を計算します。 * * @param answerMethod シミュレーション対象の解答方式(ランダム解答の精度) * @param knownCorrectQuestionsOverall 事前に知っている正解数(試験全体に対する数) * @param trials 試行回数 * @return シミュレーション結果を表す {@code SimulationResult} オブジェクト * @throws IllegalArgumentException 不正な引数が指定された場合 */ public SimulationResult simulateScenario(AnswerMethod answerMethod, int knownCorrectQuestionsOverall, int trials) { if (knownCorrectQuestionsOverall < 0 || knownCorrectQuestionsOverall > TOTAL_EXAM_QUESTIONS) { throw new IllegalArgumentException( "事前に知っている正解数 (" + knownCorrectQuestionsOverall + ") は、0から総問題数 (" + TOTAL_EXAM_QUESTIONS + ") の範囲で指定してください。" ); } if (trials <= 0) { throw new IllegalArgumentException("試行回数は1以上を指定してください。"); } int successfulTrials = 0; // 試験全体の「事前に知っている問題総数」を科目Aと科目Bに按分 int knownCorrectQuestionsA = (int) Math.round(knownCorrectQuestionsOverall * ((double) TOTAL_QUESTIONS_A / TOTAL_EXAM_QUESTIONS)); int knownCorrectQuestionsB = knownCorrectQuestionsOverall - knownCorrectQuestionsA; // 科目ごとの上限チェック (念のため) knownCorrectQuestionsA = Math.min(knownCorrectQuestionsA, TOTAL_QUESTIONS_A); knownCorrectQuestionsB = Math.min(knownCorrectQuestionsB, TOTAL_QUESTIONS_B); for (int i = 0; i < trials; i++) { int currentScoreA = 0; int currentScoreB = 0; // 既に「事前に知っている問題」としてマークされた問題のインデックスを保持 Set<Integer> knownCorrectIndicesA = new HashSet<>(); Set<Integer> knownCorrectIndicesB = new HashSet<>(); // 1. 「事前に知っている問題」の処理(科目A) List<Integer> questionIndicesA = new ArrayList<>(); for (int k = 0; k < TOTAL_QUESTIONS_A; k++) { questionIndicesA.add(k); } Collections.shuffle(questionIndicesA, random); // 問題インデックスをシャッフル for (int k = 0; k < knownCorrectQuestionsA; k++) { int problemIndex = questionIndicesA.get(k); // シャッフルされたインデックス currentScoreA += PROBLEMS.get(problemIndex).getScore(); // その問題の配点を加算 knownCorrectIndicesA.add(problemIndex); // 既に処理済みとしてマーク } // 2. 「事前に知っている問題」の処理(科目B) List<Integer> questionIndicesB = new ArrayList<>(); // 科目Bの問題インデックスは、PROBLEMSリストの中での相対位置ではなく、科目B内での0からのインデックスで扱う for (int k = 0; k < TOTAL_QUESTIONS_B; k++) { questionIndicesB.add(k); } Collections.shuffle(questionIndicesB, random); // 問題インデックスをシャッフル for (int k = 0; k < knownCorrectQuestionsB; k++) { int problemIndexB_relative = questionIndicesB.get(k); // シャッフルされた科目B内でのインデックス int problemIndexInOverallList = TOTAL_QUESTIONS_A + problemIndexB_relative; // 全体リストでのインデックス currentScoreB += PROBLEMS.get(problemIndexInOverallList).getScore(); // その問題の配点を加算 knownCorrectIndicesB.add(problemIndexB_relative); // 科目B内での相対インデックスをマーク } // 3. 「残りの問題」の処理(ランダム解答、科目A) for (int j = 0; j < TOTAL_QUESTIONS_A; j++) { if (!knownCorrectIndicesA.contains(j)) { // 事前に知っている問題でなければランダムに解答 Problem problem = PROBLEMS.get(j); if (answerMethod.isCorrect(random, problem)) { currentScoreA += problem.getScore(); // 正解なら配点を加算 } } } // 4. 「残りの問題」の処理(ランダム解答、科目B) for (int j = 0; j < TOTAL_QUESTIONS_B; j++) { if (!knownCorrectIndicesB.contains(j)) { // 事前に知っている問題でなければランダムに解答 Problem problem = PROBLEMS.get(TOTAL_QUESTIONS_A + j); // 全体リストから科目Bの問題を取得 if (answerMethod.isCorrect(random, problem)) { currentScoreB += problem.getScore(); // 正解なら配点を加算 } } } // 5. 合格条件の判定 boolean passedA = (currentScoreA >= REQUIRED_RAW_SCORE_A); boolean passedB = (currentScoreB >= REQUIRED_RAW_SCORE_B); if (passedA && passedB) { successfulTrials++; } } double successRate = (double) successfulTrials / trials * 100; return new SimulationResult(knownCorrectQuestionsOverall, successRate); } /** * シミュレーション結果を表示します。 * * @param result 表示するシミュレーション結果 */ public void displayResult(SimulationResult result) { System.out.printf("・事前に%d問知っている場合: 合格率: %.2f%%\n", result.getKnownCorrectQuestionsOverall(), result.getSuccessRate()); } /** * メインメソッド。基本情報技術者試験の各種シナリオでシミュレーションを実行します。 */ public static void main(String[] args) { FeSimulator simulator = new FeSimulator(); System.out.println("★基本情報技術者試験 突破確率計算:ランダム選択方式(モンテカルロシミュレーション)★"); System.out.println("---"); System.out.println("総問題数: " + TOTAL_EXAM_QUESTIONS + "問"); System.out.println("科目A 合格に必要な素点: " + REQUIRED_RAW_SCORE_A + "点 / " + TOTAL_QUESTIONS_A + "点満点 (100点換算60点)"); System.out.println("科目B 合格に必要な素点: " + REQUIRED_RAW_SCORE_B + "点 / " + TOTAL_QUESTIONS_B + "点満点 (100点換算60点)"); System.out.println("---"); System.out.println(); // 各解答方式のシミュレーション for (AnswerMethod answerMethod : AnswerMethod.values()) { System.out.println("■" + answerMethod.getName() + " 試行結果"); // knownCorrectQuestions は0問からTOTAL_EXAM_QUESTIONSまで繰り返す for (int i = 0; i <= TOTAL_EXAM_QUESTIONS; i++) { try { SimulationResult result = simulator.simulateScenario(answerMethod, i, DEFAULT_TRIALS); simulator.displayResult(result); } catch (IllegalArgumentException e) { System.err.println("エラー: " + e.getMessage()); } } System.out.println(); // 各解答方式の後に空行 } System.out.println("---"); System.out.println("※本シミュレーターは、基本情報技術者試験のIRT(項目応答理論)採点を厳密に再現するものではなく、"); System.out.println(" 各問題の配点が上記で設定された値であると仮定した「素点計算」に基づいています。"); System.out.println(" 実際の試験結果と異なる場合があることをご留意ください。"); } }
Editor Settings
Theme
Key bindings
Full width
Lines