¿Se utiliza una estructura de datos de pila para algoritmos multirecursionales?

Una pila es una generalización y hay muchas áreas de memoria en una computadora en funcionamiento que podrían llamarse “pilas”. Siempre y cuando los datos se lean en el orden opuesto al que se colocan, llamamos un área de memoria una “pila”.

Cuando hablamos de recursividad, estamos hablando de la “pila de ejecución” para un hilo específico en un proceso. El programador no crea esto, ya que el compilador crea el código para tratarlo. En tiempo de ejecución, cada subproceso de ejecución tiene una pila para que el código pueda realizar un seguimiento de dónde estaba antes y pueda restaurar ese estado anterior cuando se realiza con el actual. Mire este código, por ejemplo, prestando atención a los comentarios a la derecha.

función c (int z) {// 7. establezca z en 9, como se pasó en
devuelve z + 7; // 8. la pila de abajo se muestra en este punto
}
función b (int y) {// 4. establece y en 3, como se pasó en
int w = 6; // 5. establece w en 6
retorno c (y + w); // 6. guardar la ubicación de devolución, llamar a c (9)
}
función a () {// 1. comienza aquí
int x = 5; // 2. pon un 5 en la pila
imprimir (b (3) + 5); // 3. guardar la ubicación de devolución en la pila, llamar a b (3)
}

Aquí hemos llamado a la función a (), que llama a b (), que luego llama a c (), por lo que la pila de ejecución contendría tres cuadros, uno para c (), b () y a (). (Nota: en algunas computadoras la pila de ejecución crece hacia arriba y en otras crece hacia abajo, pero los resultados son los mismos).

7 // parte superior de la pila dentro de c (), se agregará un 7
9 // el valor de z en c (), pasado de b ()
volver a dentro de b // donde en el código debemos volver a
6 // el valor de w en b ()
3 // el valor de y en b ()
volver a dentro de // otro retorno a realizar, en un ()
5 // valor de x, dentro de a ()

La pila de ejecución crece a medida que realizamos llamadas a funciones / métodos anidados o recursivos, como puede ver en el ejemplo anterior.

Sin embargo, la mayoría de los compiladores realizan la Optimización de llamadas de cola. Si esto se compilara con TCO, los dos valores de la pila “return to b” y “6” nunca se pondrían en la pila. ¿Por qué? Debido a que el compilador se daría cuenta de que, después de llamar y devolver c (), no quedaría nada que hacer en b (), y que la variable w ya no se usaría. Entonces, el optimizador del compilador crearía código que nunca pondría esas dos cosas innecesarias en la pila de ejecución en primer lugar. c () volvería directamente dentro de a () y todo seguiría funcionando como se esperaba.

Entonces, si su código está escrito de tal manera que no hay nada más que hacer después de una llamada anidada / recursiva, y si el optimizador del compilador admite TCO, entonces la recursión no hace que la pila crezca sin límites, ni afecta negativamente el rendimiento. Pero solo si”.

Para responder a su pregunta a un nivel más básico: El programador no necesita estar consciente de que se está utilizando una pila cuando escribe funciones recursivas. Pero deben tener en cuenta que el código compilado será más eficiente si la llamada recursiva es lo último en la función, y si no se debe hacer nada más con el valor que devuelve la llamada, aparte de devolverlo intacto.

PD: en caso de que le interese, esta es una buena coyuntura para explicar la gran diferencia en la ejecución entre funciones convencionales y lambdas. Las variables (x, y, z) en los ejemplos anteriores, como puede ver, se guardan en la pila. Si estos se escribieran como lambdas, entonces estos “datos de la pila” se almacenarían en el espacio de datos (el montón) en lugar de la pila.

  • Para lambdas, las variables / parámetros locales se almacenan en una estructura de datos junto con un puntero / referencia al punto de entrada del código de función. La lambda es simplemente un puntero / referencia a esa estructura. Esto significa que la lambda continúa existiendo después de que el código ha regresado.
  • Para funciones / métodos convencionales, las variables / parámetros se almacenan directamente en la pila junto con las direcciones de retorno. Esto significa que las variables / parámetros se sacan de la pila cuando la función regresa.

Se utiliza una pila de retorno para todas las llamadas a funciones, incluidas las llamadas recursivas, para que se conozca la dirección de retorno. También es necesaria una pila si se van a pasar parámetros a la función en cuestión, aunque generalmente es la misma pila.

La idea de una pila (donde empujas cosas y las sacas) es, pero es una pila especial: la pila de llamadas. Normalmente tiene mucho menos espacio disponible que una pila que declaras. Es por eso que el “desbordamiento de la pila” es un tipo diferente de excepción que “sin memoria”, y es probable que lo obtenga con solo decenas de miles de iteraciones.

More Interesting

Cómo escribir un programa para ingresar una cadena e imprimir el número de caracteres en minúscula y mayúscula en la cadena

¿Algunos algoritmos de ML son más vulnerables a los conjuntos de entrenamiento desequilibrados que otros? ¿Por qué?

¿Por qué algunos algoritmos son más eficientes que otros? ¿Por qué se prefiere la búsqueda binaria sobre la lineal?

¿Soy un mal programador si no puedo entender Towers of Hanoi?

¿Cuál es el mejor algoritmo para verificar si un número es primo?

¿Cuáles son algunos algoritmos que usamos diariamente que tienen complejidades [matemáticas] O (n), O (n ^ 2), [/ matemáticas] y [matemáticas] O (\ log n) [/ matemáticas]?

¿Qué sucede cuando hay un ciclo negativo en el gráfico?

¿Qué algoritmos de programación de procesos usa Android?

¿Qué estrategia debo usar para resolver esta pregunta? - HackerEarth | Iniciar sesión (Equipo del proyecto | HackerEarth)

¿Un programador autodidacta necesita aprender materias como algoritmos y cálculo? ¿Por qué?

¿Cuál es la diferencia entre usar <y <= en la búsqueda binaria?

¿Qué esfuerzos hará para crear un gráfico de la estructura de datos básicos, que también puede ser entendido por una persona no técnica?

¿Cuáles son las estructuras de datos y los algoritmos utilizados en la programación competitiva?

Aprendizaje automático: ¿Cuál es la idea general de por qué minimizar la minimización empírica de riesgos es NP-Complete?

Con fuerza bruta, ¿cómo sabemos si encontramos la llave?