个人算法能力画像:用 AI 给刷题数据建模,生成能力雷达图

📅2026/7/13 10:39:23 👁️次浏览
个人算法能力画像:用 AI 给刷题数据建模,生成能力雷达图
个人算法能力画像用 AI 给刷题数据建模生成能力雷达图一、刷了 500 道题后你能画出自己的能力雷达图吗如果问你你擅长哪种算法大多数人会凭感觉回答我 DP 还行图论不太熟。但还行是多行不太熟是哪个子领域不熟如果被追问能不能量化一下很少有人能给出数据支持的答案。这就是算法能力画像要解决的问题。它不依赖你的自我感觉而是分析你所有的刷题数据——每道题的提交次数、调试时间、是否使用 AI 提示、以及跨题的模式迁移能力——然后生成一个可量化的多维能力剖面。这个画像的价值在于它不是静态的你擅长什么而是动态的你的弱点在哪个子领域、近期有没有改善、下一步应该集中训练什么。flowchart TB A[刷题数据] -- B[数据预处理] B -- B1[提交记录: 题号、时间、次数] B -- B2[代码分析: 复杂度、代码风格] B -- B3[AI 交互: 提示次数、依赖度] B1 -- C[特征提取] B2 -- C B3 -- C C -- D[多维度评分] D -- D1[模式广度: 覆盖的算法类型] D -- D2[模式深度: 各类型的熟练程度] D -- D3[解题效率: 平均耗时] D -- D4[自主率: 不依赖 AI 的比例] D -- D5[迁移能力: 新题 - 已知模式的映射] D1 -- E[能力雷达图] D2 -- E D3 -- E D4 -- E D5 -- E E -- F[个性化训练建议]二、从提交记录到能力向量的特征工程把刷题数据转化为能力画像本质上是一个特征工程问题。原始数据是张三在 7 月 10 日提交了 LeetCode 第 300 题用了 25 分钟、提交了 3 次才 AC而我们需要提取的特征包括特征 1模式广度。你把 LeetCode 所有题目映射到 15 个算法模式双指针、滑动窗口、二分、DFS、BFS、DP-1D、DP-2D、背包、贪心、并查集、拓扑、MST、最短路、数学、设计。如果你在某个模式中至少独立完成过 3 道题就认为你掌握了这个模式。模式广度 掌握的模式数 / 15。特征 2模式深度。对每个已掌握的模式计算解题效率和一次 AC 率的加权平均。深度不仅看你做对了多少题还看你是怎么做的——是轻松一次 AC还是调试了八次才勉强过。特征 3AI 依赖度。如果最近 50 道题中有 30 道用了 AI 提示依赖度为 60%。这个指标特别重要AI 依赖度过高可能意味着你在假装进步——代码能 AC但思维没有成长。特征 4迁移能力。对于一道新题你能否快速识别它属于哪个模式这个可以通过打开题目到第一次提交的时间来衡量——如果时间很短说明你很快识别了模式如果时间很长但代码很少说明你在模式匹配上花了大量时间。三、能力画像引擎的实现 算法能力画像引擎 核心设计 1. 从刷题历史中提取多维特征 2. 用加权评分模型生成能力向量 3. 可视化雷达图 增长曲线 import json from dataclasses import dataclass, field from typing import List, Dict, Tuple from datetime import datetime from collections import defaultdict import numpy as np dataclass class SolveEntry: 单次解题记录 problem_id: int pattern: str # 算法模式 difficulty: str # Easy/Medium/Hard time_spent_min: int # 耗时分钟 attempts: int # 提交次数 used_ai_hint: bool # 是否使用 AI 提示 solved_at: datetime first_view_to_submit_min: int # 从看到题到首次提交的时间 class AbilityProfiler: 算法能力画像生成器 为什么用加权评分而非简单平均 不同难度题目的权重不同Hard Medium Easy 最近题目的权重高于早期题目时间衰减 这样才能反映当前的真实能力。 # 15 个核心算法模式 PATTERNS [ 双指针, 滑动窗口, 二分搜索, DFS/回溯, BFS, 一维DP, 二维DP, 背包问题, 贪心, 并查集, 拓扑排序, 最小生成树, 最短路径, 数学/数论, 设计/模拟 ] # 难度权重Hard 题更能体现真实能力 DIFFICULTY_WEIGHT {Easy: 0.6, Medium: 1.0, Hard: 1.5} def __init__(self, entries: List[SolveEntry]): self.entries entries self.profile: Dict[str, float] {} def compute_profile(self) - Dict[str, float]: 计算完整的能力画像 # 1. 模式广度 mastered_patterns self._compute_pattern_breadth() self.profile[模式广度] len(mastered_patterns) / len(self.PATTERNS) # 2. 各模式深度 pattern_depths self._compute_pattern_depths() self.profile.update({ f深度_{p}: pattern_depths.get(p, 0.0) for p in self.PATTERNS }) # 3. 综合解题效率 self.profile[解题效率] self._compute_efficiency() # 4. AI 独立率 self.profile[自主解题率] self._compute_independence() # 5. 模式迁移能力 self.profile[迁移能力] self._compute_transfer_ability() # 6. 综合得分 self.profile[综合得分] self._overall_score() return self.profile def _compute_pattern_breadth(self) - set: 计算模式广度 一个模式被掌握的条件独立完成 3 道题 为什么阈值是 31-2 道可能是偶然做对 3 道以上说明有稳定的解题能力。 pattern_count defaultdict(int) for entry in self.entries: if not entry.used_ai_hint: # 只统计独立完成的 pattern_count[entry.pattern] 1 return {p for p, count in pattern_count.items() if count 3} def _compute_pattern_depths(self) - Dict[str, float]: 计算每种模式的深度得分 为什么使用时间衰减一周前的 Hard 题 AC 不等于今天还能 AC时间越近权重越大。 now datetime.now() pattern_scores defaultdict(list) for entry in self.entries: # 时间衰减因子半衰期 30 天 days_ago (now - entry.solved_at).days decay 0.5 ** (days_ago / 30) # 单题得分 难度权重 × 时间衰减 × 效率因子 score ( self.DIFFICULTY_WEIGHT[entry.difficulty] * decay * (1.0 / entry.attempts) # 提交次数越少得分越高 ) pattern_scores[entry.pattern].append(score) depths {} for pattern, scores in pattern_scores.items(): # 取加权平均最近 20 题 recent_scores sorted( scores, keylambda x: -abs(x) )[:20] depths[pattern] sum(recent_scores) / len(recent_scores) return depths def _compute_efficiency(self) - float: 综合解题效率 效率 中等难度题的加权平均耗时 为什么只算中等题简单题耗时差异小、区分度低 困难题耗时受熟练度外的因素影响太大。 medium_entries [ e for e in self.entries if e.difficulty Medium ] if not medium_entries: return 0.0 times [e.time_spent_min for e in medium_entries] # 效率得分归一化平均 20 分钟算 100 分 # 平均 60 分钟算 0 分 avg_time sum(times) / len(times) efficiency max(0, 1 - (avg_time - 20) / 40) return round(efficiency, 2) def _compute_independence(self) - float: 计算 AI 独立解题率 只看最近 50 题的 AI 使用情况 为什么最近 50 题反映当前状态 而非历史累计早期可能没用 AI 但现在依赖了 recent sorted( self.entries, keylambda e: e.solved_at, reverseTrue )[:50] if not recent: return 0.0 independent sum(1 for e in recent if not e.used_ai_hint) return independent / len(recent) def _compute_transfer_ability(self) - float: 计算模式迁移能力 用首次浏览到首次提交的时间来衡量 时间越短 → 模式识别越快 → 迁移能力越强 qualified [ e for e in self.entries if e.first_view_to_submit_min 0 ] if not qualified: return 0.0 # 只取最近 30 题 recent sorted( qualified, keylambda e: e.solved_at, reverseTrue )[:30] times [e.first_view_to_submit_min for e in recent] avg_time sum(times) / len(times) # 迁移能力归一化平均 5 分钟 → 100%30 分钟 → 0% return round(max(0, 1 - (avg_time - 5) / 25), 2) def _overall_score(self) - float: 综合能力得分 if not self.profile: return 0.0 # 各维度权重 weights { 模式广度: 0.25, 解题效率: 0.20, 自主解题率: 0.25, 迁移能力: 0.15 } # 剩余 0.15 为各模式深度的平均 score 0 for dim, weight in weights.items(): score self.profile.get(dim, 0) * weight # 模式深度平均 depth_dims [ v for k, v in self.profile.items() if k.startswith(深度_) ] if depth_dims: score sum(depth_dims) / len(depth_dims) * 0.15 return round(score * 100, 1) def radar_data(self) - Dict[str, float]: 生成雷达图数据5 个核心维度 用于前端可视化展示 return { 模式广度: round(self.profile.get(模式广度, 0) * 100), 解题效率: round(self.profile.get(解题效率, 0) * 100), 自主解题率: round(self.profile.get(自主解题率, 0) * 100), 迁移能力: round(self.profile.get(迁移能力, 0) * 100), 综合得分: self.profile.get(综合得分, 0), } def weakness_report(self) - List[str]: 生成弱点分析报告 profile self.profile weak_patterns [] for pattern in self.PATTERNS: depth_key f深度_{pattern} depth profile.get(depth_key, 0) if depth 0: weak_patterns.append(f未覆盖: {pattern}) elif depth 0.3: weak_patterns.append(f较弱: {pattern} (深度 {depth:.2f})) suggestions [] if profile.get(自主解题率, 1) 0.5: suggestions.append(AI 依赖度过高建议至少 50% 题目独立完成) if profile.get(迁移能力, 1) 0.4: suggestions.append(模式识别速度偏慢建议多练习题目→模式的映射) return weak_patterns suggestions # 使用示例 if __name__ __main__: # 模拟刷题记录 entries [ SolveEntry(1, 双指针, Medium, 15, 1, False, datetime(2024, 7, 10), 5), SolveEntry(2, 二维DP, Hard, 45, 3, True, datetime(2024, 7, 11), 18), # ... 更多记录 ] profiler AbilityProfiler(entries) profile profiler.compute_profile() print( 算法能力画像 ) for key, value in profile.items(): if not key.startswith(深度_): print(f{key}: {value}) print(\n 雷达图数据 ) print(json.dumps(profiler.radar_data(), indent2, ensure_asciiFalse)) print(\n 弱点分析 ) for weakness in profiler.weakness_report(): print(f - {weakness})四、能力画像的局限性特征工程的主观性。权重、阈值、衰减因子——这些参数的值是设计者主观设定的没有最优值。不同的参数设定会影响画像的面貌。更好的做法是用 A/B 测试对比不同参数下画像与用户实际面试表现的关联度。数据稀疏性问题。如果用户在某个模式上只做了 1-2 道题画像就不足以反映真实水平。对于冷启动用户刚刷了 10 道题画像的置信度很低——需要在展示时给出明确的数据置信度提示。能力不等于刷题能力。算法能力画像反映的是刷题能力而实际工程中的算法应用——如在一个分布式系统中正确使用一致性哈希——是另一种能力。画像无法跨越这个鸿沟。过度依赖自我评估的风险。如果用户只刷自己擅长的题型这很常见画像会显示所有模式都很强但实际上未覆盖的模式是真正的盲区。解决方案是在出题推荐中强制引入薄弱模式。五、总结算法能力画像把我感觉我 DP 还行变成了你在二维 DP 上的独立解题率为 72%在一维 DP 上为 85%但背包问题上只有 30%——建议加强背包问题训练。这种量化带来两个好处知道自己真正的短板在哪以及能看到训练是否在改善这些短板。核心方法是从刷题数据中提取五维特征模式广度、模式深度、解题效率、AI 独立率、迁移能力用加权评分和时间衰减模型生成动态画像。这个框架的实现不依赖 AI 模型但结合 AI 后可以做更深入的分析——比如分析代码风格变化、识别思维定式等。能力画像的终极价值不是看自己有多强而是看自己在哪里该变强。