Arreglos 2

Descargar como pdf o txt
Descargar como pdf o txt
Está en la página 1de 17

Arreglos

Los arreglos son variables que sirven para almacenar valores de un conjunto, por
ejemplo las calificaciones de un semestre. En la utm, cada materia se califica con 4
exámenes, podemos usar un arreglo para almacenar dichos valores y luego calcular la
calificación del semestre.

La regla es que todos los valores tienen que ser del mismo tipo y estar
relacionados entre sí. Más tarde en el curso se expondrán formas más avanzadas de
almacenar información.

Consideremos el siguiente conjunto de datos:

cal = { 7.3, 6.5, 8.2, 7.4 }

En lenguaje C se tiene que declarar la variable para almacenar los datos en este
caso identificamos que los datos tienen punto decimal por lo que el tipo tiene que ser
float o double, enseguida nos damos cuenta que el tamaño del conjunto es de 4
elementos. Con la información anterior nos damos cuenta que la mejor forma de
almacenar dichos valores es por medio de un arreglo tal como se muestra a
continuación:

float cal[4];

La sintaxis de forma general es: tipo nombre[tamaño]; donde tipo pueden ser
cualquiera de los tipos reconocidos por lenguaje C como short, int, long, float, double
o char. El nombre sigue las mismas reglas que los nombres de las variables y debemos
recordar que siempre es bueno usar un nombre que nos signifique algo o nos de una
pista de lo que vamos a guardar en el arreglo. Por último, tenemos el tamaño que es el
número máximo de elementos que podemos almacenar en el arreglo. En el ejemplo, el
tipo es float porque los datos son números con punto decimal, el nombre es cal para
que nos recuerde que son calificaciones, y el tamaño es 4 porque es la cantidad de
elementos del conjunto.

El siguiente código muestra como usar un arreglo en un programa:

#include <stdio.h> //todos los programas fueron probados en netbeans


#include <stdlib.h>

int main(int argc, char** argv){


//arreglo, promedio parciales, calificacion del semestre
float cal[4], promPar, calSem;
int i;

//ciclo para leer las calificaciones


for(i=0; i<4; i++){
printf("Ingresa la calificacion %d: ", i+1);
scanf("%f", &cal[i]);
}
//Primero se calcula el promedio de los 3 parciales
promPar = (cal[0] + cal[1] + cal[2])/3.0;

//Se calcula el promedio de los parciales con la calificación del


//ordinario
calSem = (promPar + cal[3])/2.0;

//Se imprime la calificación con un solo decimal


printf("La calificación del semestre es: %.1f\n", calSem);

return 0;
}

Programa 1. Archivo calificaciones.c

En el programa anterior se han declarado 4 variables las tres primeras de tipo


float con el uso que se indica en los comentarios. Luego una variable entera que se
usa como contador para el ciclo for y también como indice del arreglo. El programa
inicia solicitando al usuario los datos de las calificaciones dentro del ciclo, deben
notar que el contador y por lo tanto el primer indice del arreglo es cero, esta es una
regla de lenguaje C. El ciclo tiene por condición de control i<4, lo que significa que el
último valor de i es 3 y esto corresponde al último elemento del arreglo. La regla de
lenguaje C dice que los indices de un arreglo inician en cero y hasta el valor del
tamaño declarado menos uno, en este caso se declaro un arreglo de 4 elementos y por
lo tanto el último elemento tiene un indice de 4-1 = 3. Observen que para que el
usuario no se confunda se ha impreso el valor de i+1 para que pida las calificaciones 1,
2, 3 y 4; en lugar de 0, 1, 2 y 3. Cuando se leen los valores desde el teclado con scanf
la instrucción es scanf(“%f”, &cal[i]); donde los valores de i van cambiando de 0
a 3 conforme se ejecuta el ciclo y almacena cada valor en un lugar distinto dentro del
arreglo, esto es muy importante porque deben recordar que una variable solo puede
almacenar un valor y el valor anterior se pierde cada guardamos un valor nuevo en el
mismo lugar. Esto quiere decir que hemos declarado 4 variables en el arreglo que se
distinguen por medio de un indice cal[0], cal[1], cal[2] y cal[3].

El programa 1 es muy simple porque puede darles la impresión de que puedo


declarar las variables como cal1, cal2, cal3 y cal4. Pero si hicieran eso, no podrían
leer los datos por medio del ciclo como se hizo en el ejemplo. Cuando son pocos
elementos realmente no se nota la diferencia, pero piensen en el caso de tener miles de
datos.
En matemáticas es común usar subindices cuando se trabaja con conjuntos por
ejemplo consideremos el conjunto x = {x1, x2, x3, ..., xn} y cuando queremos calcular
 n 
el promedio se escribe en matemáticas como una sumatoria. x =   xi  / n esto
 i =1 
quiere decir que debemos sumar todos los elementos del conjunto x desde el primero
x1 hasta el último xn y luego dividir entre el número de elementos del conjunto que es
n. En lenguaje C podemos hacer lo siguiente usando un arreglo:

