Divide y Venceras

Descargar como doc, pdf o txt
Descargar como doc, pdf o txt
Está en la página 1de 16

Cuestiones generales

La tcnica de diseo de algoritmos llamada "divide y vencers"


(divide and conquer) consiste en descomponer el problema original
en varios sub-problemas ms sencillos, para luego resolver stos
mediante un clculo sencillo. Por ltimo, se combinan los resultados
de cada sub-problema para obtener la solucin del problema original.
El pseudocdigo sera:
funcion divide_y_venceras_1(problema)
{
descomponer el problema en n subproblemas ms pequeos;
para i=1 hasta n hacer
resolver el subproblema k;
combinar las n soluciones;
}
Un ejemplo de "divide y vencers" es el quicksort. En ella, se divida
el arreglo en dos sub-arreglos, para luego resolver cada uno por
separado, y unirlos. El tiempo necesario para ordenar un arreglo de
elementos mediante el mtodo de la burbuja es cuadrtico: kN 2. Si
dividimos el arreglo en dos y ordenamos cada uno de ellos, el tiempo
necesario para resolverlo es ahora k(N/2)2+k(N/2)2=(kN2)/2. El
tiempo necesario para ordenarlo es la mitad, pero sigue siendo
cuadrtico.
Pero ahora, si los subproblemas son todava demasiado grandes, por
qu no utilizar la misma tctica con ellos, esto es, dividirlos a ellos
tambin, utilizando un algoritmo recursivo que vaya dividiendo ms el
sub-problema hasta que su solucin sea trivial? Un algoritmo del tipo:
funcion divide_y_venceras(problema)
{
si el problema es trivial
entonces resolver el problema;
si no es trivial
{
descomponer el problema en n subproblemas ms
pequeos;
para i=1 hasta n hacer
divide_y_venceras(subproblema_k);
combinar las n soluciones;
}
}
Si aplicamos este mtodo al quicksort, el tiempo disminuye hasta ser
logartmico, con lo que el tiempo ahorrado es mayor cuanto ms
aumenta N.

Divide y Venceras

Tiempo de ejecucin
El tiempo de ejecucin de un algoritmo de divide y vencers, T(n),
viene dado por la suma de dos elementos:
El tiempo que tarda en resolver los A subproblemas en los que se
divide el original, AT(n/B), donde n/B es el tamao de cada subproblema.
El tiempo necesario para combinar las soluciones de los subproblemas para hallar la solucin del original; normalmente es O(n k)
Por tanto, el tiempo total es: T(n) = AT(n/B) + O(nk). La solucin de
esta ecuacin, si A es mayor o igual que 1 y B es mayor que 1, es:
si A>Bk, T(n) = O(nlogBA)
si A=Bk, T(n) = O(nklog n)
si A<Bk, T(n) = O(nk)

Determinacin del umbral


Uno de los aspectos que hay que tener en cuenta en los algoritmos
de divide y vencers es dnde colocar el umbral, esto es, cundo se
considera que un sub-problema es suficientemente pequeo como
para no tener que dividirlo para resolverlo. Normalmente esto es lo
que hace que un algoritmo de divide y vencers sea efectivo o no. Por
ejemplo, en el algoritmo de ordenacin quicksort, cuando se tiene un
array de longitud 3, es mejor ordenarlo utilizando otro algoritmo de
ordenacin (con 6 comparaciones se puede ordenar), ya que el
quicksort debe dividirlo en dos sub-arrays y ordenar cada uno de
ellos, para lo que utiliza ms de 6 comparaciones.

Divide y Venceras

Problema de los puntos ms cercanos

El problema es: "dado un conjunto de puntos P, hallar el par de


