Professional Documents
Culture Documents
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
Ó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
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
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;
mergeSort(prueba);
mergeSort(prueba);
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
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
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
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
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
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
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
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
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