#define n 10000
int x[n], i;
float suma, promedio;
suma = 0.0;//iniciar la suma en cero
for(i=0; i<n; i++) suma = suma + x[i]; //sumar cada elemento
promedio = suma / n;

aquí el arreglo tiene un tamaño de 10,000 que es valor definido para n. Dense cuenta
que en lugar de tener una formula enorme que suma todos los valores, se usa un
acumulador y un ciclo para ir sumando un elemento del arreglo a la vez.

Una vez que tenemos almacenados los datos en el arreglo podemos hacer varias
cosas como buscar un elemento en particular y averiguar la posición del arreglo en la
que se encuentra, o bien, saber cual es el valor más grande o más pequeño. Noten que
los ciclos for y los arreglos están muy relacionados, ya que puede usarse un ciclo de
este tipo para procesar los datos que se encuentren en el arreglo sin importar cuantos
son con la condición que se tenga memoria suficiente en la computadora.

Por ejemplo para saber cual es el numero menor en el arreglo anterior, primero
debemos saber o suponer ciertas cosas. Por ejemplo, puedo suponer que el elemento
en la posición cero es el menor y compararlo con el resto y actualizar la información
cada que encuentre un número más pequeño que el último que encontré.

menor = x[0];

for(i=1; i<n; i++) if(x[i] < menor) menor = x[i];

//si quiero imprimir los valores en el arreglo puedo escribir


printf(“el elemento %d es %d\n”, i, x[i]);

Ejercicio 1

Escriba un programa en C que almacene las temperaturas en grados centigrados


de cada una de las 24 horas de un día, calcule la temperatura promedio y encuentre las
temperaturas mayor y menor de ese día.

Arreglos y funciones
En la unidad anterior se les enseño a usar funciones y a manejar parámetros tanto
por valor como por referencia. En C todos los arreglos se pasan como parámetros por
referencia, regularmente además del arreglo se acostumbra usar un parámetro que
indique el tamaño del arreglo para poder manipularlo correctamente. En el siguiente
ejemplo se ha modificado el programa 1 para que exista una función que calcule el
promedio del arreglo.

#include <stdio.h>
#include <stdlib.h>
//prototipos de las funciones
void leerDatos(float *c, int t);
void imprimirDatos(float *c, int t);
float promedio(float *c, int t);

int main(int argc, char** argv) {

float cal[4], promPar, calSem;


leerDatos(cal, 4);

//calcula el promedio de los 3 parciales


promPar = promedio(cal, 3);
printf("El promedio de los parciales es: %.1f\n", promPar);

//calcula la calificacion del semestre


calSem = (promPar + cal[3]) / 2.0;
printf("La calificacion del semestre es: %.1f\n", calSem);

//muestra las calificaciones leidas


imprimirDatos(cal, 4);

return (EXIT_SUCCESS);
}

void leerDatos(float *c, int t){


int i;

for(i=0; i<t; i++){


printf("c[%d] = ", i);
scanf("%f", &c[i]);
}
}

void imprimirDatos(float *c, int t){


int i;

for(i=0; i<t; i++) printf("c[%d] = %f\n", i, c[i]);


}

float promedio(float *c, int t){


float suma = 0.0;
int i;

for(i=0; i<t; i++) suma = suma + c[i];

return suma / t;
}

Programa 2. Usando funciones con arreglos

Como puede observarse del programa anterior los prototipos de las funciones
tienen dos parámetros float *c que es el arreglo e int t que es el tamaño del arreglo.
La implementación de las funciones se muestra después de la función principal. En la
implementación podemos ver que se sigue usando la notación de los corchetes [] para
indicar el indice del arreglo, es decir, usamos c[i] que quiere decir el elemento i del
arreglo c. Dentro de la función principal mandamos a llamar las funciones, primero se
manda a llamar leerDatos(cal, 4), que significa que estamos usando el arreglo cal
declarado en dicha función, el 4 es el tamaño del arreglo cal.

Si tenemos más de un arreglo como en el siguiente fragmento de código, se


muestran cuales serían las llamadas de las funciones:
float calProgra[4], calCalculo[4], calFilosofía[4];

leerDatos(calProgra, 4);
leerDatos(calCalculo, 4);
leerDatos(calFilosofía, 4);

Si alguno de los arreglos fuera más grande, simplemente ponemos el tamaño como
segundo parámetro.

Ahora consideren un programa más interesante que encuentra los primeros 100
números primos usando un algoritmo que usa la descomposición en factores primos
para probar si un número es primo o no. Todos los números primos con excepción del
dos son impares, por esa razón el programa solo prueba con los impares. El ciclo de la
función tiene dos condiciones de parada, si el número es divisible por alguno de los
primos menores a él entonces no es primo, la segunda condición dice que basta con
probar con los números primos menores a la raíz cuadrada de el número que
queremos saber si es primo o no. El ciclo por seguridad no busca en el arreglos
elementos con un indice mayor a 99, por que eso genera un error en tiempo de
ejecución que se llama violación de acceso a la memoria y provoca que el programa
se detenga y hacer que el sistema operativo se congele.

