Subido por luis alfonso casaperalta

11daaPDivideYVenceras

Anuncio
Facultad de Ingeniería de Sistemas e Informática
Divide y Vencerás
Métodos para el algoritmo de ordenación por mezcla
Se tiene el siguiente conjunto de elementos para ordenar con clasificación por mezcla:
•
1ra.
Pasada
.
14 07 03 12 09 11 06 02
Como estamos usando divide y vencerás para ordenar, tenemos que decidir cómo se van a ver nuestros
subproblemas.
• El problema completo es ordenar todo un arreglo. Digamos que un subproblema es ordenar
un subarreglo.
• En particular, vamos a pensar que un subproblema es ordenar el subarreglo que empieza en
el índice p y va hasta el índice r.
• Será conveniente tener una notación para un subarreglo, así que digamos que array[p..r].
denota este subarreglo de array.
En términos de nuestra notación, para un arreglo de n elementos, podemos decir que el problema
original es ordenar array[0..n-1].
Aquí está cómo el ordenamiento por mezcla utiliza divide y vencerás:
•
•
•
Divide al encontrar el número q de la posición a medio camino entre p y r. Haz este paso de la
misma manera en que encontramos el punto medio en la búsqueda binaria: suma p y r, divide
entre 2 y redondea hacia abajo.
Vence al ordenar de manera recursiva los subarreglos en cada uno de los dos subproblemas
creados por el paso de dividir. Es decir, ordena de manera recursiva el subarreglo array[p..q]
y ordena de manera recursiva el subarreglo array[q+1..r].
Combina al mezclar los dos subarreglos ordenados de regreso en un solo subarreglo ordenado
array[p..r].
Necesitamos un caso base. El caso base es el subarreglo que contiene menos de dos elementos, es
decir, cuando p ≥ r , ya que un subarreglo sin elementos o con solo un elemento ya está ordenado. Así
que vamos a dividir-vencer-combinar solo cuando p < r.
Veamos un ejemplo. Vamos a empezar con array que contiene a [14, 7, 3, 12, 9, 11, 6, 2], de modo
que el primer subarreglo es en realidad el arreglo completo, array[0..7] (p=0 y r=7). Este subarreglo
tiene por lo menos dos elementos, así que no es un caso base.
➢ En el paso de dividir, calculamos q=3.
➢ El paso de vencer nos hace ordenar los dos subarreglos array[0..3], que contiene a [14, 7, 3,
12], y array[4..7], que contiene a [9, 11, 6, 2].
o Cuando regresamos del paso de vencer, cada uno de los dos subarreglos está
ordenado: array[0..3] contiene a [3, 7, 12, 14] y array[4..7] contiene a [2, 6, 9, 11], de
modo que el arreglo completo es [3, 7, 12, 14, 2, 6, 9, 11].
➢ Por último, el paso de combinar mezcla los dos subarreglos en la primera y la segunda mitad,
para producir el arreglo final ordenado [2, 3, 6, 7, 9, 11, 12, 14].
¿Cómo se ordenó el subarreglo array[0..3]? Del mismo modo. Tiene más de dos elementos, así que no
es un caso base.
Lic. Jorge Luis Chávez Soto
1
Facultad de Ingeniería de Sistemas e Informática
➢ Con p=0 y r=, calcula q=1, ordena recursivamente array[0..1] ([14, 7]) y array[2..3] ([3, 12]),
cuyo resultado es array[0..3] que contiene a [7, 14, 3, 12], y
➢ mezcla la primera mitad con la segunda mitad, para producir [3, 7, 12, 14].
¿Cómo se ordenó el subarreglo array[0..1]? Con p=0 p y r=1, calcula q=0, ordena recursivamente
array[0..0] ([14]) y array[1..1] ([7]), cuyo resultado es array[0..1] que sigue conteniendo a [14, 7], y
mezcla la primera mitad con la segunda mitad, para producir [7, 14].
Los subarreglos array[0..0] y array[1..1] son casos base, ya que cada uno contiene menos de dos
elementos. Aquí está cómo se desarrolla todo el algoritmo del ordenamiento por mezcla:
PROCEDURE Mezcla(VAR a,b:vector;prim,ult:CARDINAL);
(* utiliza el vector b como auxiliar para realizar la mezcla *)
VAR mitad:CARDINAL;
BEGIN
IF prim<ult THEN
mitad:=(prim+ult)DIV 2;
Mezcla(a,b,prim,mitad);
Mezcla(a,b,mitad+1,ult);
Combinar(a,b,prim,mitad,mitad+1,ult)
Lic. Jorge Luis Chávez Soto
2
Facultad de Ingeniería de Sistemas e Informática
END
END Mezcla;
PROCEDURE Combinar(VAR a,b:vector;p1,u1,p2,u2:CARDINAL);
(* mezcla ordenadamente los subvectores a[p1..u1] y a[p2..u2] suponiendo que estos estan ya
ordenados y que son consecutivos (p2=u1+1), utilizando el vector auxiliar b *)
VAR i1,i2,k:CARDINAL;
BEGIN
IF (p1>u1) OR (p2>u2) THEN
RETURN END;
FOR k:=p1 TO u2 DO
b[k]:=a[k]
END; (* volcamos a en b *)
i1:=p1;i2:=p2; (* cada indice se encarga de un subvector *)
FOR k:=p1 TO u2 DO
IF b[i1]<=b[i2] THEN
a[k]:=b[i1];
IF i1<u1 THEN
INC(i1)
ELSE
b[i1]:=MAX(INTEGER)
END
ELSE
a[k]:=b[i2];
IF i2<u2 THEN
INC(i2)
ELSE
b[i2]:=MAX(INTEGER)
END
END
END
END Combinar;
LA SUBSECUENCIA DE SUMA MÁXIMA
Dados n enteros cualesquiera a1,a2,...,an, necesitamos encontrar el valor de la expresión:
que calcula el máximo de las sumas parciales de elementos consecutivos. Como ejemplo, dados los 6
enteros (–2,11,–4,13,–5,–2) la solución al problema es 20 (suma de a2 hasta a4).
Deseamos implementar un algoritmo Divide y Vencerás de complejidad nlogn que resuelva el
problema. ¿Existe algún otro algoritmo que lo resuelva en menor tiempo?
Solución
Lic. Jorge Luis Chávez Soto
3
Facultad de Ingeniería de Sistemas e Informática
Existe una solución trivial a este problema, basada en calcular todas las posibles sumas y escoger la
de valor máximo (esto es, mediante un algoritmo de “fuerza bruta”) cuyo orden de complejidad es
O(n3). Esto lo hace bastante ineficiente para valores grandes de n:
PROCEDURE Sumamax(VAR a:vector;prim,ult:CARDINAL):CARDINAL;
VAR izq,der,i:CARDINAL; max_aux,suma:INTEGER;
BEGIN
max_aux:=0;
FOR izq:=prim TO ult DO
FOR der:=izq TO ult DO
suma:=0;
FOR i:=izq TO der DO
suma:=suma+a[i]
END;
IF suma>max_aux THEN
max_aux:=suma
END
END
END;
RETURN max_aux
END Sumamax;
Una mejora inmediata para el algoritmo es la de evitar calcular la suma para cada posible subsecuencia,
aprovechando el valor ya calculado de la suma de los valores de a[izq..der–1] para calcular la suma de
los valores de a[izq..der]. Esto da lugar a la siguiente función
PROCEDURE Sumamax2(VAR a:vector;prim,ult:CARDINAL):CARDINAL;
VAR izq,der:CARDINAL; max_aux,suma:INTEGER;
BEGIN
max_aux:=0;
FOR izq:=prim TO ult DO
suma:=0;
FOR der:=izq TO ult DO
suma:=suma+a[der];
(* suma contiene la suma de a[izq..der] *)
IF suma>max_aux THEN
max_aux:=suma
END
END
END;
RETURN max_aux
END Sumamax2;
cuya complejidad es de orden O(n2), lo cual mejora sustancialmente al anterior, pero que aún no consigue la
complejidad pedida.
Utilizaremos ahora la técnica de Divide y Vencerás para intentar mejorar la eficiencia de los algoritmos
anteriores, y lo haremos dividiremos el problema en tres subproblemas más pequeños, sobre cuyas soluciones
construiremos la solución total.
En este caso la subsecuencia de suma máxima puede encontrarse en uno de tres lugares. O está en la primera
mitad del vector, o en la segunda, o bien contiene al punto medio del vector y se encuentra en ambas mitades.
Lic. Jorge Luis Chávez Soto
4
Facultad de Ingeniería de Sistemas e Informática
Las tres soluciones se combinan mediante el cálculo de su máximo para obtener la suma pedida.
Los dos primeros casos pueden resolverse recursivamente. Respecto al tercero, podemos calcular la
subsecuencia de suma máxima de la primera mitad que contenga al último elemento de esa primera mitad, y la
subsecuencia de suma máxima de la segunda mitad que contenga al primer elemento de esa segunda mitad.
Estas dos secuencias pueden concatenarse para construir la subsecuencia de suma máxima que contiene al
elemento central de vector. Esto da lugar al siguiente algoritmo:
PROCEDURE Sumamax3(VAR a:vector;prim,ult:CARDINAL):CARDINAL;
VAR mitad,i:CARDINAL;
max_izq,max_der,suma,max_aux:INTEGER;
BEGIN
(* casos base *)
IF prim>ult THEN
RETURN 0
END;
IF prim=ult THEN
RETURN Max2(0,a[prim])
END;
mitad:=(prim+ult)DIV 2;
(* casos 1 y 2 *)
max_aux:=Max2(Sumamax3(a,prim,mitad),Sumamax3(a,mitad+1,ult));
(* caso 3: parte izquierda *)
max_izq:=0;
suma:=0;
FOR i:=mitad TO prim BY -1 DO
suma:=suma+a[i];
max_izq:=Max2(max_izq,suma)
END;
(* caso 3: parte derecha *)
max_der:=0;
suma:=0;
FOR i:=mitad+1 TO ult DO
suma:=suma+a[i];
max_der:=Max2(max_der,suma)
END;
(* combinacion de resultados *)
RETURN Max2(max_der+max_izq,max_aux)
END Sumamax3;
donde la función Max2 utilizada es la que calcula el máximo de dos números enteros.
El procedimiento Sumamax3 es de complejidad O(nlogn), puesto que su tiempo de ejecución T(n)
viene dado por la ecuación en recurrencia
T(n) = 2T(n/2) + An
con la condición inicial T(1) = 7, siendo A una constante.
Obsérvese además que este análisis es válido puesto que hemos añadido la palabra VAR al vector a
en la definición del procedimiento. Si no, se producirían copias de a en las invocaciones recursivas, lo
que incrementaría el tiempo de ejecución del procedimiento.
Lic. Jorge Luis Chávez Soto
5
Facultad de Ingeniería de Sistemas e Informática
Respecto a la última parte del problema, necesitamos encontrar un algoritmo aún mejor que éste. La
clave va a consistir en una modificación a la idea básica del algoritmo anterior, basada en un algoritmo
del tipo “en línea”
Supongamos que ya tenemos la solución del problema para el subvector a[prim..i–1]. ¿Cómo podemos
extender esa solución para encontrar la solución de a[prim..i]? De forma análoga al razonamiento que
hicimos para el algoritmo anterior, la subsecuencia de suma máxima de a[prim..i] puede encontrarse
en a[prim..i–1], o bien contener al elemento a[i].
Esto da lugar a la siguiente función:
PROCEDURE Sumamax4(VAR a:vector;prim,ult:CARDINAL):CARDINAL;
VAR i:CARDINAL;
suma,max_anterior,max_aux:INTEGER;
BEGIN
max_anterior:=0;
max_aux:=0;
FOR i:=prim TO ult DO
max_aux:=Max2(max_aux+a[i],0);
max_anterior:=Max2(max_anterior,max_aux)
END;
RETURN max_anterior;
END Sumamax4.
Ejemplo:
-2 11 -4 13 -5 2
La suma de a2 hasta a4 es la subsecuencia máxima a 20.
Lic. Jorge Luis Chávez Soto
6
Descargar