puntos ms cercanos". La distancia entre dos puntos i y j es :
sqrt[(xi-xj)2+(yi-yj)2].
Una primera solucin podra ser mirar todos los pares de puntos y
quedarse con el ms pequeo; al haber n(n-1)/2 pares de puntos, el
tiempo de ejecucin es de O(n2). El programa resultante es muy
corto, y rpido para casos pequeos, pero al ser este procedimiento
una bsqueda exhaustiva, debera haber un algoritmo ms rpido.
Supongamos que ordenamos los puntos segn la coordenada x; esto
supondra un tiempo de O(nlog n), lo que es una cota inferior para el
algoritmo completo. Ahora que se tiene el conjunto ordenado, se
puede trazar una lnea vertical, x = x m, que divida al conjunto de
puntos en dos: Pi y Pd. Ahora, o el par ms cercano est en P i, o est
en Pd, o un punto est en Pi y el otro en Pd. Si los dos estuvieran en P i
o en Pd, se hallara recursivamente, subdividiendo ms el problema,
por lo que ahora el problema se reduce al tercer caso, un punto en
cada zona.

Llamemos di, dd y dc a las mnimas distancias en el primer caso, en el


segundo, y en el tercero, respectivamente, y dmin al menor de di y dd.
Para resolver el tercer caso, slo hace falta mirar los puntos cuya
coordenada x est entre xm-dmin y xm+dmin. Para grandes conjuntos de

Divide y Venceras

puntos distribuidos uniformemente, el nmero de puntos que caen en


esa franja es sqrt(n), as que con una bsqueda exhaustiva el
tiempo de ejecucin sera de O(n), y tendramos el problema
resuelto. El tiempo de ejecucin sera, segn lo dicho en el otro
apartado, O(nlog n).

Pero si los puntos no estn uniformemente distribuidos, la cosa


cambia. En el peor de los casos, todos los puntos estn en la franja,
as que la fuerza bruta no siempre funciona en tiempo lineal. Para
ello, se puede recurrir a ordenar los puntos de la franja segn la
coordenada y, lo que supone un tiempo de ejecucin de O(nlog n).
Si la coordenada y difiere en ms de dmin, se puede pasar al siguiente
punto. El tiempo de ejecucin es O(max(nlog n,nlog
n))=O(nlog n), con lo que mantenemos el tiempo de ejecucin
anterior. El programa sera:
#include
#include
#include
#include

<stdio.h>
<stdlib.h>
<string.h>
<math.h>

#define MAX 110L // Nmero mximo de puntos.


struct punto // Estructura de un punto.
{
double x;
double y;
};
void busca(struct punto *,int);
int ordenax(const void *,const void *);
double dist(struct punto,struct punto);
struct punto c1,c2; // Puntos ms cercanos.
double mindist=1e10; // Mnima distancia.
int main()
{
int a,num;
double x,y;
struct punto p[MAX];

Divide y Venceras

for(scanf(" %d",&num),a=0;a<num;a++) // Coge la entrada


{
scanf(" %lf %lf",&x,&y);
p[a].x=x;
p[a].y=y;
}
busca(p,num); // Hacer la primera bsqueda.
printf("Distancia minima: %.3lf\n",mindist);
printf("Puntos: (%.3lf,%.3lf) y (%.3lf,
%.3lf)\n",c1.x,c1.y,c2.x,c2.y);
}

return(0);

void busca(struct punto *p,int num)


{
double d;
int desde,hasta,a,b;
if(num<=1) // Si no hay pares de puntos:
return; // salir.
// Ordenar los puntos por la coordenada x.
qsort(p,num,sizeof(struct punto),ordenax);
// Mirar en el subconjunto de la izquierda.
busca(p,num/2);
// Mirar en el subconjunto de la derecha.
busca(p+num/2,(num+1)/2);
// Hallar los lmites del conjunto central.
for(desde=num/2; desde>0 && p[num/2].x-p[desde].x<mindist;
desde--);
for(hasta=num/2; hasta<num-1 && p[hasta].x-p[num/2].x<mindist;
hasta++);
// Bsqueda exhaustiva en el conjunto central.
for(a=desde;a<=hasta;a++)
for(b=a+1;b<=hasta;b++)
if((d=dist(p[a],p[b]))<mindist)
{
mindist=d;
c1.x=p[0].x;
c1.y=p[0].y;
c2.x=p[1].x;
c2.y=p[1].y;
}
}
// Funcin auxiliar del qsort.
int ordenax(const void *a,const void *b)
{
return(((*(struct punto *)a).x<(*(struct punto *)b).x)?-1:1);
}
// Funcin que calcula la distancia entre dos puntos.
double dist(struct punto a,struct punto b)
{
return(sqrt((a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y)));
}

Divide y Venceras

Hallar la mediana de un conjunto de puntos


La mediana es el elemento de un conjunto que cumple que en el
conjunto hay el mismo nmero de elementos mayores que l y
menores que l. Dicho de otra forma, si el conjunto est ordenado, la
mediana es el elemento central. Una forma de resolverlo es esa,
ordenar el conjunto y localizar el elemento central. El tiempo de
ejecucin sera de O(nlog n), pero, se podra resolver en tiempo
lineal utilizando una estrategia de divide y vencers?
Hay un mtodo llamado seleccin rpida que tiene un tiempo
esperado lineal, pero que en algunos casos puede tener tiempo
O(n2). Este mtodo no slo puede hallar la mediana, sino el
elemento que ocupa la posicin k-sima. Para ello, se toma un
elemento pivote (normalmente el primero), y se divide el conjuntos
en dos sub-conjuntos, segn los elementos sean mayores o menores
que el pivote. El pivote ahora ocupar una posicin p. Si p es igual a
k, el nmero buscado es el pivote; si p es menor que k, el nmero
buscado est en el primer subconjunto, y si p es mayor que k, el
nmero buscado est en el segundo subconjunto. Este mtodo se
contina recursivamente, hasta que se halle el elemento buscado. El
programa sera:
#include <stdio.h>
#include <stdlib.h>
int seleccionrapida(int *,int,int);
int k;
int main()
{
int num,a,m;
int *array;
scanf(" %d",&num); // Pedir el tamao del array
array=(int *)malloc(sizeof(int)*num);
for(a=0;a<num;a++) // Dar valores al array
scanf(" %d",&array[a]);
scanf(" %d",&k); // Pedir el valor k
k--; // Por ir de 0 a num-1
m=seleccionrapida(array,0,num-1); // Para llamar a la funcin
printf("%d\n",m);
return(0);
}
int seleccionrapida(int *array,int desde,int hasta)
{
int i,d,aux; // i realiza la bsqueda de izquierda a derecha

Divide y Venceras

// y j realiza la bsqueda de derecha a izquierda.


for(i=desde+1,d=hasta; ; ) // Valores iniciales de la bsqueda.
{
for( ;i<=hasta && array[i]<=array[desde]; i++); // Primera
bsqueda
for( ;d>=0 && array[d]>=array[desde]; d--); // Segunda bsqueda
if(i<d) // si no se han cruzado:
{
aux=array[i]; // Intercambiar.
array[i]=array[d];
array[d]=aux;
}
else // si se han cruzado:
break; // salir del bucle.
}
if(d==desde-1) // Si la segunda bsqueda se sale del array
d=desde; // es que el pivote es el elemento
// ms pequeo: se cambia con l mismo.
aux=array[d]; // Colocar el pivote
array[d]=array[desde]; // en su posicin.
array[desde]=aux;
if(d==k)
return(array[d]); // El pivote es el elemento buscado
else if(d>k)
return(seleccionrapida(array,desde,d-1)); // Buscar en el primer
array.
else
return(seleccionrapida(array,d+1,hasta)); // Buscar en el
segundo array.
}

En esta solucin en tiempo medio lineal se descartan slo unos


cuantos elementos cada vez que se llama a la funcin recursiva,
dependiendo del pivote que elijamos. Para descartar una fraccin
constante de elementos (y mejorar el tiempo de ejecucin del peor
caso) habra que elegir un mejor pivote, a ser preferible la mediana
(cosa que no puede ser, porque es justamente ella la que estamos
buscando). Tampoco se puede perder mucho tiempo en buscar un
buen pivote, porque hara demasiado lento el programa. Una opcin
sera elegir tres elementos y utilizar como pivote la mediana de esos
tres, pero en el peor caso eso sigue siendo una mala opcin. Hay un
algoritmo de eleccin del pivote llamado "particin con base en la
mediana de la mediana de cinco", que consiste en:

