Divide y Venceras Análisis de Algoritmos
-
Upload
ararage-kun -
Category
Documents
-
view
155 -
download
0
description
Transcript of Divide y Venceras Análisis de Algoritmos
Análisis de
Algoritmos
Práctica 2: Divide y Vencerás
Multiplicación de dos
números binarios.
por
ESCAMILLA CAMACHO JUAN
EDUARDO
PEREZ PEREZ JOSÉ RICARDO
DRA. GARCÍA MENDOZA
CONSUELO VARINIA
Profesor
1
TABLA DE CONTENIDO
INTRODUCCIÓN............................................................................................................... 2
DESARROLLO .............................................................................................................. 3
A) TABLA ..........................................................ERROR! BOOKMARK NOT DEFINED.
B) GRÁFICA ............................................................................................................. 4
C) CUESTIONARIO .................................................................................................... 4
CONCLUSIONES........................................................................................................... 5
ANEXOS ..................................................................ERROR! BOOKMARK NOT DEFINED.
A) MULTIPLICACIÓN BINARIA: MÉTODO TRADICIONAL ........... ERROR! BOOKMARK NOT
DEFINED.
B) MULTIPLICACIÓN BINARIA: MÉTODO DE DIVIDE Y VENCERÁS. ... ERROR! BOOKMARK
NOT DEFINED.
C) MULTIPLICACIÓN BINARIA: MÉTODO DE GAUSS. .ERROR! BOOKMARK NOT DEFINED.
BIBLIOGRAFÍA .......................................................ERROR! BOOKMARK NOT DEFINED.
2
INTRODUCCION
En la cultura popular, divide y vencerás hace referencia a un refrán que
implica resolver un problema difícil, dividiéndolo en partes más simples tantas
veces como sea necesario, hasta que la resolución de las partes se torna
obvia. La solución del problema principal se construye con las soluciones
encontradas.
En las ciencias de la computación, el término divide y vencerás (DYV) hace
referencia a uno de los más importantes paradigmas de diseño algorítmico. El
método está basado en la resolución recursiva de un problema dividiéndolo
en dos o más sub-problemas de igual tipo o similar. El proceso continúa hasta
que éstos llegan a ser lo suficientemente sencillos como para que se
resuelvan directamente. Al final, las soluciones a cada uno de los sub-
problemas se combinan para dar una solución al problema original.
Esta técnica es la base de los algoritmos eficientes para casi cualquier tipo de
problema como, por ejemplo, algoritmos de
ordenamiento (quicksort, mergesort, entre muchos otros), multiplicar números
grandes (Karatsuba), análisis sintácticos (análisis sintáctico top-down) y
la transformada discreta de Fourier.
Por otra parte, analizar y diseñar algoritmos de DyV son tareas que lleva
tiempo dominar. Al igual que en la inducción, a veces es necesario sustituir el
problema original por uno más complejo para conseguir realizar la recursión,
y no hay un método sistemático de generalización.
El nombre divide y vencerás también se aplica a veces a algoritmos que
reducen cada problema a un único subproblema, como la búsqueda
binaria para encontrar un elemento en una lista ordenada (o su equivalente
en computación numérica, el algoritmo de bisección para búsqueda de
raíces). Estos algoritmos pueden ser implementados más eficientemente que
los algoritmos generales de “divide y vencerás”; en particular, si es usando
una serie de recursiones que lo convierten en simples bucles. Bajo esta amplia
definición, sin embargo, cada algoritmo que usa recursión o bucles puede ser
tomado como un algoritmo de “divide y vencerás”. El nombre decrementa y
vencerás ha sido propuesta para la subclase simple de problemas.
La corrección de un algoritmo de “divide y vencerás”, está habitualmente
probada una inducción matemática, y su coste computacional se determina
resolviendo relaciones de recurrencia.
3
1|DESARROLLO
Implementa los siguientes algoritmos:
Algoritmo 1. Multiplicación de dos números en base 2 por el método tradicional.
Algoritmo 2. Multiplicación de dos números en base 2 con Divide y Vencerás a través de
sus mitades.
Algoritmo 3. Multiplicación de dos números en base 2 con Divide y Vencerás a través de
mitades, utilizando el truco de Gauss para eliminar un producto.
A) TABLA DE COMPLEJIDAD
COMPLEJIDAD COMPLEJIDAD SIN
RECURSIVIDAD
Algoritmo 1 T(n) = n²+2n. T(n) = C1*0n+ C2*n11n+
C3*n21n+ C4*n31n+
C5*n41n+ C6*n51n.
Algoritmo 2 T(n) = 4T(n/2)+C. T(n) = C1*4log2
(n) +
C2*1log2
(n).
Algoritmo 3 T(n) = T(n)= 3T(n/2) +
C.
T(n) = C1*3log2
(n) +
C2*1log2
(n).
B) GRAFICA
GRAFICA COMPLEJIDAD MULTIPLICACION DE NUMEROS BINARIOS
NORMAL
4
C) CUESTIONARIO
1. ¿La complejidad del algoritmo de la multiplicación por método tradicional
disminuye al implementarlo con la con la estrategia de Divide y Vencerás?
RESPUESTA:
SI, DISMINUYE YA QUE SE HACE RECURSIVO ES DECIR QUE SE REALIZA CON
MENOS CICLOS Y SE REDUCE POR QUE TIENE UNA FUNCIÓN EXCLUSIVA
PARA DIVIDIR ,
2. ¿La complejidad del algoritmo de la multiplicación con la estrategia de Divide y
Vencerás es menor cuando se disminuyen las llamadas recursivas en cada nivel
de recursividad, como se hizo con el truco de Gauss para disminuir una llamada
recursiva?
RESPUESTA:
SI, PORQUE EN EL ALGORITMO DE DIVIDE Y VENCERAS NORMAL SE RALIZA
4 RAMAS DEL ABROL PARA LA DIVISION Y CON GAUSS SE DESMINUYE YA
QUE DE 4 RAMAS SE OBTIENEN 3 RAMAS UNICAMENTE Y SE ELIMINA UN
CICLO RECURSIVO Y POR CONISGUIENTE SE DISMINUYE LA COMPELJIDAD.
3. Analizando la gráfica indique para el cual, el algoritmo de menor complejidad se
vuelve cota inferior de cada uno de los otros dos algoritmos.
RESPUESTA:
PUES DESPUES DE ANILIZAR EL ALGORTIMO 3 DE DIVIDE Y VENCERAS
CON EL METODO DE GAUSS SE VUELVE LA COTA INFERIOR DE LOS DEMAS
ALGORITMOS
4. ¿Cuál es el código más sencillo? ¿Por qué lo considera más sencillo?
RESPUESTA:
PUES EL CÓDIGO MAS SENCILLO ES EL PRIMER OSEA, EL PROGRAMA DE
MULTIPLICACION DE BINARIOS POR METODO TRADICIONAL YA QUE
OCUPAMOS EL METODO DE PROGRAMACION A NIVEL DE BITS Y OCUPAMOS
OPERACION DE COMPUERTAS LÓGICAS Y NO NECESITAMOS OCUAPAR UNA
FUNCION DE CONVERSION DE BINARIO A DECIMAL ESTO SE SUSTITUYO
5
CON EL SIGNO DE CORRIMIENTO A LA IZQUIERDA “<<” Y CON ESTE EL
COMPILADOR AUTOMÁTICAMENTE LO CONVIERTE Y NO SE ENCESITA
LLENAR CON CEROS POR QUE AL PROGRAMA AL NIVEL DE BITS SE LLENA
CON CEROS , Y TODO EL PROGRAMA SE REALIZA CON 12 LINEAS DE CÓDIGO
APROXIMADAMENTE.
2|CONCLUSIONES
JUAN EDUARDO ESCAMILLA CAMACHO
El objetivo principal de este proyecto consistía en el análisis del Algoritmo Divide y
Vencerás pro medio de un software sobre el método de multiplicación de números binarios
utilizando 3 algoritmos son tres: Método normal, Divide y vencerás y Divide y Vencerás
por medio de Gauss.
Se utilizó un proceso de desarrollo específico y una herramienta que permite programar
diferentes aplicaciones en diferentes lenguajes NETBEANS Y CODEBLOCKS Esta
herramienta complementa el marco de trabajo para la reutilización de requisitos y
constituye la parte práctica de la misma práctica
El resultado de emplear esta método es poder reducir la complejidad de ciertos programas
utilizando este método el cual se ocupado desde hace mucho tiempo atrás, además de
reducir la complejidad reduce el tiempo de ejecución del mismo y la programación se
torna más sencilla ya que nos ahorramos código y ciclos ya que utilizamos clases en el caso
de Java y funciones en el caso de C en CodeBlocks.
PEREZ PEREZ JOSÉ RICARDO
Para esta práctica personalmente me fue muy interesante ya que al llevar 6 semestres en la
carrera jamás había entendido bien el funcionamiento de la recursividad y como se
implementaba. Al aplicar la regla de Divide y Vencerás y conocer cómo se implementa en
los sistemas me permitió darme una idea de cómo se pueden ahorrar bastantes líneas de
código y tiempo de desarrollo del programa así como la complejidad de los mismos.
Para los problemas a solucionar cada uno fue programado en un lenguaje de programación
distinto pero la base del algoritmo fue el mismo, para el primer problema la solución se dio
en el lenguaje C, para el segundo problema de multiplicación binaria se resolvió el
problema con C# y para el último y más difícil se tuvo que implementar la solución en
JAVA ya que al desarrollarlo en C# se presentó la problemática que al compilarlo y correrlo
este entraba en un bucle infinito y al implementar la solución en JAVA este ya no
presentaba tal problema.
Fue una experiencia grata el poder desarrollar esta práctica así como desarrollar el código
de la solución a nuestro problema, aunque fue un poco estresante el pensar la solución se
que es buena práctica para desarrollar nuestras habilidades en el análisis de algoritmos.
6
3|ANEXOS
A) PRIMER ALGORITMO
#include<stdlib.h>
#include<conio.h>
#include<time.h>
#include <stdio.h>
#include <string.h>
main(){
int i,j,k,valor2,suma,resultado;
char *primernumero,*segundonumero;
primernumero=(char*)malloc(sizeof(char));//se regresa el puntero que se le
asigno para primernumero
segundonumero=(char*)malloc(sizeof(char));//se regresa el puntero que se le asigno
para segundonumero
for(;;){
resultado=0;
suma=0;
/* Pantalla de inicio. */
system( "COLOR 47" );
printf( "\n\t ****************************************\n" );
printf( "\n\n\t\t ESCUELA SUPERIOR DE COMPUTO\n\n", 224 );
printf( "\t\t ANALISIS DE ALGORITMOS", 214 );
printf( "\n\n\t\t ESCAMILLA CAMACHO JUAN EDUARDO" );
printf( "\n\n\t\t PEREZ PEREZ JOSE");
printf("\n\n\t\t PRODUCTO DE BINARIOS NORMAL \n\n",214);
printf( "\t ****************************************\n\n" );
7
time_t ahora;
struct tm *fecha;
time( &ahora );
fecha = localtime( &ahora );
printf( "\t\t\t\t\t\t FECHA DE HOY( %d/%d/%d )\n" , fecha->tm_mday , fecha-
>tm_mon+1 , fecha->tm_year+1900 );
system( "PAUSE" );
system("cls");
int w=0,i=0;
char aux,aux_1[200];
system("cls");
system("COLOR 47");
printf( "\n\t
**********************************************************\n" );
printf(" \n\n\t\t MULTIPLICACION DE BINARIOS (OJALA ESTE BIEN
=P)\n\n\n",224 );
printf(" \n\n\t\t META NUMEROS EN BINARIOS\n\n\n",214);
printf("\t
**********************************************************\n\n");
system("cls");
printf("COLOCA EL NUMERO BINARIO: \n\n");
scanf("%s",primernumero);
system("cls");
printf("COLOCA EL NUMERO BINARIO: \n\n");
scanf("%s",segundonumero);
system("cls");
//system ("pause");
printf("BIEN EL PRIMER NUMERO QUE INGRESASTE FUE:
%s\n",primernumero);
8
printf("BIEN EL SEGUNDO NUMERO QUE INGRESASTE FUE:
%s\n\n",segundonumero);
if(strlen(segundonumero) <= strlen(primernumero))
{
for(i=strlen(segundonumero)-1,k=0;i<=0;i--,k++){//se iguala i con la longitud de
segundo numero es decir lo que trae en el primer registro
for(j=strlen(primernumero)-1,valor2=0,suma=0;j<=0; j--,valor2++)//se iguala j con
el tamaño de la cadena cuando se ingreso al segundo valor
suma|=( ( (segundonumero[i]&1)&(primernumero[j]&1) )<<valor2 );// como se
trabaja a nivel de bits ocupasmos operacion booleanas
resultado+=(suma<<k);
}
}else{
printf("");
}
for(i=strlen(segundonumero)-1,k=0;i>=0;i--,k++){//se iguala i con la longitud de
segundo numero es decir lo que trae en el primer registro
for(j=strlen(primernumero)-1,valor2=0,suma=0;j>=0; j--,valor2++)//se iguala j con
el tamaño de la cadena cuando se ingreso al segundo valor
suma|=( ( (segundonumero[i]&1)&(primernumero[j]&1) )<<valor2 );// como se
trabaja a nivel de bits ocupasmos operacion booleanas
resultado+=(suma<<k);//primeramente se realiza la multplicacion de numeros
bianrios por medio de la and y se almacena en un arreglo unidemensional cada numero
// entonces el resultado se desplaza a la izquiera y con el operador |= se realiza la
suma con la operacion que salga y nos da el resutlado
// << Este operador binario realiza un desplazamiento de bits a la izquierda. El bit
más significativo (más a la izquierda) se pierde, y se le asigna un 0 al menos significativo
(el de la derecha). El operando derecho indica el número de desplazamientos que se
realizarán.
//Recuérdese que los desplazamientos no son rotaciones; los bits que salen por la
izquierda se pierden, los que entran por la derecha se rellenan con ceros. Este tipo de
desplazamientos
//se denominan lógicos en contraposición a los cíclicos o rotacionales.
//el compilador realiza una conversion automatica de tipo
9
}
printf("\nBIEN EL RESULTADO DE TU MULTIPLICACION ES LA SIGUIENTE:
%d\n",resultado);
system("pause");
}
}/*End main*/
B) SEGUNDO ALGORITMO
public int PDyV (int[]a,int[]b){ //Recibe dos matrices de tipo entero cada una
Operacion o = new Operacion (); //Declaramos un objeto de nuestra clase Operacion que
es donde se encuentran todos nuestros métodos a utilizar
int n,p,q,w;
int[] ai,ad,bi,bd;
n = a.Length; //n toma el tamaño de nuestro primer arreglo
if (a.Length == 1 && b.Length == 1) { //Si ambos arreglos recibidos son de tamaño uno
se multiplican entre si.
return o.productoMatrices(a,b);
} else {
//Se declaran nuestros arreglos en donde se guardaran nuestras divisiones ai, ad y bi y bd
ai = new int[a.Length/2];
ad = new int[a.Length/2];
bi = new int[b.Length/2];
bd = new int[b.Length/2];
ai = o.divide (a, 1);
10
ad = o.divide (a, 2);
bi = o.divide (b, 1);
bd = o.divide (b, 2);
q = Convert.ToInt32 (Math.Pow (2, n));
w = Convert.ToInt32 (Math.Pow (2, n / 2));
//Se retorna la operación final del algoritmo aplicando recursividad con los valores
obtenidos de ai,ad,bi y bd
return (q*PDyV(ai,bi)) + (w*PDyV(ai,bd)) + (w*PDyV(ad,bi)) + (PDyV(ad,bd));
}
}
C) TERCER ALGORITMO
//Se reciben dos String donde se encuentran nuestros valores y la base (2)
int multiplicaBase(String a, String b, int base){
//Declaramos un objeto de nuestra clase Operacion que es donde se encuentran todos
nuestros métodos a utilizar
Operacion o = new Operacion();
String xi,xd,yi,yd,x,y;
int p1,p2,p3,p4,mayor;
if(a.length()==1 && b.length()==1){
{ //Si ambos arreglos recibidos son de tamaño uno se multiplican entre si.
return Integer.parseInt(a,base)*Integer.parseInt(b,base);
}
else{
//Se declaran nuestros arreglos en donde se guardaran nuestras divisions ai,ad y bi y bd
xi=o.divide(a,1);
xd=o.divide(a,2);
11
yi=o.divide(b,1);
yd=o.divide(b,2);
x=Integer.toString((Integer.parseInt(xi, base)+Integer.parseInt(xd, base)),base);
y=Integer.toString((Integer.parseInt(yi, base)+Integer.parseInt(yd, base)),base);
mayor=o.obtenMayor(Integer.parseInt(x,base),Integer.parseInt(y,base));
x=o.acompleta(x,mayor,base);
y=o.acompleta(y,mayor,base);
p1=multiplicaBase(xi,yi,base);
p2=multiplicaBase(xd,yd,base);
p3=multiplicaBase(x, y, base);
//Se retorna la operación final del algoritmo aplicando recursividad con los valores
obtenidos de ai,ad,bi y bd
return ((int)Math.pow(base, a.length())*p1)+((int)Math.pow(base,
a.length()/2)*(p3-p2-p1))+p2;
}
}