#include <stdio.h>
#include <stdlib.h>

#define FALSO 0
#define VERDADERO 1

int esPrimo(int x, int *p);

int main(int argc, char** argv) {


int primos[100], x, i;
primos[0] = 2;//los 3 primeros números primos
primos[1] = 3;
primos[2] = 5;

//buscamos los siguientes números a partir del 7


for(i=3, x=7; i < 100; x+=2)
if(esPrimo(x, primos)) primos[i++] = x;

for(i=0; i < 100; i++)//imprime la lista de primos


printf("Primos[%d] = %d\n", i, primos[i]);

return (EXIT_SUCCESS);
}

//la funcion devuelve falso o verdadero según la condición


int esPrimo(int x, int *p){
int i;

for(i=0; i < 100; i++){


//si es divisible por otro no es primo
if((x % p[i]) == 0) return FALSO;
//si ya se probo con los menores a la raiz de x es primo
if((p[i]*p[i]) > x) return VERDADERO;//
}

//para evitar una advertencia si sale del ciclo también es primo


return VERDADERO;
}

Programa 3. Encontrando los primeros 100 números primos.

Ejercicio 2

Modifique el ejercicio 1 usando 3 funciones, una que calcule el promedio, otra que
devuelva la temperatura menor y la última que devuelva la temperatura mayor. Los
prototipos de las funciones son: float promedio(float *a, int t); float menor(float *a,
int t); y float mayor(float *a, int t). Nota: puede usar otras dos funciones para leer los
datos y otra para imprimir la lista de temperaturas.

Arreglos de más de una dimensión


En ocasiones es necesario almacenar datos en forma tabular o de matrices, en
matemáticas se acostumbra usar dos indices para identificar un elemento particular en
la tabla. Estos indices indican la columna y el renglón o fila, existen muchas
aplicaciones para las matrices y a continuación se mostrará la forma de trabajar con
ellas en lenguaje C. Una matriz en C se puede representar como un arreglo de dos
dimensiones y la sintaxis es tipo nombre[renglón][columna]; donde tipo sigue siendo
alguno de los tipos primitivos de C como int o float, el nombre corresponde al nombre
que se le quiera dar a la variable y renglón y columna son los indices. Al igual que
con los arreglos de una dimensión los indices inician en 0 y hasta el tamaño menos
uno. Por ejemplo, para declarar una matriz de enteros que tenga 3 renglones y 2
columnas tenemos: int matriz[3][2];

En el siguiente programa se muestra como hacer algunas operaciones básicas con


matrices, como leer la matriz, imprimir y la suma de matrices.
#include <stdio.h>
#include <stdlib.h>

int main(int argc, char** argv) {

int matriz1[3][2], matriz2[3][2], matriz3[3][2];


int i, j;

printf("Ingresa los datos de la primera matriz\n");


for(i=0; i<3; i++)
for(j=0; j<2; j++){
printf("m1[%d][%d] = ", i, j);
scanf("%d", &matriz1[i][j]);
}

printf("Ingresa los datos de la segunda matriz\n");


for(i=0; i<3; i++)
for(j=0; j<2; j++){
printf("m2[%d][%d] = ", i, j);
scanf("%d", &matriz2[i][j]);
}
//hace la suma de las matrices
for(i=0; i<3; i++)
for(j=0; j<2; j++)
matriz3[i][j] = matriz1[i][j] + matriz2[i][j];

printf("La suma de la matrices uno y dos es: \n");


for(i=0; i<3; i++){
for(j=0; j<2; j++)
printf("%d ", matriz3[i][j]);
printf("\n");
}

return (EXIT_SUCCESS);
}

Programa 3. Operaciones básicas con matrices

Como se puede notar del ejemplo anterior, las matrices siguen reglas muy
parecidas a un arreglo y debido que es una estructura tabular en donde cada renglón i,
tiene j columnas, entonces se necesita un ciclo que vaya cambiando de renglón y otro
independiente que se encargue de las columnas. Esto puede verse si se ejecuta el
código anterior, cuando pide los datos es posible ver que primero pide los datos del
primer renglón que corresponden a m1[0][0] y m1[0][1], sigue con el segundo
renglón que es m1[1][0] y m1[1][1], por último m1[2][0] y m1[2][1] que es el tercer
renglón de la matriz 1. Hace algo similar al pedir los datos de la segunda matriz y
también para encontrar la suma que se almacena en la matriz 3. A la hora de imprimir
lo hace renglón por renglón para visualizar la forma en la que se organizan los datos.
Deben darse cuenta que cada indice del arreglo usa su propio par de corchetes y esa es
una regla de lenguaje C, y también que el primer renglón tiene el indice 0 al igual que
la primer columna por eso el elemento que esta en el primer renglón y la primera
columna tiene los indices [0][0].

Ejercicio 3

Investigar como se hace la resta y la multiplicación de matrices y hacer un