Dividir los n elementos en n/5 grupos de 5 elementos


(ignorando los ltimos elementos).
Encontrar la mediana de cada grupo de 5 elementos, lo que da
una lista de n/5 medianas M.
Hallar la mediana de M, o un buen pivote en M, lo que se puede
hacer utilizando este algoritmo recursivamente.

Divide y Venceras

Se puede probar que, utilizando este mtodo, y en el peor de los


casos, cada llamada a la funcin recursiva desprecia a ms del 30%
de los elementos. Esto hace que el algoritmo de seleccin rpida sea
lineal, a pesar de que utilizamos un algoritmo auxiliar para la
bsqueda del pivote. El problema completo sera:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int seleccionrapida(int *,int *,int,int,int);
int hallamediana(int *,int *,int);
int main()
{
int num,a,m,k;
int *array,*pos;
scanf(" %d",&num); // Pedir el tamao del array
array=(int *)malloc(sizeof(int)*num);
for(a=0;a<num;a++) // Dar valores al array
scanf(" %d",&array[a]);
scanf(" %d",&k); // Pedir el valor k
k--;
pos=(int *)malloc(sizeof(int)*num);
m=seleccionrapida(array,pos,0,num-1,k); // Para llamar a la funcin
printf("%d\n",m);
}

return(0);

int seleccionrapida(int *array,int *pos,int desde,int hasta,int k)


{
int i,d,aux; // i realiza la bsqueda de izquierda a derecha
// y j realiza la bsqueda de derecha a izquierda.
// Hallar la falsa mediana para tomarla como pivote
for(i=desde;i<=hasta;i++)
pos[i-desde]=i;
d=hallamediana(array+desde,pos,hasta-desde+1);
aux=array[d]; // Poner la falsa mediana al principio.
array[d]=array[desde];
array[desde]=aux;
for(i=desde+1,d=hasta;;) // Valores iniciales de la bsqueda.
{
for(;i<=hasta && array[i]<=array[desde];i++); // Primera
bsqueda
for(;d>=0 && array[d]>=array[desde];d--); // segunda bsqueda
if(i<d) // si no se han cruzado:
{
aux=array[i]; // Intercambiar.
array[i]=array[d];
array[d]=aux;
}
else // si se han cruzado:
break; // salir del bucle.

Divide y Venceras

}
if(d==desde-1) // Si la segunda bsqueda se sale del array
d=desde; // es que el pivote es el elemento
// ms pequeo: se cambia con l mismo.
aux=array[d]; // Colocar el pivote
array[d]=array[desde]; // en su posicin.
array[desde]=aux;
if(d==k)
return(array[d]); // El pivote es el elemento buscado
else if(d>k)
return(seleccionrapida(array,pos,desde,d-1,k)); // Buscar en el
primer array.
else
return(seleccionrapida(array,pos,d+1,hasta,k)); // Buscar en el
segundo array.
}
int hallamediana(int *array,int *pos,int num)
{
int a,*array2,b,c,d;
if(num<5) // Si hay menos de 5 elementos
return(pos[0]); // vale el primero (lo que devuelve esta
// funcin es el ndice de la mediana en la variable
"array" de la funcin
// seleccionrapida que llama por primera vez a esta
funcin.
// Hallar la mediana de cada cinco elementos.
array2=(int *)malloc(sizeof(int)*((num/5)+4));
for(a=0;a+4<num;a+=5)
{
memcpy(array2+(a/5),array,sizeof(int)*5);
for(b=0;b<5;b++)
for(c=b+1;c<5;c++)
if(array2[b]>array2[c])
{
d=array2[b];
array2[b]=array2[c];
array2[c]=d;
d=pos[b];
pos[b]=pos[c];
pos[c]=d;
}
array2[a/5]=array[a+2];
pos[a/5]=pos[a+2];
}
// Hallar la mediana de las medianas.
a=hallamediana(array2,pos,num/5);
free(array2);
return(a);
}

