Requisitos de espacio de una ordenación por combinación

Resuelto Arkaitz Jimenez asked hace 14 años • 3 respuestas

Estoy tratando de comprender los requisitos de espacio para Mergesort, O (n).
Veo que los requisitos de tiempo son básicamente, cantidad de niveles (logn) * fusión (n), por lo que eso hace (n log n).
Ahora, todavía estamos asignando n por nivel, en 2 matrices diferentes, izquierda y derecha.
Entiendo que la clave aquí es que cuando las funciones recursivas regresan, el espacio se desasigna, pero no lo veo demasiado obvio.
Además, toda la información que encuentro solo indica que el espacio requerido es O(n), pero no lo explica.
¿Alguna pista?

function merge_sort(m)
    if length(m) ≤ 1
        return m
    var list left, right, result
    var integer middle = length(m) / 2
    for each x in m up to middle
         add x to left
    for each x in m after middle
         add x to right
    left = merge_sort(left)
    right = merge_sort(right)
    result = merge(left, right)
    return result

EDITAR Ok, gracias a @Uri, este es el truco.
Lo que no pude ver al principio es que el tiempo solo suma, mientras que la memoria suma y resta, por lo que la cantidad máxima de tiempo es al final de la ejecución, pero la cantidad máxima de memoria está en la parte inferior de la pila recursiva.

Entonces, si seguimos sumando n + n/2 + n/4 + n/8.... no importa cuántas veces sumamos, nunca será mayor que 2n, y cuando lleguemos a la pila recursiva abajo y comenzamos a subir, no mantenemos la memoria utilizada para la rama anterior, por lo que, como máximo, 2n sería la cantidad de memoria utilizada, O(n).

Arkaitz Jimenez avatar Jun 03 '10 22:06 Arkaitz Jimenez
Aceptado

Hay versiones de merge-sort que pueden funcionar en su lugar.

Sin embargo, en la mayoría de las implementaciones el espacio es lineal en el tamaño de la matriz. Eso significa n para el primer nivel, n/2 para el segundo, n/4 para el tercero, etc. Cuando llegue al final de su recursividad, esta serie suma aproximadamente 2n, que es lineal.

Uri avatar Jun 03 '2010 15:06 Uri

Esta es mi explicación de la complejidad espacial de su código. Básicamente, a medida que el algoritmo alcanza resultados, ¿cómo nos va con la memoria?

1) Cada llamada recursiva que realiza tiene un tamaño constante de marco de pila asignado, así como cualquier variable que no sea función de "n". Llamemos a esta constante "c". Dado que vas a niveles lg(n) profundos, el resultado es c*lg(n), que es O(lg(n)).

2) Ahora, mientras calculamos el resultado, asignamos espacio para n/(2^k) elementos, donde k es el nivel en el que se encuentra.

left = merge_sort(left)
right = merge_sort(right)

Para las personas que se pregunten cómo se nos ocurrió n/(2^k), observen que primero asignamos memoria al resolver la primera mitad de la matriz, es decir, left=merge_sort(left). Una vez que finaliza este árbol de recursividad, terminamos desasignando toda la memoria y volvemos al punto de partida antes de resolver el lado derecho. Por lo tanto, es n/(2^k). Esto será O(n).

3) Finalmente, el procedimiento de fusión también puede asignar espacio adicional (si se utiliza una lista vinculada, es posible que este espacio no sea necesario), que es O(n)

Respuesta final = O(lg(n)) + O(n) + O(n) que es O(n).

Frank Q. avatar Apr 19 '2012 00:04 Frank Q.