[Algoritmo] Búsqueda de memoria

Parte I Introducción

La memorización (inglés: memoization) es una técnica de optimización que aumenta la velocidad de ejecución de los programas informáticos. Al almacenar el valor de retorno de una función computacionalmente intensiva, cuando el resultado se necesita nuevamente, se obtiene de la memoria caché sin volver a calcular para ahorrar tiempo de cálculo. La memorización es una solución típica para lograr un equilibrio entre el tiempo de computación y el espacio de memoria de la computadora. En informática, la memoización o memoización es una técnica de optimización que se utiliza principalmente para acelerar los programas informáticos mediante el almacenamiento de los resultados de llamadas a funciones costosas y la devolución de los resultados almacenados en caché cuando se vuelve a producir la misma entrada. La memorización también se usa en otros contextos (y para fines distintos a las ganancias de velocidad), como en el análisis de descenso recursivo mutuo simple. Aunque está relacionado con el almacenamiento en caché, la memorización se refiere a un caso específico de esta optimización, distinta de las formas de almacenamiento en caché, como el almacenamiento en búfer o los mecanismos de reemplazo de archivos de caché. En el contexto de algunos lenguajes de programación lógicos, la memorización también se denomina tabling.

Mucho se ha dicho anteriormente, pero la memorización es realmente muy simple. Utiliza una lista para guardar los resultados de cálculos anteriores (por ejemplo, guardarlos en una lista), y luego cuando hay el mismo requerimiento, sacarlos de la lista directamente para evitar operaciones repetidas.
Porque aumenta la sobrecarga de memoria de una lista en comparación con la operación anterior que no usa memoria, pero evita operaciones repetidas, por lo que es una solución que logra un equilibrio entre el tiempo de cómputo y el espacio de memoria de la computadora.

Parte II Implementación de la búsqueda de memoria

Basándonos en un ejemplo , registremos brevemente cómo se implementa la memorización en el trabajo real para varios idiomas que el autor usa con frecuencia.


Primero, la descripción del tema:

Hay dos tipos de sopa, A y B. Al principio hay n ml de cada tipo de sopa. Hay cuatro operaciones de asignación:

  • Proporcione 100 ml de sopa A y 0 ml de sopa B.
  • Proporcione 75 ml de sopa A y 25 ml de sopa B.
  • Proporcione 50 ml de sopa A y 50 ml de sopa B.
  • Proporcione 25 ml de sopa A y 75 ml de sopa B.

Después de que le damos sopa a alguien, la sopa se acaba. En cada ronda elegiremos 0.25entre cuatro operaciones con la misma probabilidad. Si no queda suficiente sopa para completar una determinada operación, asignaremos la mayor cantidad posible. Pare cuando se dispensen ambos tipos de sopa.

Nota: No hay operación para asignar 100 ml de sopa B primero, 0 ≤ n ≤ 1 0 9 ​ ​ ​ ​ ​ 0 ≤ n ≤ 10^90norte1 09 ​​​​​​​。

El valor que se devolverá: la probabilidad de que la sopa A se termine primero + la probabilidad de que la sopa A y la sopa B se terminen al mismo tiempo / 2. El valor de retorno está en la respuesta correcta 1 0 − 5 10^{-5}1 05 se considerarán correctos.


Análisis simple (extraído de LeetCode@ylb boss):

Dado que cada asignación es un múltiplo entero de 25, es recomendable reducir el tamaño de los datos (dividir por 25 y redondear), luego el esquema de asignación se convierte en:

  • Proporciona 4 unidades de A, 0 unidades de B
  • Proporciona 3 unidades de A, 1 unidad de B
  • Proporciona 2 unidades de A, 2 unidades de B
  • Proporciona 1 unidad A, 3 unidades B

Usamos dfs(i,j)"la probabilidad de distribución que queremos" para ila unidad A y jla unidad B. Preste atención a la comprensión de este término. Lo que queremos es la probabilidad de que "A se asigne" + la probabilidad de que "A y B se asignen al mismo tiempo". tiempo". / 2; dfs(i,j)hay cuatro esquemas de asignación para ello, cada uno representando0.25

  • dfs(i-4,j)
  • dfs(i-3,j-1)
  • dfs(i-2,j-2)
  • dfs(i-1,j-3)