Divide y Venceras

Multiplicacin de matrices
Dadas dos matrices A y B de tamao n x n, hallar C, el producto de
las dos. Un primer algoritmo se saca de la definicin de producto de
matrices: Ci j es el producto escalar de la i-sima fila por la j-sima
columna. Su tiempo de ejecucin sera O(n3):
int i,j,k;
int A[n][n],B[n][n],C[n][n];
// Dar valores a A y B.
for(i=0;i<n;i++)
for(i=0;i<n;i++)
{
C[i][j]=0;
for(i=0;i<n;i++)
C[i][j]+=A[i][k]*B[k][j];
}

Pero este tiempo de ejecucin tambin se puede mejorar. Si n es una


potencia de 2, se pueden dividir A, B y C en cuatro submatrices cada
una, de la forma:

Ahora, para hallar las submatrices de C, basta con utilizar que:


C1,1 = A1,1B1,1 + A1,2B2,1
C1,2 = A1,1B1,2 + A1,2B2,2
C2,1 = A2,1B1,1 + A2,2B2,1
C2,2 = A2,1B1,2 + A2,2B2,2
Con este mtodo el tiempo de ejecucin es de T(n) = 8T(n/2) +
O(n2), o lo que es lo mismo, O(n 3), con lo que no habra diferencia
entre este mtodo y el primero propuesto. Hay que reducir el nmero
de multiplicaciones por debajo de 8. Esto se logra utilizando el
algoritmo de Strassen. Definimos las siguientes matrices:
M1
M2
M3
M4
M5
M6
M7

=
=
=
=
=
=
=

(A1,2 - A2,2)(B2,1 + B2,2)


(A1,1 + A2,2)(B1,1 + B2,2)
(A1,1 - A2,1)(B1,1 + B2,1)
(A1,1 + A1,2)B2,2
A1,1(B1,2 - B2,2)
A2,2(B2,1 - B1,1)
(A2,1 + A2,2)B1,1

Divide y Venceras

10

para las que se necesitan 7 multiplicaciones diferentes, y ahora se


calculan las submatrices de C utilizando slo sumas:
C1,1
C1,2
C2,1
C2,2

=
=
=
=

M1
M4
M6
M2

+
+
+
-

M2 - M4 + M6
M5
M7
M3 + M5 - M7

El nuevo tiempo de ejecucin es de T(n) = 7T(n/2) + O(n 2), es decir,


de T(n) = O(nlog27) = O(n2.81). Este algoritmo slo es mejor que el
directo cuando n es muy grande, y es ms inestable cuando se utiliza
con nmeros reales, por lo que tiene una aplicabilidad limitada.
Para el caso general en el que n no es potencia de 2, se pueden
rellenar las matrices con ceros hasta que las dos matrices sean
cuadradas y n sea una potencia de 2.
El programa queda as:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int max(int,int);
int **multiplicamatriz(int **,int **,int); // Funcin recursiva que
multiplica matrices
int **suma(int **,int **,int); // suma de matrices
int **resta(int **,int **,int);// Resta de matrices
void libera(int **,int); // Libera memoria
int main()
{
int **mapa1,**mapa2,**sol;
int f1,c1,f2,c2,a,b,m,m1;
scanf(" %d %d",&f1,&c1); // Tamao de la primera matriz
scanf(" %d %d",&f2,&c2); // Tamao de la segunda matriz
m1=max(f1,max(c1,max(f2,c2)));
for(m=1;m<m1;m*=2); // El tamao de las matrices cuadradas a
multiplicar
//debe ser de la forma 2k, si no se completan con ceros.
mapa1=(int **)malloc(sizeof(int *)*m); // Se crea la primera matriz
for(a=0;a<m;a++)
{
mapa1[a]=(int *)malloc(sizeof(int)*m);
memset(mapa1[a],0,sizeof(int)*m);
}
for(a=0;a<f1;a++) // Se cogen los datos de la primera matriz.
for(b=0;b<c1;b++)
scanf(" %d",&mapa1[a][b]);
mapa2=(int **)malloc(sizeof(int *)*m); // Se crea la sedunda
matriz.
for(a=0;a<m;a++)

Divide y Venceras

11

{
mapa2[a]=(int *)malloc(sizeof(int)*m);
memset(mapa2[a],0,sizeof(int)*m);
}
for(a=0;a<f2;a++) // Se cogen los datos de la segunda matriz.
for(b=0;b<c2;b++)
scanf(" %d",&mapa2[a][b]);
sol=multiplicamatriz(mapa1,mapa2,m); // Se multiplican.
for(a=0;a<f1;a++) // Se imprime el resultado.
{
for(b=0;b<c2;b++)
printf("%d ",sol[a][b]);
printf("\n");
}
}