programa que haga dichas operaciones. Su programa debe comprobar que las
condiciones respecto al tamaño de las matrices se cumplen de tal manera que se
pueden realizar las operaciones. Por ejemplo, en el caso del programa 3, la suma
puede hacerse solo sí las matrices tienen el mismo numero de renglones y de
columnas entre sí. En este caso, ambas son matrices de 3x2, es decir ambas tienen 3
renglones y 2 columnas. No se puede sumar una matriz de 2x2 con una matriz de 2x3
porque aunque coinciden en el número de renglones no coinciden en el número de
columnas.
Los libros de álgebra lineal tratan sobre propiedades y operaciones con matrices,
las aplicaciones en ingeniería son muchas, por ejemplo se pueden usar para resolver
sistemas de ecuaciones lineales. La imágenes por computadora se representan como
matrices de puntos. Algunas operaciones muy conocidas son la transpuesta, el
determinante, la rotación, etc.

Arreglos de más de dos dimensiones y funciones


Muchas veces se acostumbra almacenar la información en forma de tablas, por
ejemplo, si se quisiera almacenar las temperaturas de cada hora por cada día de la
semana se podría usar una matriz de 24x7 donde los renglones representarían las
horas y las columnas los días de la semana. Esta misma idea puede extenderse a 3
dimensiones si queremos hacer lo mismo para todos los dias del año organizados por
meses, se usaría un arreglo de 24x31x12, donde el primer indice serviría para las
horas, el segundo para los días del mes y el último para los meses. En este último
ejemplo, se debe considerar que no todos los meses tienen 31 días y en este caso tiene
que usarse como regla el tamaño máximo porque no se pueden tener diferentes
dimensiones para cada indice, es decir, no hay forma de decirle a lenguaje C que la
segunda dimensión es variable porque hay meses con 28, 30 y 31 días. Aunque se
desperdicia memoria todos los meses se declaran de 31 días, esto quiere decir que
para el mes de febrero se desperdicia el espacio que corresponde a tres días. Las
declaraciones de memoria serían float tempSemanal[24][7]; y float
tempAnual[24][31][12];

Recuerde la regla anterior cuando al resolver un problema no se tenga


información de cuantos datos se van a almacenar, aquí aplica aquello de más vale que
sobre a que falte. Por ejemplo, suponga que se quieren tener matrices de diferente
tamaño pero no se sabe hasta que se le pregunte durante la ejecución al usuario de que
tamaño serán, lo único que sabemos es que máximo serán 100 renglones y 100
columnas, entonces podríamos tener los siguiente:
#include <stdio.h>
#include <stdlib.h>

#define MAX_REN 100


#define MAX_COL 100

void leerMatriz(int m[][MAX_COL], int r, int c);


void imprimirMatriz(int m[][MAX_COL], int r, int c);
void sumaMatrices(int a[][MAX_COL], int renA, int colA,
int b[][MAX_COL], int renB, int colB,
int c[][MAX_COL], int *renC, int *colC);

int main(int argc, char** argv) {

int A[MAX_REN][MAX_COL], B[MAX_REN][MAX_COL], C[MAX_REN][MAX_COL];


int renA, colA, renB, colB, renC, colC;

//se piden los datos de la primera matriz


printf("Cuantos renglones tiene la matriz A? ");
scanf("%d", &renA);
printf("Cuantas columnas tiene la matriz A? ");
scanf("%d", &colA);
leerMatriz(A, renA, colA);
printf("La primer matriz es:\n");
imprimirMatriz(A, renA, colA);

//se piden los datos de la segunda matriz


printf("Cuantos renglones tiene la matriz B? ");
scanf("%d", &renB);
printf("Cuantas columnas tiene la matriz B? ");
scanf("%d", &colB);
leerMatriz(B, renB, colB);
printf("La segunda matriz es:\n");
imprimirMatriz(B, renB, colB);

//sumar A con B y el resultado en C


printf("La suma de las matrices es:\n");
sumaMatrices(A, renA, colA, B, renB, colB, C, &renC, &colC);
imprimirMatriz(C, renC, colC);

return (EXIT_SUCCESS);
}

void leerMatriz(int m[][MAX_COL], int r, int c){


int i, j;

for(i=0; i < r; i++)


for(j=0; j < c; j++){
printf("m[%d][%d]? ", i, j);
scanf("%d", &m[i][j]);
}
}

void imprimirMatriz(int m[][MAX_COL], int r, int c){


int i, j;

printf("{ ");
for(i=0; i < r; i++){
printf("{ ");
for(j=0; j < c; j++){
printf("%d", m[i][j]);
if(j < (c-1)) printf(", ");
else printf(" }");
}
if(i < (r-1)) printf("\n");
}
printf(" }\n");
}

void sumaMatrices(int a[][MAX_COL], int renA, int colA,


int b[][MAX_COL], int renB, int colB,
int c[][MAX_COL], int *renC, int *colC){
int i, j;

if((renA == renB) && (colA == colB)){


*renC = renA;
*colC = colA;
for(i=0; i < renA; i++)
for(j=0; j < colA; j++)
c[i][j] = a[i][j] + b[i][j];
}else printf("No se puede hacer la suma\n");
}