En otras palabras, dfs(i,j)=0.25 * (dfs(i-4,j), dfs(i-3,j-1), dfs(i-2,j-2), dfs(i-1,j-3)). Obviamente, esta es una iteración de arriba hacia abajo, y la condición de terminación de la iteración es:

  • i<=0 && j>0: Devuelve 1, solo se asigna la probabilidad de "A"
  • i<=0 && j<=0: Devuelve 0.5, la probabilidad de que "A y B se asignen al mismo tiempo" / 2
  • i>0 && j<=0: Devuelve 0, solo la probabilidad de "B está asignado" no es lo que necesitamos, por lo que es 0

Después del análisis anterior, ¡la respuesta está lista para salir!


Algunas optimizaciones:

  • Porque 0 ≤ n ≤ 1 0 9 ​ ​ ​ ​ ​ ​ 0 ≤ n ≤ 10^90norte1 09 ​​​​​​​​, pero después de la operación real podemos encontrar que: cuando encontramos n=4800n=4800, el resultado es0.999994994426 0.9999949944260 . 9 9 9 9 9 4 9 9 4 4 2 6 , y la precisión requerida por el título es 1 0 − 5 10^{-5}1 05 , y connnA medida que n aumenta, el resultado se acerca a1 11 , entonces cuandon > 4800 n \gt 4800norte>Cuando 4 8 0 0 , devuelve directamente1 11 es suficiente.

Cap.I Pitón

Para Python 3.9 y versiones posteriores, puede usar functoolslas funciones de orden superior proporcionadas en el paquete cache, que son cachés de funciones simples, livianas y de longitud ilimitada, que a veces se denominan "memoizar".

La sintaxis es @functools.cache(user_function), un contenedor simple que crea un diccionario que busca argumentos de función. Debido a que no necesita desalojar los valores antiguos, el tamaño de la memoria caché es ilimitado, por lo que es lru_cache()más pequeño y rápido que con un límite de tamaño. Este @cachedecorador es Python 3.9nuevo en la versión, el código para la pregunta anterior es:

from functools import cache

class Solution:
    def soupServings(self, n: int) -> float:
        @cache
        def dfs(i: int, j: int):
            if i<=0 and j<=0:
                return 0.5
            if i<=0:
                return 1
            if j<=0:
                return 0
            return 0.25*(dfs(i-4,j)+dfs(i-3,j-1)+dfs(i-2,j-2)+dfs(i-1,j-3))
        return 1 if n>4800 else dfs((n+24)//25,(n+24)//25)

Para Python 3.9las siguientes versiones, se puede utilizar @lru_cache(MaxSize), el código es el siguiente:

class Solution:
    def soupServings(self, n: int) -> float:
        MaxSize=100000
        @lru_cache(MaxSize)
        def dfs(i: int, j: int):
            if i<=0 and j<=0:
                return 0.5
            if i<=0:
                return 1
            if j<=0:
                return 0
            return 0.25*(dfs(i-4,j)+dfs(i-3,j-1)+dfs(i-2,j-2)+dfs(i-1,j-3))
        return 1 if n>4800 else dfs((n+24)//25,(n+24)//25)

Cap.II C++

@cacheDecoradores similares en C ++ , por lo que solo puede definir una matriz para almacenar los valores de la función con resultados.La respuesta a esta pregunta es la siguiente:

class Solution {
    
    
public:
    double soupServings(int n) {
    
    
        double f[200][200]={
    
    0.0};
        function<double(int,int)> dfs=[&](int i,int j) -> double {
    
    
            if(i<=0 && j<=0) return 0.5;
            if(i<=0) return 1;
            if(j<=0) return 0;
            if(f[i][j]>0) return f[i][j];
            double ans=0.25*(dfs(i-4,j)+dfs(i-3,j-1)+dfs(i-2,j-2)+dfs(i-1,j-3));
            f[i][j]=ans;
            return ans;
        };
        return n>4800?1:dfs((n+24)/25,(n+24)/25);
    }
};

Supongo que te gusta

Origin blog.csdn.net/Gou_Hailong/article/details/127959703
Recomendado
Clasificación