return(0);

int max(int a,int b)


{
return((a>b)?a:b);
}
int **multiplicamatriz(int **mapa1,int **mapa2,int num)
{
int **sol,**M[8],**f1,**f2,**aux,**aux2;
int **A[2][2],**B[2][2],**C[2][2];
int a,q,w,r;
sol=(int **)malloc(sizeof(int *)*num);
for(a=0;a<num;a++)
sol[a]=(int *)malloc(sizeof(int)*num);
if(num==1)
{
sol[0][0]=mapa1[0][0]*mapa2[0][0];
return(sol);
}
// Crear las submatrices de A y B.
for(q=0;q<2;q++)
{
for(w=0;w<2;w++)
{
A[q][w]=(int **)malloc(sizeof(int *)*(num/2));
for(a=0;a<num/2;a++)
{
A[q][w][a]=(int *)malloc(sizeof(int)*(num/2));
for(r=0;r<num/2;r++)
A[q][w][a][r]=mapa1[a+(num/2)*q][r+(num/2)*w];
}

B[q][w]=(int **)malloc(sizeof(int *)*(num/2));


for(a=0;a<num/2;a++)
{
B[q][w][a]=(int *)malloc(sizeof(int)*(num/2));
for(r=0;r<num/2;r++)
B[q][w][a][r]=mapa2[a+(num/2)*q][r+(num/2)*w];
}

Divide y Venceras

12

}
// Hallar las matrices M.
f1=resta(A[0][1],A[1][1],num/2);
f2=suma(B[1][0],B[1][1],num/2);
M[1]=multiplicamatriz(f1,f2,num/2);
libera(f1,num/2);
libera(f2,num/2);
f1=suma(A[0][0],A[1][1],num/2);
f2=suma(B[0][0],B[1][1],num/2);
M[2]=multiplicamatriz(f1,f2,num/2);
libera(f1,num/2);
libera(f2,num/2);
f1=resta(A[0][0],A[1][0],num/2);
f2=suma(B[0][0],B[0][1],num/2);
M[3]=multiplicamatriz(f1,f2,num/2);
libera(f1,num/2);
libera(f2,num/2);
f1=suma(A[0][0],A[0][1],num/2);
f2=B[1][1];
M[4]=multiplicamatriz(f1,f2,num/2);
libera(f1,num/2);
f1=A[1][1];
f2=resta(B[0][1],B[1][1],num/2);
M[5]=multiplicamatriz(f1,f2,num/2);
libera(f2,num/2);
f1=A[1][1];
f2=resta(B[1][0],B[0][0],num/2);
M[6]=multiplicamatriz(f1,f2,num/2);
libera(f2,num/2);
f1=suma(A[1][0],A[1][1],num/2);
f2=B[0][0];
M[7]=multiplicamatriz(f1,f2,num/2);
libera(f1,num/2);
// Hallar las submatrices de C.
C[0][0]=suma(M[1],M[2],num/2);
aux=C[0][0];
C[0][0]=resta(C[0][0],M[4],num/2);
aux2=C[0][0];
C[0][0]=suma(C[0][0],M[6],num/2);
libera(aux,num/2);
libera(aux2,num/2);
C[0][1]=suma(M[4],M[5],num/2);
C[1][0]=suma(M[6],M[7],num/2);
C[1][1]=resta(M[2],M[3],num/2);
aux=C[1][1];
C[1][1]=suma(C[1][1],M[5],num/2);
aux2=C[1][1];
C[1][1]=resta(C[1][1],M[7],num/2);
libera(aux,num/2);
libera(aux2,num/2);

Divide y Venceras

13

for(a=1;a<=7;a++)
libera(M[a],num/2);
// Unir las submatrices de matrices C en sol.
for(q=0;q<num;q++)
for(w=0;w<num;w++)
sol[q][w]=C[q/(num/2)][w/(num/2)][q%(num/2)][w%(num/2)];
// Liberar las submatrices de A, B y C.
for(q=0;q<2;q++)
for(w=0;w<2;w++)
{
libera(A[q][w],num/2);
libera(B[q][w],num/2);
libera(C[q][w],num/2);
}
return(sol);
}
int **suma(int **mapa1,int **mapa2,int num)
{ // sumar mapa1 y mapa2.
int a,b;
int **sol;
sol=(int **)malloc(sizeof(int *)*num);
for(a=0;a<num;a++)
{
sol[a]=(int *)malloc(sizeof(int)*num);
for(b=0;b<num;b++)
sol[a][b]=mapa1[a][b]+mapa2[a][b];
}
return(sol);
}
int **resta(int **mapa1,int **mapa2,int num)
{ // Restar mapa2 de mapa1.
int **sol;
int a,b;
sol=(int **)malloc(sizeof(int *)*num);
for(a=0;a<num;a++)
{
sol[a]=(int *)malloc(sizeof(int)*num);
for(b=0;b<num;b++)
sol[a][b]=mapa1[a][b]-mapa2[a][b];
}
return(sol);
}
void libera(int **mapa,int num)
{
int a;
for(a=0;a<num;a++) // Liberar la tabla dinmica de 2D.
free(mapa[a]);
free(mapa);
}

Divide y Venceras

14

Multiplicacin de dos enteros


Este problema apareci en una competencia internacional de
programacin y su solucin ms efectiva es la que utiliza la tcnica
"divide y vencers".
En muchos lenguajes de programacin los tipos enteros disponibles
slo permiten representar datos dentro de un rango limitado, debido
a las restricciones propias de su representacin interna. La utilizacin
de tipos de datos reales en ocasiones permite representar datos
pertenecientes a un rango mayor, pero perdiendo precisin en los
clculos. Por ello, puede ser necesario buscar mtodos alternativos de
representacin de los nmeros enteros cuando queramos manipular
valores muy grandes.
Objetivo
Se quiere poder multiplicar nmeros enteros grandes sin provocar
errores de clculo ni resultados incorrectos, y sin utilizar nmeros
reales.
Entrada
La entrada del programa consiste de una secuencia de lneas, que
residen en un archivo de texto (ASCII) con nombre MULT.DAT, que
tendr el siguiente formato:

La primera lnea (la 1) contiene el nmero m de


multiplicaciones a efectuar. Se puede suponer 1 <= m < 10.
Las 2m lneas siguientes (de la 2 a la 2m+1) contienen, cada
una, un nmero entero representado como un string de como
mximo 50 caracteres (dgitos -es decir, caracteres entre el '0'
y el '9'- posiblemente precedidos del signo '-').

Salida
La salida del programa ha de grabarse en un archivo de texto (ASCII)
con nombre MULT.RES, que contendr una secuencia de m lneas con
el resultado de las multiplicaciones:
La lnea k del archivo de salida representa la multiplicacin de los
enteros contenidos en las lneas 2k y 2k+1 del archivo de entrada.
Los enteros se representan tambin como una cadena de caracteres
(dgitos posiblemente precedidos del signo '-'). Los resultados no
pueden tener ceros superfluos a la izquierda.

Divide y Venceras

15

Ejemplo de entrada
3
-0124576679
10859687
00345
42
0
23
Ejemplo de salida
-1352863741439473
14490
0

Divide y Venceras

16

También podría gustarte