Respuesta A: Google Gemini 2.5 Flash-Lite
Respuesta: ```python import threading import time from collections import OrderedDict class Node: def __init__(self, key, value): self.key = key self.value = value self.prev = None self.next = None class LRUCache: def __init__(self, capacity: int): if capacity <= 0: raise ValueError("La capacidad debe ser un entero positivo.") self.capacity = capacity self.cache = {} self.head = Node(None, None) # Cabeza ficticia self.tail = Node(None, None) # Cola ficticia self.head.next = self.tail self.tail.prev = se...
Mostrar respuesta completa ▼
Respuesta: ```python import threading import time from collections import OrderedDict class Node: def __init__(self, key, value): self.key = key self.value = value self.prev = None self.next = None class LRUCache: def __init__(self, capacity: int): if capacity <= 0: raise ValueError("La capacidad debe ser un entero positivo.") self.capacity = capacity self.cache = {} self.head = Node(None, None) # Cabeza ficticia self.tail = Node(None, None) # Cola ficticia self.head.next = self.tail self.tail.prev = self.head self.lock = threading.RLock() # Usando RLock por simplicidad en este ejemplo, pero es posible un enfoque más detallado. def _remove_node(self, node): prev_node = node.prev next_node = node.next prev_node.next = next_node next_node.prev = prev_node def _add_to_front(self, node): node.next = self.head.next node.prev = self.head self.head.next.prev = node self.head.next = node def get(self, key): with self.lock: if key in self.cache: node = self.cache[key] self._remove_node(node) self._add_to_front(node) return node.value return None def put(self, key, value): with self.lock: if key in self.cache: node = self.cache[key] node.value = value self._remove_node(node) self._add_to_front(node) else: if len(self.cache) >= self.capacity: # Expulsar el elemento LRU lru_node = self.tail.prev self._remove_node(lru_node) del self.cache[lru_node.key] new_node = Node(key, value) self.cache[key] = new_node self._add_to_front(new_node) def delete(self, key): with self.lock: if key in self.cache: node = self.cache[key] self._remove_node(node) del self.cache[key] return True return False def __len__(self): with self.lock: return len(self.cache) def __str__(self): with self.lock: items = [] current = self.head.next while current != self.tail: items.append(f"({current.key}: {current.value})") current = current.next return " -> ".join(items) # --- Explicación de la estrategia de concurrencia --- # Esta implementación usa un único threading.RLock para todas las operaciones (get, put, delete). # Aunque esto garantiza la seguridad de los hilos, no minimiza la contención como solicita el enunciado. # Un enfoque más avanzado y detallado implicaría segmentar la caché o usar # estructuras de datos sin bloqueo. Por ejemplo, se podrían usar varios bloqueos, cada uno protegiendo un subconjunto # de las entradas de la caché (bloqueo por franjas), o usar bloqueos de lectura-escritura para permitir que varios lectores # accedan a diferentes partes de la caché de forma concurrente. # El RLock actual garantiza que solo un hilo pueda modificar el estado interno de la caché (el diccionario y la lista doblemente enlazada) a la vez. # Esto evita condiciones de carrera en las que varios hilos podrían intentar actualizar el mismo nodo, agregar/quitar nodos o expulsar entradas simultáneamente, lo que llevaría a corrupción de datos o a un orden LRU incorrecto. # El uso de RLock (bloqueo reentrante) permite que un hilo adquiera el bloqueo varias veces, lo que puede ser útil en escenarios complejos, pero no cambia fundamentalmente las características de contención en comparación con un Lock estándar en este uso específico. # --- Demostración --- def worker(cache, operations, thread_id): for op, key, value in operations: if op == 'get': cache.get(key) elif op == 'put': cache.put(key, value) elif op == 'delete': cache.delete(key) # Simular algo de trabajo y permitir que otros hilos se ejecuten time.sleep(0.001) def run_demonstration(): print("Ejecutando demostración de caché LRU...") capacity = 5 cache = LRUCache(capacity) num_threads = 10 ops_per_thread = 50 all_operations = [] for i in range(num_threads): thread_ops = [] for j in range(ops_per_thread): op_type = ['get', 'put', 'delete'][j % 3] key = (i * ops_per_thread + j) % (capacity * 2) # Claves que podrían o no estar en la caché value = f"val_{i}_{j}" thread_ops.append((op_type, key, value)) all_operations.append(thread_ops) threads = [] for i in range(num_threads): thread = threading.Thread(target=worker, args=(cache, all_operations[i], i)) threads.append(thread) thread.start() for thread in threads: thread.join() print(f"Demostración terminada. Tamaño final de la caché: {len(cache)}") # Verificar que la caché nunca exceda su capacidad assert len(cache) <= capacity, f"¡Se excedió la capacidad de la caché! Tamaño actual: {len(cache)}, Capacidad: {capacity}" print("Verificación superada: se mantiene la capacidad de la caché.") # Comprobación básica de integridad de datos (no exhaustiva) print("Realizando comprobación básica de integridad...") # Esta es una comprobación muy básica. Una prueba más robusta implicaría rastrear los estados esperados. # Por ahora, solo nos aseguramos de que no haya corrupción evidente como tamaño negativo o excepciones durante el acceso. try: # Intentar acceder a algunas claves que probablemente fueron insertadas for i in range(min(5, capacity)): key_to_check = (i * ops_per_thread) % (capacity * 2) cache.get(key_to_check) print("Comprobación básica de integridad superada.") except Exception as e: print(f"La comprobación de integridad falló con la excepción: {e}") assert False, "La comprobación de integridad de la caché falló." if __name__ == "__main__": run_demonstration() ```
Resultado
Votos ganadores
0 / 3
Puntuacion media
Puntuacion total
Comentario general
La respuesta A implementa una caché LRU convencional con un diccionario más lista doblemente enlazada y su lógica LRU básica de un solo hilo es sólida. Maneja correctamente la validación de capacidad positiva, get/put/delete, e incluye una demostración multihilo. Sin embargo, utiliza explícitamente un RLock global en cada operación, lo que viola el requisito de la consigna de evitar un bloqueo de grano grueso y no permite gets concurrentes en claves diferentes. Las aserciones de la demostración son débiles y no validan significativamente la corrección estricta de concurrencia o la integridad de los datos más allá de las comprobaciones básicas de capacidad.
Ver detalle de evaluacion ▼
Correccion
Peso 35%La implementación de diccionario más lista doblemente enlazada es correcta para el comportamiento LRU estándar, y get actualiza la recencia mientras que put elimina tail.prev. El comportamiento de delete también es correcto. Sin embargo, la corrección para el entorno concurrente previsto está limitada por la serialización completa bajo un solo bloqueo, y el diseño no aborda las condiciones de carrera más difíciles que enfatiza la tarea.
Integridad
Peso 20%Incluye la API requerida, validación para capacidad no positiva, explicación y una demostración multihilo. Pero no satisface la solicitud de minimizar la contención o las lecturas concurrentes no bloqueantes en claves diferentes, y la demostración no prueba profundamente la corrección bajo entrelazamientos.
Calidad del codigo
Peso 20%El código es legible, directo y está organizado claramente. Los métodos auxiliares para las operaciones de lista son sensatos. Sin embargo, hay una importación innecesaria de OrderedDict, la explicación admite que el diseño no es lo que se solicitó y la lógica de pruebas es bastante superficial.
Valor practico
Peso 15%Como una caché LRU simple y segura para hilos, es utilizable en la práctica, pero el bloqueo global único crea alta contención y frustra el requisito práctico principal de acceso concurrente escalable. La demostración da una confianza limitada para cargas de trabajo multihilo reales.
Seguimiento de instrucciones
Peso 10%Sigue los requisitos de API y formato de módulo, pero claramente no cumple la instrucción de evitar un bloqueo global de grano grueso y permitir gets concurrentes en claves diferentes. Esa es una omisión importante del requisito central.
Puntuacion total
Comentario general
La respuesta A proporciona una implementación correcta de caché LRU con una estructura adecuada de lista doblemente enlazada y mapa hash, lógica de desalojo correcta y manejo apropiado de casos extremos. El código es limpio y legible. Sin embargo, falla fundamentalmente el requisito central de concurrencia al usar un único RLock para todas las operaciones, lo cual el propio autor reconoce que no es aceptable según el enunciado. La demostración está presente pero las aserciones son mínimas. Esta es una implementación base sólida que necesitaría un rediseño significativo para cumplir con los requisitos de concurrencia de grano fino.
Ver detalle de evaluacion ▼
Correccion
Peso 35%La respuesta A implementa una caché LRU correcta con una lista doblemente enlazada y mapa hash adecuados. La lógica de desalojo, las operaciones get/put/delete y el manejo de casos extremos (ValueError para capacidad no positiva) son todos correctos. Sin embargo, utiliza un único RLock para todo, lo cual el propio autor reconoce que no satisface el requisito de concurrencia de grano fino. La semántica LRU es correcta pero el modelo de concurrencia es ingenuo.
Integridad
Peso 20%La respuesta A incluye todas las operaciones requeridas, manejo de casos extremos y una demostración con múltiples hilos. Sin embargo, falla explícitamente en implementar la concurrencia de grano fino requerida (usa un único RLock global), que es un requisito central. La demostración está presente pero las aserciones son mínimas y la verificación de integridad es superficial.
Calidad del codigo
Peso 20%La respuesta A es limpia y legible con buena estructura. El uso de centinelas de cabeza/cola ficticias es apropiado. Sin embargo, usar RLock en lugar de Lock es innecesario aquí y el comentario reconoce que la implementación no cumple con los requisitos establecidos. El código está bien organizado pero el diseño de concurrencia es una deficiencia conocida.
Valor practico
Peso 15%El enfoque de bloqueo único de la respuesta A tiene un valor práctico limitado para escenarios de alta concurrencia. Funcionaría correctamente pero sería un cuello de botella bajo carga. El autor reconoce esta limitación. Para uso en producción, la implementación necesitaría un rediseño significativo.
Seguimiento de instrucciones
Peso 10%La respuesta A sigue la mayoría de las instrucciones pero falla explícitamente el requisito central de ir más allá de un único bloqueo global. El enunciado dice 'un único bloqueo de grano grueso alrededor de todo no es aceptable' y la respuesta A usa exactamente eso. El autor lo reconoce en los comentarios, lo que muestra conciencia pero no soluciona el problema. La demostración y el manejo de casos extremos están presentes.
Puntuacion total
Comentario general
La respuesta A proporciona una implementación correcta y segura para hilos de una caché LRU utilizando un diccionario estándar y una lista doblemente enlazada. Su código es limpio y fácil de entender. Sin embargo, falla fundamentalmente en el requisito central de concurrencia de la indicación al usar un único bloqueo global para todas las operaciones. Si bien reconoce correctamente esta limitación en su explicación, esta elección de diseño la convierte en una solución no conforme y mucho más simple de lo que se solicitó. El código de demostración es funcional, pero sus comprobaciones de integridad son muy básicas.
Ver detalle de evaluacion ▼
Correccion
Peso 35%La lógica LRU y la seguridad para hilos son correctas debido al único bloqueo global. Sin embargo, no es una implementación correcta del diseño concurrente solicitado, ya que serializa todas las operaciones, fallando en cumplir el objetivo de rendimiento.
Integridad
Peso 20%La respuesta proporciona todos los componentes solicitados: la implementación de la clase, una explicación y una demostración. Sin embargo, la implementación central está incompleta con respecto a los requisitos de concurrencia de la indicación.
Calidad del codigo
Peso 20%El código está limpio, bien comentado y sigue los modismos estándar de Python. La implementación del diccionario y la lista doblemente enlazada es sencilla y correcta.
Valor practico
Peso 15%Una caché LRU con un único bloqueo tiene un valor práctico limitado en aplicaciones de alta concurrencia, ya que el bloqueo se convierte rápidamente en un cuello de botella. Sirve como un ejemplo básico de libro de texto, pero no es una solución de alto rendimiento.
Seguimiento de instrucciones
Peso 10%La respuesta no sigue una restricción negativa clave en la indicación: "un único bloqueo de grano grueso alrededor de todo no es aceptable." Al usar un único RLock para todas las operaciones, viola directamente esta instrucción, que era central para la dificultad de la tarea.