Se desea calcular el tiempo de ejecucin de la funcin f, cuyo cdigo

Anuncio
PROBLEMA 1 (ANÁLISIS DE ALGORITMOS)
Se desea calcular el tiempo de ejecución de la función f, cuyo código aparece abajo.
func f (ent/sal a: array[1..N] de Entero) dev (s: Entero)
alg
s := g (a, 0, 1)
fin
func g (ent/sal a: array[1..N] de Entero, r: Entero, i: Entero) dev (s: Entero)
alg
si i > N:
s := r
| i ≤ N:
r := r + a[i]
s := g (a, r, i+1)
fsi
fin
(a) ¿Cómo escogería el tamaño del problema n para la función recursiva g?
(b) Determine el tiempo de ejecución Tg(n) de una llamada recursiva a g.
(c) Determine el tiempo de ejecución de ejecución Tf(N) de la función f.
Nota: Usar constantes que engloben el tiempo de ejecución de las operaciones elementales.
Solución:
(a) El tamaño del problema n para la función recursiva g:
n=N–i+1
(b) El tiempo de ejecución Tg(n) de una llamada recursiva a g vendrá dado por:
k1
⎧
Tg (n) = ⎨
⎩Tg (n − 1) + k2
,n = 0
,n > 0
Ecuación característica: ( x − 1) 2 = 0
⇒ Tg ( n) = c1n + c2 ;
Condiciones iniciales: Tg (0) = k1 = c2 , Tg (1) = Tg (0) + k2 = k1 + k2 = c1 + c2
⇒ c1 = k2 , c2 = k1 ⇒ Tg ( n) = k2 n + k1
(c) El tiempo de ejecución de ejecución Tf(N) de la función f será:
T f ( N ) = Tg ( N − 1 + 1) + k3 = Tg ( N ) + k3 = k2 N + k1 + k3
PROBLEMA 2 (ANÁLISIS DE ALGORITMOS)
Dado el siguiente algoritmo:
proc procesa(ent/sal a: array[1..k] de Entero, prim: Entero, ult: Entero)
var
i, j, aux: Entero
alg
si prim < ult
(1)
i := prim
(2)
j := ult
(3)
mientras i < j
(4)
si a[i] != a[j]
(5)
aux := a[i]
(6)
a[i] := a[j] * 4 + 3
(7)
a[j] := aux * 4 – 3
(8)
fsi
i := i + 1
(9)
j := j – 1
(10)
fmientras
procesa(a, prim + 1, ult - 1)
(11)
fsi
fin
(a) ¿Cómo escogería el tamaño de problema n para el proceso recursivo procesa?
(b) Determine el caso mejor y el tiempo de ejecución T(n) para el proceso procesa en ese caso.
(c) Determine el caso peor y el tiempo de ejecución T(n) para el proceso procesa en ese caso.
Notas:
• Utilizar las constantes que se crean necesarias para englobar los costes de las operaciones
(indicando qué operaciones engloban).
• Suponer que la llamada inicial es procesa(a, 1, k)
Solución:
a) n = ult – prim + 1
b) y c) En cada llamada recursiva el tamaño del problema disminuye en dos unidades por el
decremento de ult y el incremento de prim. El bucle (líneas (4) a (10)) se ejecuta n/2
(división entera) veces. Si nunca se cumple la condición de la línea (5) estaremos en el caso
mejor, que corresponde a un array donde los elementos están dispuestos de forma simétrica.
La diferencia entre el caso mejor y peor corresponde a las instrucciones (6), (7) y (8). Por
tanto, la diferencia de coste entre un caso y otro, será una constante que llamaremos k5.
T (n) =
{
k1
T (n − 2) + ⎣⎢ n / 2 ⎦⎥ k2 + k3
n ≤ 1 ( prim ≥ ult )
n > 1 ( prim < ult )
Para el caso mejor, k2 = k4, mientras que para el caso peor, k2 = k4 + k5, donde:
k1 : Engloba las operaciones del caso base (evaluación de (1))
k2 : Coste de cada iteración del bucle mientras, instrucciones de la (4) a la (10)
k3 : Engloba las operaciones (1), (2), (3), evaluación de la condición del bucle (4) al salir, y
cálculos de parámetros de la llamada recursiva (11)
k4 : Engloba la evaluación de la condición (4) y (5), y las instrucciones (9) y (10)
k5 : Engloba las operaciones (6), (7) y (8)
Lo resolveremos suponiendo n par. Para n impar se resolvería de forma análoga, simplemente
cambiando la constante k3. Del caso recursivo podemos deducir:
T(n) – T(n – 2) =
n
k2 + k3
2
La ecuación característica resultante es ( x 2 − 1)( x − 1) 2 = 0 (ec. no homogénea con b = 1, d =
1), con soluciones r1 = 1 repetida 3 veces y r2 = -1 repetida 1 vez. Por tanto:
T ( n) = c1 ( −1) n + c2 (1) n + c3 n(1) n + c4 n 2 (1) n = c1 (−1) n + c2 + c3 n + c4 n 2
Será de orden cuadrático (Θ(n )) si la constante c4 es diferente a 0. Usando T(0), T(2), T(4) y
T(6), se deduce que: c1 = k1, c2 = 0, c3 = (k2 + k3) / 2, c4 = (1/4) k2. Si n fuese impar
usaríamos T(1), T(3), T(5) y T(7).
2
PROBLEMA 3 (ALGORITMOS VORACES)
Se dispone de N vasijas para transportar aceite. La tabla capac de N enteros indica sus
diferentes capacidades (∀i:1..N | capac[i] > 0) y se encuentra ordenada ascendentemente. En
otro array aceite de N enteros se indican los litros de aceite que hay en cada una de ellas
(∀i:1..N | 0 ≤ aceite[i] ≤ capac[i]).
Diseñar un algoritmo voraz que obtenga el máximo número de vasijas llenas mediante el
trasvase de unas a otras. La solución sol al problema debe ser un array NxN de enteros tal que
cada elemento sol[i, j] sea el número de litros que se trasvasan de la vasija i a la vasija j.
Problema
capac
aceite
5
2
10
5
15
10
25
15
50
4
1
2
3
4
5
1
0
0
0
0
3
Solución
2
3
0
0
0
0
0
0
4
5
1
0
4
0
0
0
0
0
5
0
0
0
0
0
Para este ejemplo, una posible solución sería la presentada, donde se trasvasan 3 litros de la
vasija 5 a la 1, 1 litro de la vasija 5 a la 2, 4 litros de la vasija 4 a la 2 y 5 litros de la vasija 4 a
la 3.
Solución:
proc voraz()
alg
inicializa()
mientras No fin()
seleccionaYElimina ()
si prometedor()
anotaEnSolucion()
fsi
fmientras
fin
Clase VasijasAceite hereda EsquemaVZ
capac: Array [1..N] de Entero
aceite: Array [1..N] de Entero
pri, ult: Entero
solucion: Array [1..N,1..N] de Entero
proc inicializa()
alg
pri := 1
ult := N
capac := <inicializa con la capacidad de las vasijas>
aceite := <inicializa con las cantidades almacenadas por las vasijas>
solucion := <inicializa con ceros>
fin
func fin() dev (b:Logico)
b := (pri>=ult)
fin
proc seleccionaYElimina()
alg
si aceite[ult]=0:
ult := ult – 1
fsi
si capac[pri] = aceite[pri]:
pri := pri + 1
fsi
fin
func prometedor() dev (b:Logico)
alg
b := (capac[pri] > aceite[pri] Y aceite[ult] > 0 Y pri < ult)
fin
proc anotaEnSolución()
alg
si aceite[ult] > capac[pri] – aceite[pri]:
solucion[pri, ult] := capac[pri] – aceite[pri]
otras:
solucion[pri, ult] := aceite[ult]
fsi
aceite[pri] := aceite[pri] + solucion[pri, ult]
aceite[ult] := aceite[ult] – solucion[pri, ult]
fin
fClase
PROBLEMA 4 (ALGORITMOS VORACES)
Diseñar un algoritmo voraz que maximice el valor de una mochila de peso máximo pMax, en
la que se pueden introducir N tipos de objetos que vienen representados por objetos: array
[1..N] de Registro (no ordenado), cuyos campos son peso, valor y cantidad, que
indican respectivamente el peso del objeto, su valor económico y el número de ejemplares
que hay de cada uno.
Solución:
proc voraz()
alg
inicializa()
mientras No fin()
seleccionaYElimina ()
si prometedor()
anotaEnSolucion()
fsi
fmientras
fin
Atributos de la Clase Mochila:
k, val, pRestante: Entero
objetos: Array [1..N] de Registro
sol: Array[1..N] de Entero
proc inicializa()
alg
quicksort(objetos) // ordena decrecientemente por la relación valor/peso
sol := <array de ceros>
< val, k > := < 0, 0 >
pRestante := pMax
fin
func fin() dev (b: Logico)
alg
b:=(k=N O pRestante=0)
fin
proc seleccionaYElimina ()
alg
k:=k+1
fin
func prometedor() dev (b: Logico)
alg
b := cierto
fin
proc anotaEnSolucion()
alg
sol[x] := minimo(objetos[x].cantidad, ⎣pRestante/objetos[x].peso⎦ )
si sol[x] > 0:
pRestante := pRestante – sol[x]*objetos[x].peso
val := val + sol[x]*objetos[x].valor
fsi
fin
PROBLEMA 5 (DIVIDE Y VENCERÁS)
Sea a: array[1..N] de Elemento, no ordenado, se desea obtener los elementos mínimo y
máximo de a. Para ello se dispone de la función compara(x, y: Elemento), que devuelve un
valor negativo si x < y, positivo si x > y o cero si x = y. Diseñar un algoritmo de Divide y
Vencerás para obtener los resultados deseados. El algoritmo debe minimizar el número de
llamadas a la función compara. Justificar la solución adoptada.
Solución:
func dyV(x: Problema) dev (s: Solucion)
var
subproblemas: array[] de Problema
subsoluciones: array[] de Solucion
a,i: entero
alg
si esCasoBase(x):
s:= resuelveCasoBase(x)
|otras:
subproblemas := divide(x)
a := tamaño(subproblemas)
subsoluciones := <crea array de a Solucion>
desde i:=1 hasta a
subsoluciones[i] := dYV(subproblemas[i])
fdesde
s := combina(subsoluciones)
fsi
fin
Clase MinMaxDyV hereda de EsquemaDyV
a: array [1..N] de Elemento
xini: Problema
sol: Solucion
Clase Problema
i,j: Entero
proc Problema(pi,pj:Entero)
alg
<i,j> := <pi,pj>
fin
fClase
Clase Solucion
minimo, maximo: Elemento
proc Solucion(pmin,pmax:Elemento)
alg
<minimo,maximo> := <pmin,pmax>
fin
fClase
proc resuelve()
alg
xini := <crea Problema(1,N)>
sol := dyV(xini)
<tratar sol>
fin
func esCasoBase(x:Problema) dev (b:Logico)
alg
b := ((x.j-x.i)<2) // 1 ó 2 elementos
fin
func resuelveCasoBase(x:Problema) dev (s:Solucion)
alg
si x.i=x.j:
s := <crea Solucion(a[x.i],a[x.i])>
|otras: // 2 elementos
si compara(a[x.i],a[x.j])<0:
s := <crea Solucion(a[x.i],a[x.j])>
|otras:
s := <crea Solucion(a[x.j],a[x.i])>
fsi
fsi
fin
func divide(x:Problema) dev (probs: array[] de Problema)
var
k: Entero
alg
k := (i+j) div 2
probs := <crea array[1..2] de Problema>
probs[1] := <crea Problema(i,k)>
probs[1] := <crea Problema(k+1,j)>
fin
func combina(sols:array[] de Solucion) dev (s:Solucion)
alg
s := <crea Solucion>
si compara(sols[1].minimo,sols[2].minimo)<0:
s.minimo := sols[1].minimo
|otras:
s.minimo := sols[2].minimo
fsi
si compara(sols[1].maximo,sols[2].maximo)<0:
s.minimo := sols[2].maximo
|otras:
s.minimo := sols[1].maximo
fsi
fin
fClase
Justificación:
Un método sencillo consiste en recorrer el array en busca del máximo y el mínimo, lo que
podría suponer un total de 2N – 2 llamadas a la función compara.
Para minimizar en lo posible el número de comparaciones de elementos la cuestión clave es
que sólo se necesita una llamada a compara para obtener el mínimo y el máximo de dos
elementos. Mediante el esquema de divide y vencerás se puede conseguir sacar provecho de
esta propiedad, de manera que se subdivida el array de forma sucesiva hasta llegar a ese
tamaño. La forma más sencilla es hacerlo en dos mitades. De esta manera, el número de
comparaciones que se realizaría usando divide y vencerás para n elementos, NC(n), vendría
dado por:
0
,n = 1
⎧
⎪
NC ( n ) = ⎨
1
,n = 2
⎪2 NC ( n / 2) + 2 , n > 2
⎩
si n es potencia de 2. Resolviendo la ecuación, quedaría NC(n)=c1n+c2. Para determinar c1 y
c2 consideremos dos supuestos para el caso base: subproblema con 1 ó 2 elementos:
• Si 1 elemento:
NC(1) = 0 = c1+c2; NC(2) = 2NC(1)+2 = 2 = 2c1+c2 Î c1 = 2 ; c2 = –2
Î NC(N) = 2N–2 (igual número de comparaciones que en recorrido lineal)
(Nótese que se necesitarían 2 comparaciones para obtener el mínimo y el máximo de 2
elementos si el caso base sólo contempla tener un elemento)
• Si 2 elementos :
NC(2) = 1 = 2c1+c2; NC(4) = 2NC(2)+2 = 4 = 4c1+c2 Î c1 = 3/2 ; c2 = –2
Î NC(N) = (3/2)N–2
Dado que N no tiene por qué ser potencia de 2, el caso base debe considerar también que hay
que resolver subproblemas de tamaño 1.
PROBLEMA 6 (DIVIDE Y VENCERÁS)
Un sistema de localización de fuegos en un área geográfica dispone de un array de NxN
lógicos, tal que N es potencia de 2, donde V indica que hay fuego en una determinada
cuadrícula y F que no (se omite en el dibujo por claridad).
Se dispone de una función hayFuego(a: Area) dev (b: Lógico) que para un Area
geográfica dada devuelve Verdadero si hay fuego en el área que se le pasa o falso en caso
contrario.
En el ejemplo la función hayFuego para todo el área del dibujo (NxN) devuelve Verdadero,
pero para el área que recoge el cuadrado inferior izquierdo de tamaño 4x4 devuelve Falso.
Para la cuadro superior derecha de tamaño 1x1 devuelve Verdadero.
V
V
V
Diseñar un algoritmo de divide y vencerás que devuelva una lista con los puntos donde hay
fuego en esa área.
Solución
func dyV(x: Problema) dev (s: Solucion)
var
subproblemas: array[] de Problema
subsoluciones: array[] de Solucion
a,i: entero
alg
si esCasoBase(x):
s:= resuelveCasoBase(x)
|otras:
subproblemas := divide(x)
a := tamaño(subproblemas)
subsoluciones := <crea array de a Solucion>
desde i:=1 hasta a
subsoluciones[i] := dYV(subproblemas[i])
fdesde
s := combina(subsoluciones)
fsi
fin
Clase Punto
i,j: Entero
fclase
// coordenadas del punto
Clase Area
p: Punto
n: Entero
fClase
// punto del área con menor i,j
// longitud del lado
Clase Problema
a: Area
fClase
Clase Solucion
listp: Lista de Punto
fClase
func esCasoBase (x: Problema) dev (b: Logico)
alg
b := (hayFuego(x.a) = falso O x.a.n = 1)
fin
func resuelveCasoBase(x: Problema) dev (s: Solucion)
alg
s := <nueva Solucion>
s.listp := <nueva lista Vacía>
si hayFuego(x.a):
s.lisp.añadir(<nuevo Punto(x.a.i,x.a.j)>)
fsi
fin
func divide(x: Problema) dev (subpr: Array [] Problema)
alg
subpr := <nuevo array [4] de Problema>
mitad := x.a.n/2
subpr[1] := <nuevo Problema(x.a.i, x.a.j, mitad)>
subpr[2] := <nuevo Problema(x.a.i+mitad, x.a.j, mitad)>
subpr[3] := <nuevo Problema(x.a.i, x.a.j+mitad, mitad)>
subpr[4] := <nuevo Problema(x.a.i+mitad, x.a.j+mitad,
mitad)>
fin
func combina(sub[]: Solucion) dev (s: Solucion)
alg
s := <nueva Solucion>
s.listp := <nueva lista Vacía>
desde i :=1 hasta sub.tamaño
s.listp := s.listp + sub[i].listp //concatena listas
fdesde
fin
Descargar