You are on page 1of 13

Índice

„ Algoritmos ávidos

Diseño de Algoritmos
„ Dividir y vencer

Técnicas de Programació
Programación „ Programación dinámica
Gabriel Huecas
„ Retroceso (backtracking
(backtracking))

1 2

Algoritmos Ávidos Algoritmos Ávidos


„ Algoritmos ávidos „ La solución puede no ser óptima (la mejor)
„Problema del cambio mínimo: pero es “óptimo local”.
„ monedas de 25, 10, 5, 1. „monedas de 11, 5 y 1.
„ Devolver 63 con nº mínimo de monedas: „ cambio de 15 =>
„ 63 - 25 = 38 - 25 = 13 - 10 = 3 - 1 = 2 - 1 = 15 - 11 = 4 - 1 = 3 - 1 = 2 - 1 = 1 - 1 = 0
1-1=0 „ Solución: 1 de 11 y 4 de 1.

„ Óptimo: 3 de 5.
„ Varias fases
„ En cada fase se toma una decisión óptima, sin
pensar en el futuro
3 4

1
Problema del Agente Viajero Ejemplo
“Recorrer una serie de ciudades con coste mínimo”
„ Recorrido: ciclo simple que incluya todos los
vértices una sola vez C (1,7) D (15,7)
CICLO DE HAMILTON
„ Grafo: B E (15,4)
(4,3)
„ Vértices = ciudades
„ Arístas = viaje entre ciudades A (0,0) F (18,0)
„ Peso = coste de viajar de una ciudad a otra (peso de
una arista)
„ Grafo no dirigido:
„ Coste (A → B) = Coste (B → A)
5 6

Posible soluciones
„ Variante del Algoritmo de Kruskal:
Kruskal:
Def.-
Def.- Grado de un vértice: nº de aristas conectadas
a dicho vértice
„ Se van seleccionando las aristas más cortas,
siempre que
„ no haga que un vértice tenga grado 3
„ no forme ciclo, excepto que el nº de aristas seleccionadas
sea igual al nº de vértices ⇒ EXCEPTO QUE SEA LA
ÚLTIMA

7 8

2
Matriz de conectividad Posible soluciones
„ Longitudes
49’
49’70
A B C D E F 50
A - 5 7,08 16,6 15,5 18
B - 5 11,7 11 14,30
C - 14 14,3 18,40
D - 3 7,62
48’
48’38
E - 5
F - 49’
49’78

9 10

Dividir y Vencer MergeSort


„ Dividir: en problemas más pequeños, resueltos „ Ordenación por Intercalación
recursivamente ordenar
„ Excepto los caso base
24 13 1 26 15 2 38 27
„ Vencer: la solución al problema original se ordenar = ordenar
forma en base a las soluciones de los
subproblemas
24 13 1 26 15 2 38 27
+
intercalar
11 12

3
MergeSort: intercalación (I) MergeSort: intercalación (II)
24 13 1 26 15 2 38 27 1 13 24 26 2 15 27 38 1 2 13
posI posD posTmp
24 13 1 26 15 2 38 27
1 13 24 26 2 15 27 38 1 2 13 15
1 13 24 26 2 15 27 38 posI posD posTmp
posI posD posTmp
1 13 24 26 2 15 27 38 1 2 13 15 24
1 13 24 26 2 15 27 38 1
posI posD posTmp
posI posD posTmp
1 13 24 26 2 15 27 38 1 2 13 15 24 26
1 13 24 26 2 15 27 38 1 2
posI posD posTmp
posI posD posTmp
13 14

MergeSort: intercalación (III) Implementación (I)


