Title: Divide y Vencers
1Divide y Vencerás
- Curso 2003/2004
- Daniel García
- José Moya
- José A. Gallud
21. El método de diseño Divide y Vencerás
- Técnica para diseñar algoritmos
- Descomponer el problema en cierto número de
subproblemas más pequeños - Resolver sucesiva e independientemente todos los
subproblemas - Combinar las soluciones
- Esquema general ?
funcion DV(x) si x es suficientemente
pequeño resolver ad_hoc(x) descomponer x en
casos más pequeños x1, x2, ..., xm para i1
hasta m yi DV(xi) recombinar los yi para
obtener una solución de x devolver y
3- El número de subejemplares, m, suele ser pequeño
e independiente del caso concreto a resolver - Si m1 no tiene sentido descomponer el caso.
Puede tener sentido reducir la resolución. - Criterios para utilizar DV
- La decisión de utilizar el subalgoritmo básico en
lugar de hacer llamadas recursivas debe tomarse
con cuidado - Debe ser posible descomponer el caso en subcasos
y recomponer las soluciones - Los subcasos han de ser del mismo tamaño
- En la mayoría de algoritmos DV el tamaño de los
m subcasos es n/b donde n es el tamaño del
problema y b es una cte
4- Análisis de los tiempos de ejecución de DV
- g(n) es el tiempo de DV para casos de tamaño n
- t(n) lt(n/b) g(n) tiempo para DV con n
grande - Si ? k ? enteros y g(n) ? ?(nk)
-
- T(n) ?
- La cuestión del umbral
?(nk) si lltbk ?(nk log n) si lbk
?(nlogbl) si lgtbk
5- Análisis del umbral
- La decisión de dividir el caso y hacer llamadas
recursivas o invocar el subalgoritmo básico
dependerá del umbral n0 - El subalgoritmo se emplea para resolver casos de
tamaño inferior a n0 - Esta decisión no afecta al orden del tiempo de
ejecución del algoritmo, aunque nos interesa que
la cte multiplicativa de la notación ? sea la más
pequeña posible. - El umbral óptimo se puede estimar hallando el
tamaño n del caso para el cual no hay diferencia
entre la llamada recursiva y el algoritmo básico
6- Ejemplo multiplicación de enteros grandes
- T(n)
-
- donde h(n) ? ?(n2) y g(n) ? ?(n)
- Supongamos una implementación en la que h(n)n2 y
g(n)16n (microsegundos) - n5000 ?41 (recursivo con n01)
- ?25 algoritmo clásico
- Hasta n32789 (nº cifras de los números a
multiplicar) el algoritmo clásico es mejor que el
recursivo - Se puede lograr hacerlo en 6 escogiendo bien n0
(64) - Y 7 niveles de recursión
h(n) si n ? n0 3t(?n/2?) g(n) en caso
contrario
7- Determinar el umbral óptimo
- Empíricamente obteniendo tiempos
- No es posible determinarlo teóricamente porque
varía de una implementación a otra - Enfoque híbrido
- Determinar teóricamente la forma de las
ecuaciones de recurrencia - Buscar valores de las ctes para la implementación
concreta - Hallar el tamaño del caso para el cual no hay
diferencia entre el algoritmo básico y aplicar la
recursión - Ejemplo anterior
- t(n)16n y ta(n)n2 ? ta(n)tc(n)
- n23/4n2 16n ? nn064 ?
nlt64 básico ngt64 DV
8- Algoritmos DV para la búsqueda binaria
- Uno de los problemas más antiguos localizar un
nombre en un diccionario, en una agenda de
teléfonos. - Es la aplicación más sencilla de DV
- Es una aplicación de reducción o simplificación
- Sea T1..n una matriz ordenada por orden no
decreciente - Ti ? Tj siempre que 1 ? i ? j ? n
- Problema buscar x en la matriz T
- Localizar i 1 ? i ? n1 y Ti-1 lt x ? Ti
- Aproximación directa
- funcion secuencial(T1..n,x)
- para i1 hasta n hacer
- si Ti ? x entonces devolver i
- devolver n1
- ?(r) siendo r el índice que se devuelve
9- Segundo algoritmo
- funcion busquedabin(T1..n,x)
- si n0 or xgtTn or xltT1 entonces devolver
n1 - sino devolver binrec(T1..n,x)
- fin busquedabin
- funcion binrec(Ti..j,x)
- si ij entonces
- si Tix devolver i
- sino devolver n1
- k(ij)/2
- si xTk entonces devolver k
- si x lt Tk entonces devolver binrec(Ti..k,x)
- sino devolver binrec(Tk1..j,x)
- fin binrec
- t(n)t(n/2) g(n) como l1, b2 y k0 ? t(n)
??(log n)
10- Tercer algoritmo (versión iterativa)
- funcion biniter(T1..n,x)
- si xgtTn entonces devolver n1
- i1
- jn
- mientras iltj hacer
- k(ij)2
- si xTk entonces devolver k
- si x lt Tk entonces jk
- sino ik1
- fin_mientras
- devolver n1
- fin biniter
- t(n) ??(log n)
11- Algoritmos DV para la ordenación
- Ordenar ascendentemente T1..n
- Métodos Selección e Inserción ? O(n2)
- Ordenación por mezcla MergeSort
- Descomponer la matriz en dos partes cuyos tamaños
sean tan parecidos como sea posible - Ordenar cada parte mediante llamadas recursivas
- Fusionar las soluciones de cada parte,
ordenándolas - Algoritmo para fusionar dos matrices ordenadas
- Procedimiento mezclar(U1..m1,V1..n1,T1..mn
) - //Um1 y Vn1 contienen 1maximo de cada
matriz - //el resultado se almacena en T
- i1 j1 Um1? Vn1?
- Para k1 hasta mn hacer
- si Ui lt Vi entonces TkUi ii1
- sino TkVj jj1
12- Subalgoritmo básico ordenación por inserción
- procedimiento ordenarpormezcla(T1..n)
- si n es suficientemente pequeño entonces
- insercion(t)
- sino
- U1..?n/2?T1..?n/2?
- V1..?n/2?T?n/2?1..n
- ordenarpormezcla(U1..?n/2?)
- ordenarpormezcla(V1..?n/2?)
- mezclar(U,V,T)
- Como insercion(T)?O(n2), el umbral debe ser muy
bajo (1) - Pasos
- La matriz se divide en dos subcasos de tamaño
mitad - Se resuelve cada caso recursivamente
- Combinar las 2 matrices ordenadas para formar la
solución final - t(n) tseparacion 2t(n/2) tfusion 2t(n/2)
g(n) - O(n) O(n) ?(n)
- t(n) 2t(n/2) g(n) ? (l2, b2, k1) ? ?(nlg n)
- Para el caso en que n sea par
13- Destaca la importancia de escoger subcasos de
igual tamaño - Variante incorrecta del algoritmo
- procedimiento ordenarmezclamal(T1..n)
- si n es suficientemente pequeño entonces
- insercion(t)
- sino
- U1..n-1T1..n-1
- V1Tn
- ordenarmezclamal(U1..n-1)
- ordenarmezclamal(V1..1)
- mezclar(U,V,T)
- t(n) t(n-1) t(1) g(n) ? t(n) t(n-1)
1n(n1) - O(1) ?(n)
- t(n) c11n c21nn 1nn2 ? ?(n2)
- No equilibrar los tamaños puede hacer DV
totalmente ineficaz
14- QuickSort
- Proceso
- Se elige un elemento x del array (pivote)
- Se intercambian pares de elementos (a,b) tal que
- agtx y b?x
- de forma que los elementos ?x queden a la
izquierda de x y los gtx queden a la derecha - Se aplica recursivamente a ambas partes,
separadas por x hasta ordenar el array
15- Procedure Quicksort(var aarray1..n of
tipobase) - procedure Part(iz,detipoindice)
- var i,jtipoindice
- x,ytipobase
- begin
- iizjde xai
- repeat
- while ai lt x do ii1
- while aj gt x do jj-1
- if i ltj then
- begin
- yai aiaj ajy
- ii1 jj-1
- end
- until igtj
- if (iz lt j) then Part(iz,j)
- if (i lt de) then Part(i,de)
- end
- Begin
16- Ejemplo
- Cálculo de la complejidad
- Parte no recursiva es O(n)
- Llamadas recursivas (depende de los valores de
i,j) - Si se parte por k (1?k ? n-1)
- Part(iz,j) ? T(k)
- Part(i,de) ? T(n-k)
- En un caso concreto Tk(n)
- Considerando todos los casos igualmente probables
-
- T(n)
2 1 4 1 5 9 3 6 5
Pivote 3
3 1 4 1 5 9 2 6 5
i j j j
i i j j j
2 1 1 4 5 9 3 6 5
2 1 1
4 5 9 3 6 5
ji ij
Part(iz,j) Part(i,de)
1 n1 nTk(k)Tk(n-k) ngt1
1 n1 n1/(n-1) ? T(k)T(n-k) ngt1
17- (...)
- Mejor caso pivote mediana
- La matriz se parte por la mitad
- T(n)n 2T(n/2) ? O(n lgn)
- La mayoría de casos tienen este comportamiento
- Peor caso O(n2)
- Cálculo de la complejidad
- Peor caso
- T ordenada (desequilibrio, llamada recursiva con
1 y n-1 elementos) - t(n)n t(1) t(n-1) ? ?(n2)
- Mejor caso
- Pivote mediana, la matriz se divide en dos
mitades - T(n)n 2t(n/2) ? ?(n lgn)
- Caso medio
- T tiene un orden aleatorio
18- Multiplicación de enteros muy grandes
- Método clásico O(mn)
- Multiplicar un entero de m cifras por uno de n ?
O(n2) - Método DV
- Se suponen dos enteros de n cifras (se ajusta con
0) - Ejemplo 981 x 1234
- Se divide cada operando en dos partes
- w09 y12
- x81 z34
- 981102w x 1234102y z
- 9811234(102w x)(102y z)104wy 102(wzxy)
xz - Se necesitan 4 multiplicaciones de enteros de
tamaño/2 - Calculando la función de complejidad T(n)
- n desplazamientos y otras operaciones
- (...) ? O(n2) que no mejora el algoritmo clásico
19- Se mejora la complejidad realizando sólo 3
multiplicaciones - 104wy 102(wzxy) xz
- llamamos r (wx)(yz) wy wz xy xz
- pwy qxz
- Con una multiplicación obtenemos la suma de los 4
términos necesarios para el producto - En el ejemplo
- p wy 0912108
- q xz 8134 2754
- r (wx)(yz) 90 46 4140
- 9811234 104p 102(r p q) q
- Se ha obtenido la multiplicación con
- 3 multiplicaciones de números de 3 cifras
- Un cierto número de desplazamientos (2
multiplicaciones por potencias de 10) - Un mayor número de sumas y restas ? es mucho
menos costoso que realizar una multiplicación mas
20- Cálculo de la complejidad
- h(n)cn2 tiempo del algoritmo clásico
- g(n) tiempo de DV sin contar el tiempo de los 3
productos - g(n) ??(n)
- Si las tres multiplicaciones de tamaño mitad se
calculan con el algoritmo clásico - 3h(n/2) g(n) 3cn2/22 g(n) ¾ cn2 g(n)
¾ h(n) g(n) - mejora un 25 respecto a h(n), pero sigue
siendo O(n2) - Si los tres subcasos de DV vuelven a calcularse
con DV de forma recursiva (siempre que los
tamaños sean grandes) - t(n) 3t(n/2) g(n)
- l3, b2, k1 ? ?(nlg3) ?(n1,585 / n sea
potencia de 2)
21- Multiplicación de matrices
- A,B matrices nxn AxBC
- Cij ?Aik Bkj de modo que cada cij ?(n)
- Algoritmo clásico nn elementos, cada uno con
?(n) ? ?(n3) - Matrices 2x2
-
- A B
- Algoritmo clásico 8 multiplicaciones y 4 sumas
- c11 a11b11 a12b21
- c12 a11b12 a12b22 T(n)
- c21 a21b11 a22b21
- c22 a21b12 a22b22
- Se puede conseguir hacerlo en 7 multiplicaciones
y 18 sumas y se aplica a matrices de nxn - t(n) tiempo para multiplicar 2 matrices nxn
- g(n) tiempo para sumar y restar matrices ??(n2)
- t(n) 7t(n/2) g(n) 7t(n/2) n2 ? t(n)
??(nlg7)
b11 b12 b21 b22
a11 a12 a21 a22
b nlt2 8T(n/2) n2 ? l8, b2,
k2 ? ?(n3)
22- Exponenciación
- a,n enteros xan
- Con n pequeño se emplea el algoritmo clásico
- funcion exposec(a,n)
- r a
- para i1 hasta n-1 hacer
- r ar
- devolver r
- ?(n) siempre y cuando las multiplicaciones sean
elementales - Problema de desbordamiento de enteros, incluso
con a y n pequeño - Mejora de exposec ? an (a n/2)2
- a n/2 se puede calcular 4 veces más rápido que
an - an
a si n1 (a n/2)2 si n es
par aan-1 si n es impar
23- Ejemplo
- a29 aa28 a(a14)2a((a7)2)2
a((a(aa2)2)2)2 - necesita 3 multiplicaciones y 4 elevado a 2
- antes 28 multiplicaciones
- Algoritmo DV
- Funcion expoDV(a,n)
- si n1 devolver a
- si n es par devolver (expoDV(a,n/2))2
- devolver aexpoDV(a,n-1)
- Para obtener la complejidad, suponemos n potencia
de 2 y la expresión ... no se realiza nunca - T(n)
- T(n) t(n/2) 1 ? l1, b2, k0 ? t(n) ??(lgn)
- si n1
- T(n/2) 1 en otro caso
24- Versión iterativa
- funcion expoiter(a,n)
- in r1 xa
- mientras i gt 0 hacer
- si i es impar
- r rx
- x x2
- i i/2
- devolver r
- T(n) ??(lg n)