Programa 4. Operaciones con matrices

Los programas con funciones tienen muchas ventajas, se puede resolver cada
parte del programa por separado y lo más importante se pueden llegar a probar los
módulos o funciones por separado hasta estar seguros que funcionan sin problemas.
Para ello es necesario tener disciplina y buenas prácticas de programación, eso
incluye que el diseño de los programas debe hacerse pensando en subdividir un
problema grande en problemas más pequeños y hacer que las soluciones de cada una
de ellas sean lo más independientes posible. La independencia permite probar por
separado los módulos y hacer que los errores queden contenidos dentro de cada
módulo, esto se consigue usando de forma inteligente el paso de paramétros y
variables locales siempre que sea posible, con la experiencia se da uno cuenta que en
muy pocos casos se justifica usar variables globales y aún así deberíamos evitar
usarlas.

Las funciones de lectura e impresión de las matrices en el programa 4, se usan


más de una vez y solo se tienen que cambiar los paramétros. En C, las matrices se
declararan como paramétros indicando el tamaño de memoria del último indice como
en la lectura void leerMatriz(int m[][MAX_COL], int r, int c);, donde el primer
par de corchetes se pone vacío y en el segundo se indica que el tamaño de las matrices
que va a recibir tiene MAX_COL como segundo indice. Otra opción es usar void
leerMatriz(int *m[MAX_COL], int r, int c);, donde el asterisco hace la misma
función de los corchetes vacíos. Esta regla es muy importante porque de no seguirla
se genera un error de sintaxis y el programa no funciona.

Las matrices se declararon de máximo 100 renglones y 100 columnas como en


A[MAX_REN][MAX_COL], sin embargo, en la función principal se le solicita al usuario
que indique cual es tamaño de las matrices que quiere ingresar los datos y luego se
envia dicha información a la función de lectura al hacer la llamada. Deberíamos
advertir y validar que el usuario ingrese matrices de 2x2 hasta 100x100, con cualquier
combinación de renglones y columnas como 2x3, 7x5, etc. Este diseño de nuestro
programa tiene la flexibilidad de usar cualquier matriz que cumpla con los requisitos
máximos de tamaño que hemos definido.

Ejercicio 4

Escriba un programa que haga sumas, restas y multiplicaciones de matrices.


Considere las reglas de tamaño que dicen que para hacer sumas y restas, las matrices
deben coincidir en los renglones y en las columnas; es decir, a[3][2] se puede sumar
con b[3][2] pero no con b[2][2]. En el caso de las multiplicaciones, la regla dice que
debe coincidir las columnas de la primera matriz con los renglones de la segunda
matriz como en a[2][3] se puede multiplicar con b[3][5] y el resultado sería c[2][5].
La expresión matemática para la multiplicación de matrices es
n
Ci , j =  Ai , k * Bk , j para la multiplicacion de una matriz A[m][n] con una matriz
k =1
B[n][z] y dará como resultado una matriz C[m][z]. Básicamente quiere decir que se
toma un renglón de A y una columna de B y se suma la multiplicacion de los
elementos del renglón con los elementos de la columna, uno a uno, es decir el primero
con el primero, el segundo con el segundo, y así sucesivamente.

Regresando a los ejemplos de la temperatura semanal podríamos los promedio de


cada día y guardarlos en un arreglo:

float tempSemanal[24][7];
float suma, promedios[7];
int dia, hora;

for(dia = 0; dia < 7; dia++){


suma = 0.0;
for(hora = 0; hora < 24; hora++)
suma = suma + tempSemanal[hora][dia];
promedios[dia] = suma / 24.0;
}
Se puede hacer algo similar para encontrar día más caluroso del año con el
siguiente código:

float tempAnual[24][31][12], tMax;


int hora, dia, mes, hMasCal, dMasCal, mMasCal;

hMasCal = dMasCal = mMasCal = 0;


tMax = tempAnual[0][0][0];
for(mes = 0; mes < 12; mes++)
for(dia = 0; dia < 31; dia++)
for(hora = 0; hora < 24; hora++){
if(tempAnual[mes][dia][hora] > tMax){
tMax = tempAnual[mes][dia][hora];
hMasCal = hora;
dMasCal = dia;
mMasCal = mes;
}
}

En el primer fragmento de código se pueden imprimir los promedios o usarlos


para encontrar en promedio cuál día de la semana fue el más caluroso y que
temperatura. En el segundo, se puede saber cuál fue la temperatura máxima, y en que
día, mes y hora ocurrió. Si supieramos que este dato pudo ocurrir más de una vez, se
pueden repetir los tres ciclos pero ahora que ya sabemos cuál fue la temperatura, se
pueden buscar los días, meses y horas en que esto ocurrió. Aquí no se ha usado el
conocimiento de que meses tienen 28, 30 o 31 días para validar el programa de tal
manera que no busque datos equivocados por ejemplo un día mayor a 28 en febrero.

A continuación les presento un fragmento de código con el que se puede resolver


el problema de tener que ciclos que son controlados por la cantidad de días de cada
mes. También se presenta la forma de inicializar un arreglo y una matriz por medio de
la notación de llaves.
#include <stdio.h>
#include <stdlib.h>

//se declara global para usarse en todas las funciones


int diasXMes[12] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
int m[2][2] = { { 1, 2}, { 3, 4} };

int main(int argc, char** argv) {


float tempAnual[12][31][24];//mes, dia, hora
int mes, dia, hora;

for(mes = 0; mes < 12; mes++){


printf("El mes %d tiene %d dias\n", mes+1, diasXMes[mes]);
for(dia = 0; dia < diasXMes[mes]; dia++){
//printf("Hoy es el dia %d\n", dia+1);
for(hora = 0; hora < 24; hora++);
//aquí puede ponerse el código para leer los datos
}
}

return (EXIT_SUCCESS);
}
Arreglos de caracteres y manejo de texto en C
En lenguaje C, los datos de texto se manejan por medio de códigos ASCII
(American Standard Code for Information Interchange), estos códigos son enteros de
un byte (valores de 0 a 255) y representan caracteres o simbolos imprimibles como
letras, números, caracteres especiales como la arroba, el gato, etc. Algunos
representan códigos especiales para controlar el intercambio de datos o bien
dispositivos de impresión como el salto de línea, tabulador, inicio o fin de transmisión,
etc. Se puede encontrar la tabla completa de estos códigos en algunos libros de texto o
en internet, se les deja como ejercicio buscar dicha información para usarla en sus
programas cuando lo consideren necesario. El tipo de dato para esto es char y se
acostumbra representar constantes de tipo caracter por medio del entero de su código
ASCII o encerrando el simbolo entre apostrofes como en ‘a’, ‘\n’, ‘@’, etc.

Los caracteres individuales tienen un tratamiento igual a los enteros, se pueden


comparar con ==, <, >, etc.; como en ‘a’ < ‘b’. Sin embargo, el texto no es igual, las
constantes de texto se representan entre comillas como en “hola mundo”, y se
representan como conjuntos de caracteres, es decir, por medio de arreglos. Al texto se
le denomina cadena, y una cadena en C es un arreglo de caracteres que puede ser de
cualquier tamaño o longitud, con la condición que termine con el caracter nulo ‘\0’
que tiene el valor de cero. Esto significa que el texto “hola” se descompone en ‘h’,
‘o’, ‘l’, ‘a’, ‘\0’, y por lo tanto necesita 5 espacios en un arreglo para
almacenarse, esto es algo que no se debe olvidar a la hora de trabajar con cadenas en
C.
El texto es tan importante que lenguaje C tiene una librería especial para hacer
operaciones con el texto, esta libreria es string.h y debemos incluirla en los programas
que trabajen con cadenas. Esta librería tiene funciones para calcular la longitud de una
cadena (número de caracteres sin contar el caracter nulo), comparar cadenas, etc; en
algunas páginas de internet se puede encontrar documentación de las librerías
estándar de C como en http://c.conclase.net/, les recomiendo usar dicha página como
fuente de consulta cuando tengan dudas de como usar funciones en C, viene la
sintaxis y ejemplos de como usarlas.

Un ejemplo clásico de cadenas consiste en averiguar si una frase es palindrome,


es decir que sus letras son las mismas si lee al derecho y al revés. En el siguiente
programa se resolverá el problema manejando las cadenas sin ayuda de las funciones
de la librería.
#include <stdio.h>
#include <stdlib.h>

#define VERDADERO 1
#define FALSO 0

void quitaEspacios(char *c1, char *c2);


void invierte(char *c1, char *c2);
int sonIguales(char *c1, char *c2);

int main(int argc, char** argv) {


char frase[50], sinEspacios[50], alreves[50];

printf("Ingresar la frase a verificar: ");


fgets(frase, 50, stdin);
//quita los espacios y la pone alreves
quitaEspacios(frase, sinEspacios);
invierte(sinEspacios, alreves);
printf("La frase sin espacios es: %s\n", sinEspacios);
printf("La frase alreves es: %s\n", alreves);

//compara la cadena sin espacios con la misma pero al reves


if(sonIguales(sinEspacios, alreves)) printf("Es palindrome\n");
else printf("No es palindrome\n");

return (EXIT_SUCCESS);
}

void quitaEspacios(char *c1, char *c2){


int i, j;
for(i = j = 0; c1[i] != '\0'; i++)
//copia los diferentes a espacio
if(c1[i] != ' ')
c2[j++] = c1[i];
//quita el enter y añade el caracter nulo
c2[j-1] = '\0';
}

void invierte(char *c1, char *c2){


int i, j, longitud;

//calcula la longitud de la cadena


longitud = 0;
for(i = 0; c1[i] != '\0'; i++)
longitud++;

//copia iniciando por el último caracter


j = longitud - 1;
for(i = 0; j >= 0; j--, i++)
c2[i] = c1[j];

//termina la cadena con el caracter nulo


c2[i] = '\0';
}