class mergeSort {

„ Se copia el resto del segundo array en destino public static void main (String args[])
args[]) {
Random r= new Random();

int prueba[]=
prueba[]= new int[20];
for (int
(int i= 0; i < prueba.length;
prueba.length; i++)
1 13 24 26 2 15 27 38 1 2 13 15 24 26 27 38 prueba[i]=
prueba[i]= r.nextInt()
r.nextInt() % 100;

posI posD posTmp


System.out.println("El array inicial es:");
es:");
for (int
(int i= 0; i < prueba.length;
prueba.length; i++)
System.out.println(prueba[i]
System.out.println(prueba[i] + " ");

mergeSort(prueba);
mergeSort(prueba);

System.out.println("El array después de ordenar es:");


es:");
for (int
(int i= 0; i < prueba.length;
prueba.length; i++)
System.out.println(prueba[i]
System.out.println(prueba[i] + " ");
15 } 16

4
Implementación (II) Implementación (III)
public static void mergeSort (int A[]) { private static void merge
mergeSort (A, 0, A.length-
A.length-1); (int A[], int posI,
posI, int posD,
posD, int der)
der) {
int Atmp[]=
Atmp[]= new int[der - posI+1];
} int beginI=
beginI= posI;
posI;
int izq=
izq= posD - 1;
private static void mergeSort int posTmp=
posTmp= 0;
(int A[], int izq,
izq, int der)
der) {
if (izq
(izq < der)
der) { while ( (posI
(posI <= izq)
izq) && (posD
(posD <= der)
der) ) {
if (A[posI
(A[posI]
] < A[posD])
A[posD]) {
int centro=
centro= (izq
(izq + der)
der) / 2; Atmp[posTmp]
Atmp[posTmp] = A [posI
[posI];
];
mergeSort(A,
mergeSort(A, izq,
izq, centro);
centro); posI++;
posI++;
mergeSort(A,
mergeSort(A, centro+1, der);
der); } else {
merge(A,
merge(A, izq,
izq, centro+1, der);
der); Atmp[posTmp]
Atmp[posTmp] = A [posD
[posD];
];
} posD++;
posD++;
}
}
posTmp++;
posTmp++;
17 } 18

Implementación (IV) Torres de Hanoi


// copiamos el resto del array que quede
for (int
(int i= 0; i <= izq-
izq-posI;
posI; i++)
„ Problema: pasar n disco de A a C
Atmp[posTmp+i]=
Atmp[posTmp+i]= A[posI+i];
A[posI+i]; „ Solución:
for (int
(int i= 0; i <= der-
der-posD;
posD; i++)
Atmp[posTmp+i]=
Atmp[posTmp+i]= A[posD+i];
A[posD+i];
„ Pasar n-
n-1 discos de A a B
„ Pasar 1 disco de A a C

„ Pasar n-
n-1 discos de B a C
// pasamos al array original
for (int
(int i= 0; i < Atmp.length;
Atmp.length; i++)
A[beginI + i]= Atmp[i];
Atmp[i];
} A B C

19 20

5
Ejemplo
„ Multiplicar 2 enteros X e Y de n bits (o dígitos) „ X = 61438521 Y = 94736407 X·Y= 5820464730934047
A = 6143 B = 8521 C = 9473 D = 6407
„Algoritmo enseñado en básica requiere n productos X = A·104 + B Y = C·104 + D
parciales de tamaño n -> O(n2) X·Y= A·C·108 + (A·D + B·C)·104 + B·D
X = 6143 Y = 9473
„ Simplificación: n potencia de 2
„

A = 61 B = 43 C = 94 D = 73
„ Separamos X e Y en n/2 bits: A·C = 5734 A·D = 4453 B·C = 4042 B·D = 3139
X = A·2n/2 + B Y = C·2n/2 + D A·D+B·C = 8495
X·Y= A·C·2n + (A (A·D+B·
D+B·C)·2n/2+ B·D A·B·104 57 350 000
(A·D+B·C)·102 849 500
„ 4 multiplicaciones de n/2 bits
B·D 3 139
„ 3 sumas
---------
„ 2 desplazamientos de 2n y 2n/2 58 192 639
21 22

Implementación Programación Dinámica


Algoritmos recursivos con cálculos repetitivos
int mult (int X, int Y, int n) { // n: número de bits
if (n == 1) { „
return ((X == 1) & (Y == 1));
} else {
int A = n/2 bits izq. X;
int B = n/2 bits der. X; „ Almacenar valores intermedios en una tabla
int C = n/2 bits izq. Y;
int D = n/2 bits der. Y; „ variable global
int m1 = mult (A, C, n / 2); „ inicialización

„ registro de valor la primera vez


int m2 = mult (A, D, n / 2);
int m3 = mult (B, C, n / 2);
int m4 = mult (B, D, n / 2);

return m1 * 2^n
2^n + (m2+m3
(m2+m3)*2^(n/2)
)*2^(n/2) + m4;
}

23 24

6
Fibonacci recursivo Complejidad Fibonacci
⎧ 1 n = 0,1 F6
f ( n) = ⎨
⎩ f (n − 1) + f (n − 2) n > 1 F5 F4
F4 F3 F3 F2
public long fib (int n) throws Exception { F3 F2 F2 F1 F2 F1 F1 F0
if (n < 0) throw new Exception (“Pues si que...”);
if ( (n = 0) || (n = 1) )
F2 F1 F1 F0 F1 F0 F1 F0
return 1; F1 F0
else
return fib (n-1) + fib (n-2);
}

25 26

Fibonacci iterativo Problema de la Triangulación


public long fib (int n) throws Exception {
long ult, penult, res; „ Dados los vértices de un polígono y las
if (n < 0) throw new Exception (“Pues si que...”); distancias entre vértices, hallar la triangulación
if ( (n = 0) || (n = 1) )
mínima.
return 1; „ Triangulación:
ult = penult = 1; „ Conjunto de cuerdas que no se cruzan entre si,
for (int i = 2; i <= n; i++) { entre vértices no adyacentes, de forma que el
res = ult + penult; polígono queda dividido en triángulos
penult = ult;
ult = res; „ Mínimo:
La suma de la longitud de dichas cuerdas debe ser
}
return res; „
} mínima.
27 28

7
Representación Sabemos que…
„ Polígono de n vértices „ Hecho 1: el par vi y vi+1 es tocado, al menos,
v0,...,v
,...,vn-1 en sentido horario por una cuerda.
(8,26) v2 v3
(15,26) arista „ Hecho 2: sea (vi, vj) una cuerda de cualquier
v1 triangulación. Entonces existe vk tal que (v
(vi,vk),
(0,20)
(0,20)
v4 (27,21) (vk, vj) son aristas o cuerdas.
v5
(0,10) (22,12)
v0 Demostración: De otra forma, vi y vi+1 podría
cuerda limitar una región no triangular.
v6 (10,0)

29 30

Solución Solución recursiva


„ Para empezar la búsqueda, se cogen dos vértices „ Quedan dos subproblemas,
subproblemas, por ejemplo
adyacentes (v0, v1). eligiendo v3
Debe existir vk tal que (v1, vk) y (v
(vk,v0) sean cuerdas o
aristas en la triangulación. v2 v3 v3

v1
„ Se prueban todos los vk (k # 0, #1) v4
„ Con n vértices, tendremos n-
n-2 selecciones.
v5
„ Se examina cada selección. v0 v0

v6
31 32

8
„ Sis subproblema de tamaño s a partir del vértice vi „ Los problemas Si3 no hay que “resolverlos”
vi , vi+1,..., vi+s-
i+s-1 => s vértices v3 v3
(vi ,vi+s-
i+s-1) >= cuerda
Opciones para solución de Sis v4 v4

1. coger vi+s-
i+s-1, formar el triángulo (vi, vi+s-
i+s-1), (vi, vi+s-
i+s-2) y
v5 v5
i+s-2,vi+s-
(vi+s- i+s-1) y resolver Si,s-
i,s-1
v0 v0

2. coger vi+1, formar el triángulo (vi, vi+s-i+s-1), (vi+1, vi+s-


i+s-1) y v6 v6
(vi,vi+1) y resolver Si+1,s-
i+1,s-1
3. k entre 2 y s-s-3,coger vi+k, formar el triángulo (vi, vi+k), „Problemas repetidos
(vi+k, vi+s-
i+s-1) y (vi,vi+s-
i+s-1) y resolver Si,k+1 y Si+k,s-
i+k,s-k s -4
„3 llamadas recursivas (sin “resolver” Si3)
33 34

Ejemplo
„ Solución eficiente: calcular coste Cis de la 7 C07=75.43
triangulación Sis 6 C06=53.54 C16=55.22 C26=57.58 C36=64.69 C46=59.78 C56=59.78 C66=63.62

Cis = C (S
(Sis) 5 C05=37.54 C15=31.81 C25=35.49 C35=37.74 C45=45.50 C55=39.98 C65=38.09

Cis = min [Ci,k+1 + Ci+k,s-


Ci+k,s-k + 4 C04=16.16 C14=16.16 C24=15.65 C34=15.65 C44=22.69 C54=22.69 C64=17.89
s i=0 1 2 3 4 5 6
D(vi,vi+k) + D(vi+k,vi+s-
i+s-1)] 1 ≤ k ≤ s-2
Ejemplo
( )
D vp , vq = ⎨
(
⎧ long v p , v q ) si v p , v q no son adyacentes „
C65 = min [C6,2 + C0,4 +D(v6,v0) + D(v0,v3),
⎩0 si v p , v q son adyacentes 1 ≤ k ≤ s-2 C6,3 + C1,3 +D(v6,v1) + D(v1,v3),
• para s ≥ 4 en orden (S=4,5,...,n-
(S=4,5,...,n-1) C6,4 + C2,2 +D(v6,v2) + D(v2,v3)]
• Sis = 0 para s < 4
35 36

9
Retroceso (Backtracking)
D(v6,v0) = 0 (arista) „ Problemas sin una solución analítica o algorítmica:
D(v0,v3) = 21.93 „ Hay que hacer una búsqueda exhaustiva del espacio de
D(v6,v1) = 22.36 posibilidades
„ Y elegimos la buena
D(v1,v3) = 16.16
D(v6,v2) = 26.08
D(v2,v3) = 0 (arista)
„ Complejidad computacional alta: producto cartesiano
de posibilidades para construir cada candidato a
C65 = min [0 + 16.16 + 0 + 21.93,
solución
0 + 0 + 22.36 + 16.16, „ PERO se puede aprovechar parte de la solución anterior para
17.89 + 0 + 26.08 + 0] = construir la nueva
min [38.19, 38.52, 43.97] = 38.19
„ Al incluir este valor en la tabla, es conveniente indicar las „ Decisiones parciales
cuerdas que produjeron esta triangulación mínima. 37 38

Aplicaciones Ejemplo: 8 reinas en tablero


„ Problemas NP-
NP-completos „ Poner 8 reinas en un tablero de ajedrez (8x8)
„ Sin solución analítica sin que se den jaque.
„ Pintar un mapa con cuatro colores sin que dos „ Debemos probar todas las posibilidades
países fronterizos tengan el mismo color „ Cada reina puede estar en cualquiera de las
„ Obtención de mejor jugada casillas: 648 posibles combinaciones
„ Algo menos, las reinas no puedes estar en la misma
„ Tres en raya, damas, ajedrez (+poda
(+poda alfa-
alfa-beta) casilla
„ Etc.

39 40

10
Modelado de Casilla Modelado de Tablero (I)
public enum Casilla { public class Tablero {
VACIA, Reina, Rey, Torre, Alfil, Caballo, Peon;
Peon; int width,
width, height;
height;
Casilla tablero[][];
public String toString()
toString() {
switch(this)
switch(this) { Tablero (int
(int w, int h) {
case VACIA : return " "; width= w;
case Reina : return "r"; height= h;
case Rey : return "R"; tablero= new Casilla[w][h];
Casilla[w][h];
case Torre : return "t";
case Caballo: return "c"; for (int i= 0; i < width;
width; i++)
i++)
case Alfil : return "a"; for (int j= 0; j < height;
height; j++)
j++)
case Peon : return "p"; tablero[i][j]=
tablero[i][j]= Casilla.VACIA;
Casilla.VACIA;
} }
throw new AssertionError("Casilla
AssertionError("Casilla desconocida " + this);
this);
}
} 41 42

Modelado de Tablero (II) Cada reina en una columna


void imprime() { public static boolean ponReinas (Tablero t, int Size,
Size, int n) {
for (int j0= 0; j0 < Size;
Size; j0++) {
for (int i= 0; i < width;
width; i++)
i++) {
t.pon (0, j0, Figura.Reina);
Figura.Reina);
System.out.println("
System.out.println("-----------------
-----------------");
"); for (int j1= 0; j1 < Size;
Size; j1++) {
System.out.print("|");
System.out.print("|"); t.pon (1, j1, Figura.Reina);
Figura.Reina);
for (int j= 0; j < height; ...
height; j++)
j++) {
for (int j7= 0; j7 < Size;
Size; j7++) {
System.out.print(tablero[i][j]
System.out.print(tablero[i][j] + "|"); t.pon (7, j7, Figura.Reina);
Figura.Reina);
} if ( ! estanTodasEnJaque (t, Size)
Size) )
System.out.println();
System.out.println(); return true;
true;
t.pon (7, j7, Figura.VACIA);
Figura.VACIA);
}
}
System.out.println("
System.out.println("-----------------
-----------------");
"); ...
} }
t.pon (1, j1, Figura.VACIA);
Figura.VACIA);
public void pon(int i, int j, Casilla pieza) {
}
tablero[i][j]=
tablero[i][j]= pieza; t.pon (0, j0, Figura.VACIA);
Figura.VACIA);
} }
public Casilla dame(int i, int j) { return false;
false;
}
return tablero[i][j];
tablero[i][j]; 43 44

11
No insistir en los errores estanEnJaque
Poner N reinas es colocar una en una posición
public static boolean estanEnJaque
„ (Tablero t, int Size,
Size, int x, int y) {
libre de jaque e intentar poner N-
N-1 reinas // compruebo fila
for (int i= 0; i < Size;
Size; i++)
i++) {
„ Cada vez que ponemos una reina, comprobamos if (i == x) continue;
continue; // la misma pieza
if (t.dame(i,
t.dame(i, y) != Casilla.VACIA)
Casilla.VACIA)
que no está en jaque con las demás return true;
true;
}
„ Si lo está, pasamos a la siguiente posición
// compruebo columna
„ Si se agotan las posiciones, devolvemos false (no for (int j= 0; j < Size;
Size; j++)
j++) {
pudimos colocar la reina) if (j == y) continue;
continue; // la misma pieza
if (t.dame(x,
t.dame(x, j) != Casilla.VACIA)
Casilla.VACIA)
return true;
true;
}

45 46

estanEnJaque ponReinas
// comprueba diagonal
for (int i= x+1,
x+1, j = y+1;
y+1; (i < Size)
Size) && (j < Size);
Size); i++,
i++, j++)
j++) { public boolean ponReinas (Tablero t, int Size, int n) {
if ( t.dame(i,
t.dame(i, j) != Casilla.VACIA ) if (n==0)
return true;
true; return true;
}
// comprueba diagonal
for (int i= x+1,
int x, y; // posición de esta reina
x+1, j = y-
y-1; (i < Size)
Size) && (j >= 0); i++,
i++, j--
j--)) {
if ( t.dame(i,
t.dame(i, j) != Casilla.VACIA ) x= n-
n-1; // cada reina en una fila
return true;
true; for (y= 0; y < Size; y++) {
} t.pon(x,
t.pon(x, y, Casilla.Reina);
Casilla.Reina);
// comprueba diagonal if (! estanEnJaque(t,
estanEnJaque(t, Size, x, y) )
for (int i= x-1, j = y+1;
y+1; (i >= 0) && (j < Size);
Size); i--
i--,
, j++)
j++) { if ( ponReinas(t,
ponReinas(t, Size, n-
n-1) )
if ( t.dame(i,
t.dame(i, j) != Casilla.VACIA ) return true;
return true;
true; t.pon(x,
t.pon(x, y, Casilla.VACIA);
Casilla.VACIA);
}
}
// comprueba diagonal
for (int i= x-1, j = y-
return false;
y-1; (i >= 0) && (j >= 0); i--
i--,
, j--
j--)
) {
if ( t.dame(i,
t.dame(i, j) != Casilla.VACIA ) }
return true;
true;
} 47 48
return false;
false;

12
prueba Ejercicio
public static void main (String args[])
int Size= 8;
args[]) {
„ Para el próximo día
Tablero t = new Tablero(Size,
Tablero(Size, Size);
„ Se recogerá en clase
t.imprime();
t.imprime();

if (ponReinas(t
(ponReinas(t,
, Size, Size)) { „ Dado un tablero de ajedrez, nos preguntamos
System.out.println("Conseguí poner " + Size + "
reinas en el tablero");
tablero");
si un caballo, desde una posición determinada,
} else { podría recorrer todo el tablero sin pisar dos
System.out.println("NO Conseguí poner " + Size + "
reinas en el tablero");
tablero");
veces la misma casilla.
}
„ Escribir un programa que lo compruebe
t.imprime();
t.imprime(); „ Plus:
Plus: imprimir el recorrido si existe
} 49 50

13

You might also like