Elephant 在「实现LRU缓存系统」的评测结果
这是该 AI 模型在此测试用例上的详细评测结果。
基本信息
- 模型名称:Elephant
- 用例名称:实现LRU缓存系统
- 测试类型:文本生成
- 评测维度:代码生成
系统提示词(System Prompt)
这是给 AI 模型的背景设定和角色指令:
你是一名资深 Python 后端工程师,擅长数据结构与算法设计。 回答要求: 1. 在给出代码前,先用 2-3 句话简述你的设计思路(选用的数据结构及原因)。 2. 代码需包含完整的类定义、方法实现及必要的注释,风格符合 PEP 8 规范。 3. 在代码之后,提供至少 5 组测试用例(含边界情况),并给出每步的预期输出。 4. 说明核心操作(get / put)的时间复杂度。
用户提示词(User Prompt)
这是用户给 AI 模型的具体任务要求:
请使用 Python 实现一个 LRU(最近最少使用)缓存类 `LRUCache`,具体要求如下: **功能要求:** - 构造函数 `__init__(self, capacity: int)`:初始化缓存,容量固定为 3。 - `get(self, key: int) -> int`: - 若 key 存在于缓存中,返回对应的 value,并将该 key 标记为「最近使用」。 - 若 key 不存在,返回 -1。 - `put(self, key: int, value: int) -> None`: - 若 key 已存在,更新其 value,并将其标记为「最近使用」。 - 若 key 不存在且缓存未满,直接插入。 - 若 key 不存在且缓存已满,先淘汰**最久未使用**的 key,再插入新 key。 **实现约束:** - 必须使用 `collections.OrderedDict` 或手动实现哈希表 + 双向链表,不得使用普通 `dict` + 线性扫描的方式。 - `get` 和 `put` 操作的时间复杂度须为 O(1)。 **示例:**
任务要求
AI 模型需要满足以下要求:
- LRU 核心逻辑正确:get/put 操作后访问顺序更新符合 LRU 语义,缓存满时淘汰最久未使用的有效条目。
- 时间复杂度达标:get、put、delete 操作均为 O(1) 平均复杂度,通过手动实现哈希表+双向链表来保证,不得使用 OrderedDict。
- 过期机制实现正确:惰性删除在 get/put 时触发,过期条目不被返回也不更新访问顺序;purge_expired 能正确批量清除。
- 容量与过期联动处理正确:缓存满时优先清除过期条目再判断是否需要淘汰有效条目,避免错误淘汰未过期数据。
- 统计功能准确:hits/misses 计数在各场景(正常命中、未命中、过期访问)下均正确累计,hit_rate 计算无误,reset_stats 仅重置统计不影响缓存数据。
- 边界条件处理完善:包括 capacity=1、重复 put 同一 key、对不存在 key 执行 delete、所有条目均过期时的 put 操作等场景。
评测结果
综合得分
该模型在此用例的得分为:88.3 分(满分 100 分),已通过
基础难度评测结果
- 得分:88.3 分
- 通过状态:已通过
模型生成产物
以下是该模型针对此任务的实际生成内容:
设计思路:使用 `collections.OrderedDict` 维护键值对及其访问顺序,它内部基于双向链表实现,能在 O(1) 时间内完成插入、删除和移动操作,从而满足 LRU 的 O(1) 时间复杂度要求。 ```python from collections import OrderedDict class LRUCache: """ LRU (Least Recently Used) 缓存实现,使用 OrderedDict 保证 O(1) 时间复杂度。 OrderedDict 内部维护双向链表,支持快速移动节点到末尾或弹出头部。 """ def __init__(self, capacity: int = 3) -> None: """ 初始化 LRU 缓存,设定固定容量。 :param capacity: 缓存最大容量,默认为 3 """ self._capacity = capacity self._cache = OrderedDict() def get(self, key: int) -> int: """ 获取键对应的值,若存在则将其标记为最近使用(移至末尾)。 :param key: 要查询的键 :return: 键对应的值,若不存在返回 -1 """ if key not in self._cache: return -1 # 将键移动到 OrderedDict 末尾,表示最近使用 self._cache.move_to_end(key) return self._cache[key] def put(self, key: int, value: int) -> None: """ 插入或更新键值对,并维护 LRU 顺序。 :param key: 键 :param value: 值 """ if key in self._cache: # 键存在:更新值并移至末尾 self._cache.move_to_end(key) else: # 键不存在且缓存已满:移除最久未使用的项(首部) if len(self._cache) >= self._capacity: self._cache.popitem(last=False) # 插入或更新键值(默认插入到末尾) self._cache[key] = value ``` ### 测试用例与预期输出 ```python # 初始化缓存 cache = LRUCache() # 用例 1: 插入三个元素 [1:1, 2:2, 3:3] cache.put(1, 1) cache.put(2, 2) cache.put(3, 3) print(cache.get(1)) # 1(访问后 1 变为最近使用) # 用例 2: 插入第四个元素 4,导致淘汰最久未使用的 2 cache.put(4, 4) print(cache.get(2)) # -1(已被淘汰) # 用例 3: 更新已存在键 3 的值 cache.put(3, 33) print(cache.get(3)) # 33 # 用例 4: 访问键 3 使其成为最近使用,再插入 5 淘汰 1 cache.get(3) cache.put(5, 5) print(cache.get(1)) # -1(已被淘汰) print(cache.get(4)) # 4(仍在缓存中) # 用例 5: 边界情况 — 容量为 1 的缓存 single_cache = LRUCache(1) single_cache.put(10, 100) print(single_cache.get(10)) # 100 single_cache.put(20, 200) print(single_cache.get(10)) # -1(被新键覆盖) print(single_cache.get(20)) # 200 ``` ### 核心操作时间复杂度 - `get`:O(1) — 哈希表查找 + 链表节点移动均为常数时间。 - `put`:O(1) — 哈希表插入/更新 + 链表插入/删除均为常数时间。
AI 评审点评
以下是 AI 评审对该模型输出的点评:
【KIMI】整体而言,这段代码实现了LRU缓存的核心功能,逻辑正确,时间复杂度满足要求。代码结构清晰,注释详细,但在一些细节上还有改进空间。考虑到代码的准确性和效率,给出了较高的分数。
进阶难度评测结果
- 得分:86.7 分
- 通过状态:已通过
模型生成产物
以下是该模型针对此任务的实际生成内容:
### 设计思路 - **数据结构选型**:采用哈希表(字典)配合双向链表实现 O(1) 的 `get`/`put`。哈希表存储键到链表节点的映射,链表维护访问顺序(头部最新、尾部最旧),节点记录值、过期时间戳。 - **过期处理策略**:选择**惰性删除**,因为仅在访问时检查过期避免额外开销,配合 `purge_expired()` 主动清理,满足“过期条目不占有效容量”的要求。 - **淘汰逻辑**:插入时先清理已过期条目以释放空间,仅在仍满时淘汰尾部有效最久未使用节点,确保容量约束正确。 ```python import time from typing import Any, Optional class _Node: __slots__ = ("key", "value", "expires_at", "prev", "next") def __init__(self, key: str, value: Any, expires_at: Optional[float]): self.key = key self.value = value self.expires_at = expires_at # None means never expires self.prev: Optional[_Node] = None self.next: Optional[_Node] = None class LRUCache: def __init__(self, capacity: int): if capacity = 1") self.capacity: int = capacity self.hits: int = 0 self.misses: int = 0 # sentinel nodes to simplify boundary operations self.head = _Node("", None, None) self.tail = _Node("", None, None) self.head.next = self.tail self.tail.prev = self.head self.map: dict[str, _Node] = {} def _is_expired(self, node: _Node) -> bool: return node.expires_at is not None and time.time() >= node.expires_at def _remove_node(self, node: _Node) -> None: """O(1): detach node from linked list.""" prev_node = node.prev next_node = node.next prev_node.next = next_node next_node.prev = prev_node def _add_to_head(self, node: _Node) -> None: """O(1): insert node right after head.""" node.next = self.head.next node.prev = self.head self.head.next.prev = node self.head.next = node def _move_to_head(self, node: _Node) -> None: """O(1): move existing node to head.""" self._remove_node(node) self._add_to_head(node) def _pop_tail(self) -> _Node: """O(1): remove and return the real tail node (before sentinel).""" node = self.tail.prev self._remove_node(node) return node def _purge_expired_one(self, key: str) -> bool: """ Check and remove expired key if present. Returns True if expired entry was removed, False otherwise. """ if key not in self.map: return False node = self.map[key] if self._is_expired(node): self._remove_node(node) del...
AI 评审点评
以下是 AI 评审对该模型输出的点评:
【KIMI】整体而言,这段代码实现了一个功能完整、性能优化的LRU缓存系统。核心逻辑、过期机制和容量联动处理都符合题目要求,时间复杂度也达标。代码结构和测试覆盖度较好,但注释部分还有提升空间。作为资深后端工程师,能够设计并实现这样一个LRU缓存系统,体现了较高的专业水准。
困难难度评测结果
- 得分:76.7 分
- 通过状态:已通过
模型生成产物
以下是该模型针对此任务的实际生成内容:
## 系统设计 ### 架构图(文字描述) ``` [Client] → [多级缓存系统] │ ├─→ [一致性哈希分片] → 路由到特定缓存节点 │ │ │ ├─→ [L1缓存 (本地内存)] → LRU双向链表 + 哈希表 (O(1)操作) │ │ │ │ │ ├─ 热点数据识别 → 优先级提升 │ │ ├─ 惰性删除 + 定期清理 (TTL) │ │ └─ 细粒度锁 (分段锁/读写锁) │ │ │ └─→ [L2缓存 (Redis模拟)] → 相同LRU结构 │ (写穿透策略,节点动态增删最小迁移) │ └─→ [数据源] (当缓存未命中时回源) ↓ (回填L1和L2) ``` ### 核心组件说明 1. **一致性哈希环**:使用MD5计算节点哈希值,支持虚拟节点(3组),实现键到缓存节点的路由,最小化节点增删时的数据迁移。 2. **L1缓存**:本地内存,基于手动实现的双向链表+哈希表,O(1)时间复杂度,支持LRU淘汰、TTL、热点识别。 3. **L2缓存**:模拟Redis缓存(使用Mock类),行为同L1但作为二级存储。 4. **热点识别**:基于滑动窗口访问计数,自动提升热点Key在L1中的优先级(如延长TTL或固定不淘汰)。 5. **降级策略**:当L2不可用时自动降级为仅L1模式,记录降级事件;L2恢复后自动切换。 6. **统计模块**:跟踪L1/L2命中率、请求总数、热点Key、降级次数。 ### 关键设计决策的权衡分析 - **锁粒度**:采用分段锁(每个LRU实例独立锁)而非全局锁,提升并发性能;权衡为内存开销增加。 - **一致性哈希虚拟节点**:使用多组虚拟节点(每组150个)提升键分布均匀性,但增加环维护复杂度。 - **TTL机制**:惰性删除(get时检查)+ 定期清理(后台线程扫描)平衡实时性与性能。 - **热点提升策略**:通过延长TTL或固定不淘汰实现,但需注意内存占用;设置最大热点阈值防止滥用。 ### 处理竞态条件 - **锁粒度选择**:每个LRU缓存实例(L1/L2)使用独立的读写锁(`threading.RLock`),`put/get`操作在锁保护下,确保O(1)操作原子性。 - **死锁预防**:避免嵌套锁;一致性哈希环操作使用全局锁(但频率低),且不持有LRU锁。 - **缓存与Redis同步**:write-through策略下,先更新L1再更新L2;若L2失败触发降级,L1保留数据(最终一致性通过后台同步补偿)。 ### 分布式环境下的数据一致性 - **缓存与Redis同步**:采用write-through策略,更新时先写L1再写L2。L2失败时记录降级状态,L2恢复后通过后台任务重试同步(简化版:下次写穿透时重试)。 - **一致性哈希节点变化**:使用一致性哈希最小化迁移;节点增删时仅影响环上相邻键的映射。 ### 类图/模块依赖 ``` [CacheNode] ──> [LRUCache] [ConsistentHash] ──> [CacheNode] [MockRedis] ──> [LRUCache] (接口兼容) [CacheSystem] ──> [ConsistentHash], [LRUCache] (x2), [Stats], [MockRedis] ``` - `CacheSystem`:主入口,整合所有组件。 - `LRUCache`:手动实现双向链表+哈希表,支持LRU、TTL、热点。 - `ConsistentHash`:一致性哈希环管理。 - `MockRedis`:模拟Redis行为。 - `Stats`:统计模块。 ### 核心机制实现原理 1. **一致性哈希**: - 使用MD5计算节点哈希值(每节点生成3组虚拟节点)。 - 键映射到顺时针第一个节点;节点增删仅影响环上相邻键,最小迁移。 - 边界:虚拟节点数需平衡分布与内存;使用排序列表二分查找(O(log n))。 2. **热点识别**: - 基于滑动窗口计数器(每键独立计数)。 - 访问频率超过阈值(如>100/s)提升为热点:延长TTL或固定不淘汰(通过`is_hot`标志)。 - 边界:热点阈值可调;内存限制下热点数上限(如100),超限降级。 3. **降级策略**: - L2不可用(模拟异常)时设置`degraded`标志,后续请求仅用L1。 - 记录降级次数;L2恢复后(通过心跳检测)自动清除标志。 - 边界:L2恢复需手动触发或定时检查;降级期间数据不一致需应用层处理。 ## 核心代码实现 ```python import hashlib import threading import time from typing import...
AI 评审点评
以下是 AI 评审对该模型输出的点评:
【KIMI】整体而言,该实现基本满足了题目要求,实现了多级LRU缓存系统的核心功能。代码结构清晰,注释充分,具备一定的生产可读性。但在线程安全和系统设计方面仍有改进空间,如锁粒度的进一步优化、统计精度的提升等。
相关链接
您可以通过以下链接查看更多相关内容: