tut -suma de n numeros de d digitos -pe13

Anuncio
Enunciado
Obtenga los primeros diez dígitos de la suma de los siguientes cien números de 50 dígitos.
[Fuente: projecteuler.net]
Solución
Cada uno de estos números es muy muy largo para almacenarlo en una variable entera en C, por lo que los trataremos como cadenas de
caracteres y los manipularemos dígito a dígito, dando la suma como otra cadena de caracteres. Esto nos lleva a redactar una función que
“sume a mano” dos cadenas de dígitos tratados como cadenas de caracteres.
Cuando hacemos a mano una suma, los dígitos menos significativos nos quedan a la derecha y por ello cada número lo invertiremos antes
de sumarlo, de manera que al invertirlos los podemos sumar de izquierda a derecha, es decir, desde la posición cero hasta la final, y no al
revés, que sería más complicado. He aquí una función que invierte una cadena de caracteres.
void invierte(char c[])
{
// invierte a c (asume string.h)
int s, b = strlen(c) - 1, m = (b+1)/2;
char t; // temporal, para intercambio
// sube, baja, mitad
for (s = 0; s < m; s++)
{
t = c[s];
c[s] = c[b];
c[b--] = t;
}
}
La función invierte() va tomando un caracter del extremo izquierdo y otro del derecho y los intercambia hasta llegar al centro. Por eso la
cantidad de pasos del ciclo for es sólo la mitad de la longitud de la cadena que llega. Note que los valores de s empiezan en cero y terminan
poco antes de la mitad, mientras que los de b empiezan en la última posición y y también terminan en la mitad.
En realidad, si la cantidad de caracteres de la cadena a invertir es impar, el carácter del centro no llega ser apuntado y se queda intacto, lo
cual es deseable, porque no es necesario intercambiarlo consigo mismo, por supuesto. Consideremos ahora una función que “suma” dos
cadenas de dígitos que se suponen han sido ya invertidas.
void suma(char o[], char f[])
{
// suma “a mano” la cadena de dígitos f (fuente) a o (objetivo)
// asume que la longitud de f <= que la de o (asume string.h)
char d[] = "0123456789"; // dígitos para convertir de int a char
int p, d1, d2; // posición en f o en o, dígitos actuales en f y o, suma
int s, r = 0; // suma de d1 y d2, incluyendo posible rebosamiento, rebosamiento
for (p = 0; p < strlen(f); p++)
{
d1 = f[p] - '0';
d2 = o[p] - '0';
s = (d1 + d2 + r) % 10;
r = (d1 + d2 + r) / 10;
o[p] = d[s];
}
for ( ;
{
d1 =
s =
r =
o[p]
p < strlen(o); p++)
o[p] - '0';
(d1 + r) % 10;
(d1 + r) / 10;
= d[s];
}
if (r > 0) o[p++] = d[r];
o[p] =
'\0';
}
Se empieza por declarar el arreglo d y colocar en él los dígitos decimales (0 a 9) para luego usarlo una vez obtenido el valor numérico de la
suma de los dos dígitos actuales, incluyendo el rebosamiento, y obtener el carácter correspondiente al dígito de las unidades de esta suma.
Note que el carácter correspondiente a cada dígito está precisamente en la posición que corresponde a su valor numérico.
La variable p sirve para recorrer simultáneamente las cadenas f y o, hasta agotar la longitud de o con el primer ciclo for, y luego concluir el
recorrido de o con el segundo for. Es obvio que si las dos cadenas son de igual longitud el segundo ciclo no se ejecuta. d1 y d2 sirven para
obtener los valores numéricos de los dos caracteres dígitos actuales en f y o, respectivamente.
En la variable se asigna el dígito de las unidades de la suma de d1 y d2, incluyendo el rebosamiento, porque se toma el residuo de esta
suma con respecto a 10. El rebosamiento, r, recibe el dígito de las decenas (cero si no le hay) porque toma el cociente de la suma de d1, d2
y r. Note cómo el carácter correspondiente a s es tomado del arreglo d y colocado en la cadena o en la posición p.
El segundo ciclo for termina de asegurar que el rebosamiento quede aplicado a lo que pueda quedar dela cadena o, incluyendo el último
posible rebosamiento que podría quedar suelto. Finalmente se asegura que la cadena o tenga su terminador nulo para que se pueda
calcular su longitud correctamente en la próxima llamad a la función o se la vaya a desplegar con printf().
La función que realice la suma de los cien números largos en una matriz de caracteres de dos dimensiones.
void sumaTodo(char s[], int cf, int cc, char m[][cc])
{
// suma todos los enteros (como cadenas de dígitos) en la matriz m
// que tiene cf filas; el resultado se retorna en s
int f; // fila actual en m
strcpy(s, m[0]);
invierte(s);
for (f = 1; f < cf; f++)
{
invierte(m[f]);
suma(s, m[f]);
}
invierte(s);
}
Se recibe una matriz de caracteres, la cantidad de cf filas y de columnas cc, la matriz m, indicando que es de dos dimensiones y que el
número de columnas por filas es precisamente cc. Cada fila contiene uno de los “números” a sumar. La suma se retorna en s, un arreglo de
una dimensión. En la función se declara el contador f para recorrer las filas de la tabla.
Se copia el primer número en s y se invierte (porque se asume que la suma se hará con números invertidos) . Sigue un ciclo en el que se
recorre el resto de las filas, invirtiendo cada número y sumándolo, acumulando en s la suma. Antes de salir, se invierte la suma para
obtener el número orientado correctamente.
Es claro la respuesta a nuestro enunciado son los primeros diez dígitos de la cadena s retornada por la función sumaTodo. Una función que
despliegue los primeros p caracteres en una cadena podría ser.
void desPriCar(char c[], int p)
{
// despliega los primeros p caracteres en c
int k; // posición actual en c
for (k = 0; f < p; k++)
printf("%c", c[k]);
}
Es obvio que este algoritmo pierde un tiempo invirtiendo cada sumando antes de sumarlo, pero en este caso son pocos números y no hay un
mucho overhead. Para una cantidad muy grande de valores a sumar se podría realizar la suma sin invertir los sumandos, de derecha a
izquierda, lo cual no es muy difícil en realidad. Pero no se discute aquí.
Es claro que para ver que este código funciona hay que redactar una función main() en la cual declarar una matriz de caracteres de dos
dimensiones y colocar en ella en ellas los “números” a sumar y otra de una dimensión para albergar la suma. Invocar la función sumaTodo()
enviándole la cadena para la suma, el número de filas, el número de columnas y la matriz con los “números” a sumar.
Luego hay que invocar a desPriCar() con el número de caracteres que deseamos desplegar, en este caso 10. Note que como ya la suma está
invertida, los primeros diez dígitos buscados están precisamente al principio de la suma.
ecabrera, 2011, octubre
Descargar