int sonIguales(char *c1, char *c2){


int i;

//compara uno por uno y al encontrar el primero diferente es falso


for(i = 0; c1[i] != '\0'; i++)
if(c1[i] != c2[i]) return FALSO;

//si todos son iguales devuelve verdadero


return VERDADERO;
}

Programa 5. Palindromes sin usar funciones de la librería.

El programa anterior resuelve el problema manejando las cadenas como arreglos


de caracteres para ilustrar lo que hacen las funciones de la librería. Lo primero que
deben notar es la función que se usa para leer cadenas fgets, dicha función se
encuentra en stdio.h y lee una cadena hasta que encuentra el enter y este es incluido
en el resultado, después de eso añade de forma automática el carácter nulo para
indicar el fin de la cadena. Luego para resolver el problema se quitan los espacios de
la cadena original porque eso es lo que hacemos de forma manual, de otro modo al
invertir la cadena los espacios quedarán en lugares distintos a la cadena original y eso
provocará que al compararlas carácter a carácter no coincidan y por lo tanto
concluiremos que son diferentes. En el siguiente programa les presentaré una versión
que aprovecha que ya existe una función para calcular la longitud y otra para
comparar cadenas.

Ejercicio 5

Existen muchas formas de resolver el problema de los palíndromes, ahora realice


un programa con una función que recorra al mismo tiempo la cadena al derecho y al
reves, saltando los espacios antes de comparar los caracteres y deberá devolver
verdadero si es palindrome y falso si no lo es. El prototipo de la función es int
esPalindrome(char *c);. El programa debe leer la cadena con fgets y usar las
funciones de string.h como strlen dentro del código.

Búsquedas y ordenamientos en arreglos


Hasta ahora hemos visto algunos ejemplos básicos del uso de arreglos en una y
más dimensiones, también de la forma de pasar arreglos a las funciones y de los
arreglos de caracteres o cadenas. Deben notar que se requiere el uso de ciclos junto
con los arreglos; para almacenar datos, para recorrer el arreglo en busca de
información o para procesar sus datos como en el caso de los promedios o las
operaciones con matrices.

Si deseamos buscar información en un arreglo, la forma más básica de hacerlo es


por medio de una búsqueda lineal que no es otra cosa que recorrer el arreglo con un
ciclo para buscar aquellos elementos que satisfacen algún criterio de búsqueda como
por ejemplo el buscar el mayor o menor elemento. También puede ser que nos
interese saber si algún dato en especial se encuentra en el arreglo y/o en que posición
se encuentra. La siguente función hace una búsqueda del dato x, en el arreglo a de
tamaño t.
int busqueda(int *a, int t, int x){
int pos;

for(pos = 0; pos < t; pos++)


if( x == a[pos]) return pos;

return -1;
}

La función devuelve la posición en la que encuentra el elemento x y se sale de la


función, o si termina de recorrer el arreglo y nunca lo encuentra devuelve -1, esta
consideración se toma porque sabemos que en los arreglos no puede haber indices
negativos. En el caso de que hubiera elementos repetidos y nos interesara saber
cuantos veces aparece x, entonces en lugar de return pos; pondriamos un contador
y/o un arreglo que almacene las posiciones donde apareció.
Ejercicio 6

En estadística existe un análisis que se conoce como histograma para graficar las
veces que aparecen datos de un conjunto dentro de subconjuntos especiales llamados
clases. Tipicamente, el rango total se acostumbra dividir en 5 subrangos, por ejemplo
consideremos un conjunto de enteros que representa la edad, podemos poner de 0 a 12
como niñez, 13 a 17 como adolescentes, 17 a 30 como juventud, de 31 a 60 como
adultos y 61 a 100 como ancianos. En un censo puede que nos interese saber que
porcentaje de la población se encuentra en cada rango para planear acciones de
gobierno que atiendan las necesidades de cada segmento. Haga un programa que
almacenando datos de 10,000 personas las clasifique en los rangos mencionados y
calcule el porcentaje de cada rango con respecto a la población total. Nota: puede
probar su programa con una población de solo 20 para comprobar que los resultados
de su programa son correctos. Las veces que un dato o rango de datos se repiten se
llama frecuencia y la frecuencia mayor se conoce como moda. Para encontrar la moda
se recomienda almacenar las frecuencias en un arreglo.

Busqueda binaria y ordenamiento de la burbuja


La búsqueda lineal aunque resuelve el problema no es la forma más eficiente de
realizar dicha operación. Una forma más eficiente se conoce como búsqueda binaria y
requiere que los datos se encuentren ordenados para así discriminar en cada paso a la
mitad de los elementos por ser mayores o menores al que estamos buscando. Dado
que se requiere ordenar los datos se presentará el método de la burbuja que aunque no
es tampoco muy eficiente pero resuelve el problema. Primero se presenta la versión
que usa información númerica y se pueden usar los operadores <, >, ==, etc; y la
versión de cadenas que requiere del uso de la función strcmp de la librería string.h.

