/**1. Dos números enteros muy grandes a y b, pueden multiplicarse usando el algoritmo de Karatsuba, que es un ejemplo típico de divide y vencerás:
a = 10^{n/2}*x + y
b = 10^{n/2}*v + w
a*b = 10^{n}*x*v + 10^{n/2}*((x+y)*(v+w)-(x*v+y*w)) + y*w
Supuesto que n es par (Si no redondear n/2 por defecto)
La ventaja del algoritmo es que conlleva sólo 3 multiplicaciones (las multiplicaciones por potencias de 10 se calculan como desplazamientos)
Escribir un algoritmo que tome dos números enteros dados en dos y los multiplique siguiendo el algoritmo dado anteriormente.
Los números se almacenan con su cifra más significativa en el índice más bajo.
En un primer momento considerar que los números son positivos. En una segunda versión incorporar el tratamiento del signo. El signo puede representarse
en la cifra más significativa del número.
*/
import java.util.ArrayList;
public class NumeroGrande {
private ArrayList<Integer> num;
/**
* Calcula y devuelve la multiplicación de this por b usando el algoritmo recursivo de Karatsuba
* @param b: un NumeroGrande
* @return this*b
*/
public NumeroGrande algoritmoMultiplicacionKaratsuba (NumeroGrande b){
int la = this.num.size();
int lb = b.num.size();
// Si las instancias del problema son pequeñas
if (Math.max(la, lb) < 5)
return this.multiplicarNumerosPeques(b);
// Manejo del signo
int sign_a = this.num.get(0).intValue();
int sign_b = b.num.get(0).intValue();
int signoMult = sign_a*sign_b;
this.num.set(0, new Integer(Math.abs(sign_a)));
b.num.set(0, new Integer(Math.abs(sign_b)));
// Dividimos el problema
int med = (int)Math.floor(Math.max(la, lb)/2.0);
NumeroGrande x = this.parteAltaNumeroGrande(med);
NumeroGrande y = this.parteBajaNumeroGrande(med);
NumeroGrande v = b.parteAltaNumeroGrande(med);
NumeroGrande w = b.parteBajaNumeroGrande(med);
NumeroGrande xmasy = x.sumaNumerosGrandes(y);
NumeroGrande vmasw = v.sumaNumerosGrandes(w);
// Hacemos las tres multiplicaciones recursivamente
NumeroGrande xv = x.algoritmoMultiplicacionKaratsuba(v);
NumeroGrande yw = y.algoritmoMultiplicacionKaratsuba(w);
NumeroGrande xmasyvmasw = xmasy.algoritmoMultiplicacionKaratsuba(vmasw);
// Compongo la solución al problema original
NumeroGrande xvmasyw = xv.sumaNumerosGrandes(yw);
NumeroGrande parteMeida = xmasyvmasw.diferenciaNumerosGrandes(xvmasyw);
NumeroGrande parteBaja = componerNumeroGrande(med, parteMeida, yw);
NumeroGrande result= componerNumeroGrande(2*med, xv, parteBaja);
if (signoMult < 0){
result.num.set(result.num.size()-1, new Integer(-1*result.num.get(result.num.size()-1).intValue()));
}
this.num.set(0, new Integer(Math.abs(sign_a)));
b.num.set(0, new Integer(Math.abs(sign_b)));
return result;
}
/**
* Multiplica dos números grandes que tienen menos de 5 cifras cada uno mediante la
* multiplicación de dos int.
* @param b el segundo número a multiplicar
* @return this * b
*
* Precondición: Math.max(this.num.size(),b.num.size()) < 5
*/
private NumeroGrande multiplicarNumerosPeques(NumeroGrande b) {
// TODO Auto-generated method stub
return null;
}
/**
* Devuelve this como un int
* @return es el valor de this como un int
*
* Precondición: this.num.size() < 10
*/
private int toInt() {
// TODO Auto-generated method stub
return 0;
}
/** Devuelve 10^n * parteAlta + parteBaja
*
* @param n
* @param parteAlta
* @param parteBaja
* @return
*/
private static NumeroGrande componerNumeroGrande(int n,
NumeroGrande parteAlta, NumeroGrande parteBaja) {
for (int i=1; i<=n; i++){
parteAlta.num.add(new Integer(0));
}
return parteAlta.sumaNumerosGrandes(parteBaja);
}
/**
* Devuelve la diferencia de dos números grandes
* @param y: un número grande
* @return this - y
*/
private NumeroGrande diferenciaNumerosGrandes( NumeroGrande y) {
// TODO Auto-generated method stub
return null;
}
/**
* Devuelve la suma de dos números grandes.
* @param y un número grande a sumar
* @return this + y
*/
private NumeroGrande sumaNumerosGrandes(NumeroGrande y) {
NumeroGrande s1, s2, suma;
int lx = this.num.size();
int ly = y.num.size();
int dif=0;
if (lx > ly){
s1 = this;
s2 = y;
dif = lx -ly;
} else {
s1 = y;
s2 = this;
dif = ly - lx;
}
int s1size = s1.num.size();
suma = new NumeroGrande();
for (int i=0; i<dif; i++){
suma.num.add(s1.num.get(i));
}
for (int i=dif; i < s1size; i++){
suma.num.add(new Integer(0));
}
int acarreo = 0;
int ults2 = s1size-dif;
for (int i=s1.num.size()-1; i >= dif; i--){
int ss = acarreo + s1.num.get(i) + s2.num.get(ults2 + s1size - i - 2); //el índice de s2 es: ults2 - 1 + s1size - i - 1
acarreo = ss / 10;
suma.num.set(i,new Integer(ss % 10));
}
int i = dif - 1;
while (acarreo == 1 && i>=0){
int ss = acarreo + s1.num.get(i);
acarreo = ss / 10;
suma.num.set(i--,new Integer(ss % 10));
}
if (i == -1 && acarreo == 1){
suma.num.add(0, new Integer(1));
} else {
while (i >=0){
suma.num.add(0,s2.num.get(i));
}
}
return suma;
}
/** Devuelve las med cifras más bajas del NumeroGrande
* @param med un entero positivo
* @return un NumeroGrande igual a las med cifras menos significativas de this
*
* precondición: med >= 0
*/
private NumeroGrande parteBajaNumeroGrande(int med) {
// TODO Auto-generated method stub
NumeroGrande a = new NumeroGrande();
if(med>=0){
for (int i = med; i < this.num.size(); i++) {
a.num.get(i);
}
}
return a;
}
/**
* Devuelve un NumeroGrande quitando las med cifras menos significativas
* @param med un número entero positivo o cero
* @return un NumeroGrande igual a this pero quitándole las med cifras menos significativas
*
* precondición: med >= 0
*/
private NumeroGrande parteAltaNumeroGrande(
int med) {
// TODO Auto-generated method stub
return null;
}
}
a = 10^{n/2}*x + y
b = 10^{n/2}*v + w
a*b = 10^{n}*x*v + 10^{n/2}*((x+y)*(v+w)-(x*v+y*w)) + y*w
Supuesto que n es par (Si no redondear n/2 por defecto)
La ventaja del algoritmo es que conlleva sólo 3 multiplicaciones (las multiplicaciones por potencias de 10 se calculan como desplazamientos)
Escribir un algoritmo que tome dos números enteros dados en dos y los multiplique siguiendo el algoritmo dado anteriormente.
Los números se almacenan con su cifra más significativa en el índice más bajo.
En un primer momento considerar que los números son positivos. En una segunda versión incorporar el tratamiento del signo. El signo puede representarse
en la cifra más significativa del número.
*/
import java.util.ArrayList;
public class NumeroGrande {
private ArrayList<Integer> num;
/**
* Calcula y devuelve la multiplicación de this por b usando el algoritmo recursivo de Karatsuba
* @param b: un NumeroGrande
* @return this*b
*/
public NumeroGrande algoritmoMultiplicacionKaratsuba (NumeroGrande b){
int la = this.num.size();
int lb = b.num.size();
// Si las instancias del problema son pequeñas
if (Math.max(la, lb) < 5)
return this.multiplicarNumerosPeques(b);
// Manejo del signo
int sign_a = this.num.get(0).intValue();
int sign_b = b.num.get(0).intValue();
int signoMult = sign_a*sign_b;
this.num.set(0, new Integer(Math.abs(sign_a)));
b.num.set(0, new Integer(Math.abs(sign_b)));
// Dividimos el problema
int med = (int)Math.floor(Math.max(la, lb)/2.0);
NumeroGrande x = this.parteAltaNumeroGrande(med);
NumeroGrande y = this.parteBajaNumeroGrande(med);
NumeroGrande v = b.parteAltaNumeroGrande(med);
NumeroGrande w = b.parteBajaNumeroGrande(med);
NumeroGrande xmasy = x.sumaNumerosGrandes(y);
NumeroGrande vmasw = v.sumaNumerosGrandes(w);
// Hacemos las tres multiplicaciones recursivamente
NumeroGrande xv = x.algoritmoMultiplicacionKaratsuba(v);
NumeroGrande yw = y.algoritmoMultiplicacionKaratsuba(w);
NumeroGrande xmasyvmasw = xmasy.algoritmoMultiplicacionKaratsuba(vmasw);
// Compongo la solución al problema original
NumeroGrande xvmasyw = xv.sumaNumerosGrandes(yw);
NumeroGrande parteMeida = xmasyvmasw.diferenciaNumerosGrandes(xvmasyw);
NumeroGrande parteBaja = componerNumeroGrande(med, parteMeida, yw);
NumeroGrande result= componerNumeroGrande(2*med, xv, parteBaja);
if (signoMult < 0){
result.num.set(result.num.size()-1, new Integer(-1*result.num.get(result.num.size()-1).intValue()));
}
this.num.set(0, new Integer(Math.abs(sign_a)));
b.num.set(0, new Integer(Math.abs(sign_b)));
return result;
}
/**
* Multiplica dos números grandes que tienen menos de 5 cifras cada uno mediante la
* multiplicación de dos int.
* @param b el segundo número a multiplicar
* @return this * b
*
* Precondición: Math.max(this.num.size(),b.num.size()) < 5
*/
private NumeroGrande multiplicarNumerosPeques(NumeroGrande b) {
// TODO Auto-generated method stub
return null;
}
/**
* Devuelve this como un int
* @return es el valor de this como un int
*
* Precondición: this.num.size() < 10
*/
private int toInt() {
// TODO Auto-generated method stub
return 0;
}
/** Devuelve 10^n * parteAlta + parteBaja
*
* @param n
* @param parteAlta
* @param parteBaja
* @return
*/
private static NumeroGrande componerNumeroGrande(int n,
NumeroGrande parteAlta, NumeroGrande parteBaja) {
for (int i=1; i<=n; i++){
parteAlta.num.add(new Integer(0));
}
return parteAlta.sumaNumerosGrandes(parteBaja);
}
/**
* Devuelve la diferencia de dos números grandes
* @param y: un número grande
* @return this - y
*/
private NumeroGrande diferenciaNumerosGrandes( NumeroGrande y) {
// TODO Auto-generated method stub
return null;
}
/**
* Devuelve la suma de dos números grandes.
* @param y un número grande a sumar
* @return this + y
*/
private NumeroGrande sumaNumerosGrandes(NumeroGrande y) {
NumeroGrande s1, s2, suma;
int lx = this.num.size();
int ly = y.num.size();
int dif=0;
if (lx > ly){
s1 = this;
s2 = y;
dif = lx -ly;
} else {
s1 = y;
s2 = this;
dif = ly - lx;
}
int s1size = s1.num.size();
suma = new NumeroGrande();
for (int i=0; i<dif; i++){
suma.num.add(s1.num.get(i));
}
for (int i=dif; i < s1size; i++){
suma.num.add(new Integer(0));
}
int acarreo = 0;
int ults2 = s1size-dif;
for (int i=s1.num.size()-1; i >= dif; i--){
int ss = acarreo + s1.num.get(i) + s2.num.get(ults2 + s1size - i - 2); //el índice de s2 es: ults2 - 1 + s1size - i - 1
acarreo = ss / 10;
suma.num.set(i,new Integer(ss % 10));
}
int i = dif - 1;
while (acarreo == 1 && i>=0){
int ss = acarreo + s1.num.get(i);
acarreo = ss / 10;
suma.num.set(i--,new Integer(ss % 10));
}
if (i == -1 && acarreo == 1){
suma.num.add(0, new Integer(1));
} else {
while (i >=0){
suma.num.add(0,s2.num.get(i));
}
}
return suma;
}
/** Devuelve las med cifras más bajas del NumeroGrande
* @param med un entero positivo
* @return un NumeroGrande igual a las med cifras menos significativas de this
*
* precondición: med >= 0
*/
private NumeroGrande parteBajaNumeroGrande(int med) {
// TODO Auto-generated method stub
NumeroGrande a = new NumeroGrande();
if(med>=0){
for (int i = med; i < this.num.size(); i++) {
a.num.get(i);
}
}
return a;
}
/**
* Devuelve un NumeroGrande quitando las med cifras menos significativas
* @param med un número entero positivo o cero
* @return un NumeroGrande igual a this pero quitándole las med cifras menos significativas
*
* precondición: med >= 0
*/
private NumeroGrande parteAltaNumeroGrande(
int med) {
// TODO Auto-generated method stub
return null;
}
}