Estoy atrapado analizando la complejidad temporal del siguiente algoritmo:
def fun (r, k, d, p):
if d > p:
return r
if d = 0 and p = 0:
r <- r + k
return r
if d > 0:
fun (r, k + 1, d - 1, p)
if p > 0:
fun (r, k - 1, d, p - 1)
La llamada raíz será fun (0, 0, n, n)
, y n
es el tamaño del problema.
Supongo que: la relación de recurrencia es , que es equivalente a , y entonces .
¿Es correcto mi análisis (sé que no es muy completo y exacto)? Si tiene un defecto grave, indíquelo o muéstreme una prueba correcta y completa de la complejidad temporal de este algoritmo.
d>0
yp>0
. No muestra lo que devuelve la función si alcanzamos las declaraciones if 3ra y 4ta. ¿Querías tener unareturn
declaración después de cada invocación recursiva defun
? (¿queríasfun (r, k + 1, d - 1, p)
serreturn fun (r, k + 1, d - 1, p)
?) ¿O querías tener unareturn
declaración al final del cuerpo de la función? Edite su pseudocódigo para aclarar y asegúrese de mostrar lo que esto devuelve en todos los casos posibles.d<=p
yd>0
yp>0
todos sostienen. ¿Qué se supone que debe pasar? ¿El algoritmo hace 2 invocaciones recursivas a la función? ¿O invoca recursivamentefun(r, k + 1, d - 1, p)
y luego regresa inmediatamente, sin invocar recursivamentefun(r, k - 1, d, p - 1)
? Si tomo su pseudocódigo literalmente, parece que hace 2 invocaciones recursivas y luego regresa con un valor de retorno indefinido, pero eso parece extraño y me hace preguntarme si hay un error tipográfico / error en el pseudocódigo.Respuestas:
Los únicos dos argumentos relevantes para el análisis asintótico sond y p . Estos argumentos (virtualmente) satisfacend,p≥0 y d≤p (Necesitamos mezclar ligeramente la lógica en la función para obtener esto). En cada punto de la ejecución, tomas el par actual(d,p) y luego llama recursivamente a la función con los pares (d−1,p),(d,p−1) , evitando pares que invalidan las restricciones indicadas anteriormente.
Podemos imaginar el árbol de llamadas resultante como una ruta que comienza en(0,0) . Cada vez que disminuyesp , agregue un / paso. Cada vez que disminuyesd , agregue un \ step. La condiciónd≤p garantiza que nunca irás por debajo del eje X. Además, tiene un "presupuesto" den de cada paso El número total de hojas en este árbol de llamadas es exactamente el número catalán.(2nn)/(n+1)=Θ(4n/n3/2) , y esto nos da un límite inferior en el tiempo de ejecución de la función.
Para obtener un límite superior, tenga en cuenta que en el camino a cada hoja pasamos por2n nodos, y esto da un límite superior 2n más grande que el límite inferior, es decir, Θ(4n/n−−√) .
Tenemos un límite inferior deΩ(4n/n3/2) y un límite superior en O(4n/n−−√) . ¿Cuáles son las asintóticas exactas? Crecen como el número total de caminos que no cruzan el eje X que tienen como máximon pasos en cada dirección. Usando el teorema de la boleta de Bertrand podemos obtener una expresión exacta para esto:
fuente
Caso por caso:
fun
repite en d-1 hasta d ≯ 0; desde p> 0, esto es lineal en d + (caso 4) .fun
repite en p-1 hasta p p 0; esto es lineal en p + (uno de los casos 1, 2 o 5)Comenzando con d = p = n> 0 coincide con el caso 3, que es seguido por el caso 4. Si n es un número entero, el caso final es 2, de lo contrario, el caso final es 5. El tiempo total para esos casos es d + p +1 o 2n + 1.
fuente
if
como "hacer uno de estos", mientras que @Yuval los tomó como "considere cada uno de estos en orden". Esto último es, por supuesto, lo queif
s (sinelse
) significa en el código real; Estoy acostumbrado a lo primero en casi cualquier otro contexto que no sea el código real (incluso en pseudocódigo, aunque el uso en pseudocódigo es inconsistente).return
declaraciones en la segunda mitad del código hace que esto sea bastante confuso.