//versión para ordenar numeros


void burbuja(int *a, int t){
int i, j, aux;

//esta versión ordena de menor a mayor


for(i = 0; i < t; i++)
//comparar un dato con el que esta adelante
for(j = 0; j < (t-1); j++)
//si estan desordenados los intercambia
if(a[j] > a[j+1]){
aux = a[j];
a[j] = a[j+1];
a[j+1] = aux;
}
}

//version para ordenar cadenas


void burbuja2(char cadenas[][LON_MAXIMA+1], int t){
int i, j;
char aux[LON_MAXIMA + 1];

//también ordena de menor a mayor en orden lexicográfico


//notar que compara con strcmp que devuelve > 0, si la primer cadena
//es mayor que la segunda.
//strcmp copia la segunda cadena en la primera, no se puede hacer
//por asignación
for(i = 0; i < t; i++)
for(j = 0; j < (t-1); j++)
if(strcmp(cadenas[j]), cadenas[j+1]) > 0){
strcpy(aux, cadenas[j]);
strcpy(cadenas[j], cadenas[j+1]);
strcpy(cadenas[j+1], aux);
}
}

El método de la burbuja ordena el último elemento del arreglo en cada pasada del
ciclo de adentro, por esa razón el ciclo de afuera se debe hacer tantas veces como
elementos tenga el arreglo. El ordenamiento se hace comparando un elemento con el
que se encuentra delante de el, es decir, el que se encuentra en la posición j con el que
se encuentra en la posición j+1, por esta razón el ciclo interior tiene la condición de
terminar con el penúltimo para poderlo comparar con el último. Dado que se ordena
de menor a mayor, un par se encuentra desordenado si el elemento j es mayor que
elemento j+1, en ese caso se lleva a cabo un intercambio el cual tiene que hacerse por
medio de una variable auxiliar. La versión que sirve para ordenar cadenas sigue el
mismo razonamiento, solo que las cadenas tienen que compararse con strcmp y las
cadenas deben copiarse caracter a caracter por medio de la función strcpy, ambas
funciones se encuentran en string.h, puede encontrar una descripción más detallada de
dichas funciones en los libros de Deitel y Aitken que se les proporcionó como parte
del material del curso. También deben notar que un arreglo de cadenas en realidad es
un arreglo de arreglos, es decir, una matriz y la forma de pasarla como parámetros es
igual a los ejemplos que se dieron con matrices, donde LON_MAXIMA + 1 considera
la longitud máxima de las cadenas incluyendo el caracter nulo.

El humilde método de la burbuja puede ser la mejor opción si los datos cambian
raramente y dicha operación no tiene que realizarse de forma frecuente como ocurre
en gran mayoría de los casos, cuálquier cambio en los datos requiere volver a
ordenarlos. Lo más común es que los datos cambien poco, además para poder aplicar
un método eficiente de búsqueda como la búsqueda binaria es necesario usar alguna
clave que sea única para cada dato, un ejemplo de esto es la matrícula que se les
asigna al ingresar a la universidad, de este modo aunque existan dos personas que se
llamen igual, estas no deberían tener la misma matrícula. A continuación se presenta
la versión iterativa (por medio de ciclos) de la búsqueda binaria.

int busquedaBinaria(int *a, int inf, int sup, int x){


int m;
//busca entre el primero(inf) y el último(sup)
//esta condición equivale a decir que al menos hay un elemento
while(sup >= inf){
//encuentra la posición de la mitad del arreglo
m = (sup + inf)/2;
//busca a la mitad y devuelve dicha posición si lo encuentra
if (a[m] == x) return m;
//prueba si se encuentra en la mitad inferior
else if(x < a[m]) sup = m-1;
//prueba si se encuentra en la mitad superior
else if(x > a[m]) inf = m+1;
}
//cuando ya no hay mas mitades quiere decir que no esta y
//devuelve -1
return -1;
}

La búsqueda binaria es más eficiente que la lineal, porque mientras la búsqueda


lineal recorre el arreglo completo posición por posición para concluir que un elemento
no se encuentra, la binaria elimina la mitad de los elementos al comparar con el que se
encuentra en medio y concluye que se encuentra en la mitad superior o inferior en
cada paso. Como dato, para un arreglo de 1000 elementos la búsqueda lineal haría
1000 pasos contra solo 10 de la binaria y esta diferencia se nota cada vez más
conforme crece el número de datos. La discriminación de la mitad de los datos puede
hacerse gracias a que están ordenados y puedo saber en cual mitad buscar en el
siguiente paso.

Ejercicio 7

Modifique el código de la función de la busqueda binaria para que use datos de


texto en lugar de datos númericos. Tip: las comparaciones deben hacerse por medio
de strcmp que devuelve cero si la primer cadena es igual a la segunda, menor a cero si
la primer cadena es menor a la segunda y mayor que cero si la primera es mayor a la
segunda. Pruebe los códigos de cadenas usando palabras simples, es decir no use
cadenas que contengan espacios.

También podría gustarte