""" 分数分配工具 解决题目分数无法整除的问题,确保: 1. 所有题目分数之和精确等于总分 2. 题目分数差异最小化(最多相差1分) 3. 支持整数分配和小数分配两种模式 """ from typing import List, Tuple from decimal import Decimal, ROUND_HALF_UP import math class ScoreDistributor: """ 智能分数分配器 使用示例: distributor = ScoreDistributor(total_score=100, question_count=6) scores = distributor.distribute() # 结果: [17, 17, 17, 17, 16, 16] 总和=100 """ def __init__(self, total_score: float, question_count: int): """ 初始化分配器 Args: total_score: 总分(如 100) question_count: 题目数量(如 6) """ if question_count <= 0: raise ValueError("题目数量必须大于0") if total_score <= 0: raise ValueError("总分必须大于0") self.total_score = total_score self.question_count = question_count def distribute_integer(self) -> List[int]: """ 整数分配模式 将总分分配为整数,前面的题目分数可能比后面的多1分 Returns: 分数列表,如 [17, 17, 17, 17, 16, 16] 示例: 100分 / 6题 = [17, 17, 17, 17, 16, 16] 100分 / 7题 = [15, 15, 14, 14, 14, 14, 14] """ total = int(self.total_score) count = self.question_count # 基础分数(向下取整) base_score = total // count # 需要额外加1分的题目数量 extra_count = total % count # 生成分数列表 scores = [] for i in range(count): if i < extra_count: scores.append(base_score + 1) else: scores.append(base_score) return scores def distribute_decimal(self, decimal_places: int = 1) -> List[float]: """ 小数分配模式 将总分分配为小数,最后一题用于补齐差额 Args: decimal_places: 小数位数,默认1位 Returns: 分数列表,如 [16.7, 16.7, 16.7, 16.7, 16.7, 16.5] """ count = self.question_count # 计算每题分数并四舍五入 per_score = self.total_score / count rounded_score = round(per_score, decimal_places) # 前 n-1 题使用四舍五入的分数 scores = [rounded_score] * (count - 1) # 最后一题用总分减去前面的和,确保总分精确 last_score = round(self.total_score - sum(scores), decimal_places) scores.append(last_score) return scores def distribute(self, mode: str = "integer") -> List[float]: """ 分配分数 Args: mode: 分配模式 - "integer": 整数分配(推荐) - "decimal": 小数分配 - "decimal_1": 保留1位小数 - "decimal_2": 保留2位小数 Returns: 分数列表 """ if mode == "integer": return [float(s) for s in self.distribute_integer()] elif mode == "decimal" or mode == "decimal_1": return self.distribute_decimal(1) elif mode == "decimal_2": return self.distribute_decimal(2) else: return [float(s) for s in self.distribute_integer()] def get_score_for_question(self, question_index: int, mode: str = "integer") -> float: """ 获取指定题目的分数 Args: question_index: 题目索引(从0开始) mode: 分配模式 Returns: 该题目的分数 """ scores = self.distribute(mode) if 0 <= question_index < len(scores): return scores[question_index] raise IndexError(f"题目索引 {question_index} 超出范围 [0, {self.question_count})") def validate(self) -> Tuple[bool, str]: """ 验证分配结果 Returns: (是否有效, 信息) """ scores = self.distribute() total = sum(scores) if abs(total - self.total_score) < 0.01: return True, f"分配有效:{scores},总分={total}" else: return False, f"分配无效:{scores},总分={total},期望={self.total_score}" @staticmethod def format_score(score: float, decimal_places: int = 1) -> str: """ 格式化分数显示 Args: score: 分数 decimal_places: 小数位数 Returns: 格式化的分数字符串 """ if score == int(score): return str(int(score)) return f"{score:.{decimal_places}f}" @staticmethod def calculate_pass_score(total_score: float, pass_rate: float = 0.6) -> float: """ 计算及格分数 Args: total_score: 总分 pass_rate: 及格率,默认60% Returns: 及格分数(整数) """ return math.ceil(total_score * pass_rate) def distribute_scores(total_score: float, question_count: int, mode: str = "integer") -> List[float]: """ 便捷函数:分配分数 Args: total_score: 总分 question_count: 题目数量 mode: 分配模式(integer/decimal) Returns: 分数列表 """ distributor = ScoreDistributor(total_score, question_count) return distributor.distribute(mode) def get_question_score( total_score: float, question_count: int, question_index: int, mode: str = "integer" ) -> float: """ 便捷函数:获取指定题目的分数 Args: total_score: 总分 question_count: 题目数量 question_index: 题目索引(从0开始) mode: 分配模式 Returns: 该题目的分数 """ distributor = ScoreDistributor(total_score, question_count) return distributor.get_score_for_question(question_index, mode)