Elephant 在「实现缓存淘汰算法」的评测结果
这是该 AI 模型在此测试用例上的详细评测结果。
基本信息
- 模型名称:Elephant
- 用例名称:实现缓存淘汰算法
- 测试类型:文本生成
- 评测维度:代码生成
系统提示词(System Prompt)
这是给 AI 模型的背景设定和角色指令:
你是一名资深软件工程师,擅长数据结构与算法设计,尤其熟悉各类缓存机制的原理与实现。 回答要求: 1. 使用 Python 语言实现,代码需包含必要的注释,解释关键逻辑。 2. 实现完成后,简要说明你选择该数据结构的原因(1-3 句话即可)。 3. 提供至少 3 个测试用例(包含正常流程、缓存满时的淘汰行为、以及边界情况),并展示预期输出。 4. 代码需具备良好的可读性:变量命名清晰,逻辑层次分明。
用户提示词(User Prompt)
这是用户给 AI 模型的具体任务要求:
请使用 Python 实现一个 FIFO(先进先出)缓存类 `FIFOCache`,要求如下: **功能要求:** - `__init__(self, capacity: int)`:初始化缓存,`capacity` 为缓存的最大容量(正整数)。 - `get(self, key: int) -> int`:若 `key` 存在于缓存中,返回对应的值;否则返回 `-1`。 - `put(self, key: int, value: int) -> None`:将键值对写入缓存。 - 若 `key` 已存在,则**更新**其对应的值(不改变该 key 在队列中的位置,即不影响淘汰顺序)。 - 若 `key` 不存在且缓存已满,则**先淘汰最早加入**的那个 key,再插入新键值对。 - 若缓存未满,直接插入。 **边界情况说明:** - `capacity` 保证为正整数(≥ 1),无需处理容量为 0 的情况。 - `key` 和 `value` 均为非负整数。 **示例:**
任务要求
AI 模型需要满足以下要求:
- 必须手动实现双向链表节点类(含 prev/next 指针),不得借助 OrderedDict 等内置有序容器,否则核心算法得分为 0。
- get 和 put 操作均需达到严格的 O(1) 时间复杂度,需在代码注释或说明中给出分析依据。
- 缓存满时的淘汰逻辑必须正确:淘汰链表尾部(最久未使用)节点,同时同步删除哈希表中对应的 key。
- 对已存在 key 执行 put 时,必须正确更新值并将节点移动到链表头部(标记为最近使用),而非插入新节点。
- 测试用例需覆盖至少 4 种场景:正常读写命中、缓存未满时的插入、缓存满时的淘汰、对已有 key 的更新后顺序验证。
- 代码结构清晰,变量命名语义明确,关键操作(如节点插入头部、从链表移除节点)应封装为独立的私有方法。
评测结果
综合得分
该模型在此用例的得分为:86.7 分(满分 100 分),已通过
基础难度评测结果
- 得分:86.7 分
- 通过状态:已通过
模型生成产物
以下是该模型针对此任务的实际生成内容:
```python from collections import deque class FIFOCache: def __init__(self, capacity: int): """ 初始化 FIFO 缓存。 :param capacity: 缓存的最大容量(正整数) """ self.capacity = capacity self.cache = {} # 存储键值对,用于 O(1) 时间复杂度的 get 和 put 更新 self.queue = deque() # 使用双端队列维护键的插入顺序,实现 FIFO 淘汰策略 def get(self, key: int) -> int: """ 获取键对应的值。 :param key: 要查询的键 :return: 若键存在,返回对应值;否则返回 -1 """ return self.cache.get(key, -1) def put(self, key: int, value: int) -> None: """ 插入或更新键值对。 规则: - 若键已存在:更新值,但不改变其在队列中的位置(即不影响淘汰顺序) - 若键不存在且缓存已满:淘汰最早插入的键(FIFO),再插入新键 - 若缓存未满:直接插入新键 :param key: 键 :param value: 值 """ if key in self.cache: # 键已存在:仅更新值,不改变队列顺序 self.cache[key] = value else: # 键不存在 if len(self.cache) >= self.capacity: # 缓存已满,淘汰最早插入的键(FIFO) oldest_key = self.queue.popleft() del self.cache[oldest_key] # 插入新键值对 self.cache[key] = value self.queue.append(key) # 测试用例 if __name__ == "__main__": # 测试用例 1:正常流程 cache1 = FIFOCache(2) cache1.put(1, 1) cache1.put(2, 2) print(cache1.get(1)) # 预期输出: 1 cache1.put(3, 3) # 缓存满,淘汰 key=1 print(cache1.get(1)) # 预期输出: -1(已被淘汰) print(cache1.get(2)) # 预期输出: 2 print(cache1.get(3)) # 预期输出: 3 # 测试用例 2:更新已存在的键(不影响淘汰顺序) cache2 = FIFOCache(2) cache2.put(1, 1) cache2.put(2, 2) cache2.put(1, 100) # 更新 key=1 的值,但不改变其插入顺序 cache2.put(3, 3) # 缓存满,应淘汰最早插入的 key=2(key=1 虽被更新但仍在队首) print(cache2.get(1)) # 预期输出: 100 print(cache2.get(2)) # 预期输出: -1(被淘汰) print(cache2.get(3)) # 预期输出: 3 # 测试用例 3:边界情况 - 容量为 1 cache3 = FIFOCache(1) cache3.put(1, 10) print(cache3.get(1)) # 预期输出: 10 cache3.put(2, 20) # 缓存满,淘汰 key=1 print(cache3.get(1)) # 预期输出: -1 print(cache3.get(2)) # 预期输出: 2 ``` ### 选择该数据结构的原因: 使用 `dict` 实现 O(1) 的键值查找与更新,使用 `deque` 维护插入顺序以支持高效的 FIFO 淘汰(`popleft` 为 O(1))。两者结合在满足功能需求的同时保证了高效的性能。 ### 测试用例说明: 1. **正常流程**:验证基本插入、查询和淘汰逻辑。 2. **更新不影响顺序**:验证更新已存在键时,其淘汰优先级不变。 3. **边界情况(容量为 1)**:验证最小容量下的插入与淘汰行为。
AI 评审点评
以下是 AI 评审对该模型输出的点评:
【KIMI】整体来看,这段代码实现了FIFO缓存的核心功能,数据结构的选择和实现合理,代码可读性较好。但是测试用例还不够全面,缺少对异常输入的处理。代码注释也有待加强。
进阶难度评测结果
- 得分:85.0 分
- 通过状态:已通过
模型生成产物
以下是该模型针对此任务的实际生成内容:
### 设计思路说明 为了实现 O(1) 时间复杂度的 `get` 和 `put` 操作,我们采用 **哈希表 + 双向链表** 的组合结构: - **哈希表**(`dict`)用于存储键到链表节点的映射,实现 O(1) 时间复杂度的查找。 - **双向链表**用于维护键的使用顺序,链表头部表示“最近使用”,尾部表示“最久未使用”。插入、删除和移动节点操作均可在 O(1) 时间内完成(通过维护头尾哨兵节点简化边界处理)。 - 当访问或更新一个键时,将其对应节点移至链表头部;当缓存满时,从链表尾部移除最久未使用的节点。 这种结构避免了使用 `OrderedDict`,完全手动控制节点链接,确保符合约束。 --- ### Python 实现代码 ```python class DLinkedNode: """双向链表节点类""" def __init__(self, key=0, value=0): self.key = key # 存储键,用于在淘汰时同步删除哈希表中的条目 self.value = value # 存储值 self.prev = None # 前驱指针 self.next = None # 后继指针 class LRUCache: """LRU 缓存类,使用哈希表 + 双向链表手动实现""" def __init__(self, capacity: int): # 哈希表:key -> DLinkedNode,实现 O(1) 查找 self.cache = {} # 使用伪头节点和伪尾节点简化边界操作(避免空指针判断) self.head = DLinkedNode() self.tail = DLinkedNode() self.head.next = self.tail self.tail.prev = self.head self.capacity = capacity self.size = 0 # 当前缓存中有效节点数 def _add_node(self, node: DLinkedNode) -> None: """在链表头部添加新节点(表示最近使用)""" node.prev = self.head node.next = self.head.next self.head.next.prev = node self.head.next = node def _remove_node(self, node: DLinkedNode) -> None: """从链表中移除指定节点""" prev_node = node.prev next_node = node.next prev_node.next = next_node next_node.prev = prev_node def _move_to_head(self, node: DLinkedNode) -> None: """将已有节点移动到头部(先移除再插入)""" self._remove_node(node) self._add_node(node) def _pop_tail(self) -> DLinkedNode: """移除并返回尾部节点(最久未使用),用于淘汰""" node = self.tail.prev self._remove_node(node) return node def get(self, key: int) -> int: """获取键对应的值,若存在则移至头部表示最近使用""" node = self.cache.get(key) if not node: return -1 # 命中:移动到头部表示最近使用 self._move_to_head(node) return node.value def put(self, key: int, value: int) -> None: """插入或更新键值对,维护 LRU 顺序""" if key in self.cache: # 键存在:更新值并移至头部 node = self.cache[key] node.value = value self._move_to_head(node) else: # 键不存在:创建新节点 new_node = DLinkedNode(key, value) self.cache[key] = new_node self._add_node(new_node) self.size += 1 # 若超出容量,淘汰尾部节点 if self.size > self.capacity: tail = self._pop_tail() del self.cache[tail.key] # 同步删除哈希表条目...
AI 评审点评
以下是 AI 评审对该模型输出的点评:
【KIMI】整体而言,代码实现了LRU缓存的基本功能,包括get和put操作,以及缓存满时的淘汰逻辑。手动实现了双向链表节点,并使用了头尾哨兵节点简化边界处理。测试用例覆盖了多种场景,验证了代码的正确性。但在容量为1的边界情况处理上存在问题,put操作时没有正确淘汰旧节点。代码可读性较好,关键操作有清晰的注释说明。
困难难度评测结果
- 得分:81.7 分
- 通过状态:已通过
模型生成产物
以下是该模型针对此任务的实际生成内容:
### Design Doc 本系统采用策略模式实现自适应缓存,核心为 `Cache` 类封装共享数据与监控逻辑。策略模块通过抽象基类 `CachePolicy` 定义接口,`LRUCache` 与 `LFUCache` 分别实现 O(1) 的 `get/put`。LRU 使用双向链表+哈希表维护访问顺序,LFU 使用频率哈希表+最小频率指针优化。命中率监控采用固定大小滑动窗口(队列存储最近命中/未命中),每隔 `check_interval` 次操作计算命中率并触发策略切换:LRU 命中率过低切换至 LFU,LFU 命中率过高回切 LRU。切换时保留缓存数据,仅更换淘汰策略。预留 `ARCPolicy` 骨架以支持未来扩展。 ### Python 代码 ```python from __future__ import annotations from abc import ABC, abstractmethod from collections import deque from typing import Dict, List, Optional, Tuple class CachePolicy(ABC): """缓存策略抽象基类,定义核心接口与扩展预留。""" @abstractmethod def get(self, key: int) -> int: """获取键对应的值,不存在返回 -1。时间复杂度 O(1)。""" pass @abstractmethod def put(self, key: int, value: int) -> None: """插入或更新键值对,容量满时淘汰。时间复杂度 O(1)。""" pass @abstractmethod def remove(self, key: int) -> bool: """移除指定键,成功返回 True。时间复杂度 O(1)。""" pass @abstractmethod def on_access(self, key: int) -> None: """记录一次访问(命中时调用)。时间复杂度 O(1)。""" pass def prepare_for_switch(self) -> None: """策略切换时预留的钩子,默认无操作。""" class ListNode: """双向链表节点,用于 LRU 策略。""" __slots__ = ("key", "value", "prev", "next") def __init__(self, key: int = -1, value: int = -1) -> None: self.key: int = key self.value: int = value self.prev: Optional[ListNode] = None self.next: Optional[ListNode] = None class LRUCache(CachePolicy): """手动实现的双向链表 + 哈希表 LRU 缓存,保证 O(1) 时间复杂度。""" def __init__(self, capacity: int) -> None: if capacity = 1") self.capacity: int = capacity self.cache: Dict[int, ListNode] = {} # 虚拟头尾节点简化边界操作 self.head = ListNode() self.tail = ListNode() self.head.next = self.tail self.tail.prev = self.head def _add_node(self, node: ListNode) -> None: """在链表头部添加节点(最新访问)。时间复杂度 O(1)。""" node.prev = self.head node.next = self.head.next self.head.next.prev = node self.head.next = node def _remove_node(self, node: ListNode) -> None: """从链表中移除指定节点。时间复杂度 O(1)。""" prev_node = node.prev next_node = node.next if prev_node and next_node: prev_node.next = next_node next_node.prev = prev_node def _move_to_head(self, node: ListNode) -> None:...
AI 评审点评
以下是 AI 评审对该模型输出的点评:
【KIMI】候选人的实现整体上满足了题目要求,特别是在系统设计和自适应切换机制方面表现较好。但在算法实现的细节上还存在一些瑕疵,如LFU的`_update_freq`方法需要优化以确保`min_freq`的正确性。代码质量和测试用例覆盖度较高,但仍有改进空间,特别是在测试用例的自动化和扩展性方面。
相关链接
您可以通过以下链接查看更多相关内容: