Módulo 3 - Fundamentos de Programación en C, C++

37
MP 11 Fundamentos Programación UF 02 Diseño Modular MP 11 UF 02 Módulo 3 Fundamentos Programación. Página 1 MÓDULO 3 FUNDAMENTOS PROGRAMACIÓN EN C, C++. OBJETIVOS. En este módulo comenzaremos a estudiar las estructuras de control de flujo. Fundamentalmente hay dos tipos de estructuras de control de flujo: las bifurcaciones y los bucles. Bifurcaciones: Sentencias if, if... else El operador condicional ?: Sentencias swicth (case). Bucles: Sentencias while, do... while Sentencia for... Sentencias break, continue, goto.

Transcript of Módulo 3 - Fundamentos de Programación en C, C++

Page 1: Módulo 3 - Fundamentos de Programación en C, C++

MP 11 – Fundamentos Programación UF 02 – Diseño Modular

MP 11 – UF 02 – Módulo 3 – Fundamentos Programación. Página 1

MÓDULO 3 – FUNDAMENTOS PROGRAMACIÓN EN C, C++.

OBJETIVOS.

En este módulo comenzaremos a estudiar las estructuras de control de flujo. Fundamentalmente hay dos tipos de estructuras de control de flujo: las bifurcaciones y los bucles.

Bifurcaciones:

Sentencias if, if... else

El operador condicional ?:

Sentencias swicth (case).

Bucles:

Sentencias while, do... while

Sentencia for...

Sentencias break, continue, goto.

Page 2: Módulo 3 - Fundamentos de Programación en C, C++

MP 11 – UF 01 – Módulo 3 – Fundamentos Programación. Página 2

1. PRÁCTICA 1: PROGRESIONES ARITMÉTICAS.

En esta práctica se hace uso de la sentencia for en su forma más estándar. Se trata de sumar los n primeros valores de una progresión aritmética.

1.1. DESARROLLO DE LA PRÁCTICA.

Crearemos un archivo del tipo C denominado m3p01.c y escribiremos el siguiente código:

//m3p01.c - Suma los valores de una progresión aritmética -

#include <stdio.h>

#include <stdlib.h>

int main(){

double a, b;

double Suma;

int n;

system("clear");

printf("Una progresion aritmetica es una sucesión,\n");

printf("el valor general de la cual es de la forma: \n");

printf("a(n)=a*n+b\n\n");

printf("\Introducir el valor de a ... ");

scanf("%lf",&a);

printf("\Introducir el valor de b ... ");

scanf("%lf",&b);

printf("\Introducir el valor de n ... ");

scanf("%lf",&n);

Suma=0;

for (ct=1; ct<=n; ct++){

Suma=Suma+a*ct+b;

}

printf("\nLa suma de los %d primeros valores es %lf\n",n,Suma);

printf("\n Se puede calcular que esta suma es igual a\n");

printf("(a1+an)*n/2 = %lf\n", (a+b+a*n+b)/2*n);

return 0;

}

1.2. EXPLICACIÓN DEL PROGRAMA.

Una progresión aritmética es una sucesión, el valor general de la cual es de la forma:

an=a·n+b

Por ejemplo, la progresión aritmética de valor general an=2·n-1 es {1, 3, 5, 7, ...}

Este programa calculará la suma de los n primeros valores de una progresión aritmética. Esta suma se hace de dos formas. La primera se hace generando todos los valores y sumándolos. La suma se hace con el siguiente bucle en el cual, la variable ct hace de comprador:

Page 3: Módulo 3 - Fundamentos de Programación en C, C++

MP 11 – UF 01 – Módulo 3 – Fundamentos Programación. Página 3

for (ct=1; ct<=n; ct++){

Suma=Suma+a*ct+b;

}

La segunda es sólo una aplicación de la fórmula conocida del cálculo elemental:

Que puede entenderse como que sumar n valores de una progresión aritmética equivale a sumar n veces la media aritmética entre el primer valor y el último:

printf("(a1+an)*n/2 = %lf\n", (a+b+a*n+b)/2*n);

1.3. CAPTURA DE LA EJECUCIÓN DEL PROGRAMA.

Page 4: Módulo 3 - Fundamentos de Programación en C, C++

MP 11 – UF 01 – Módulo 3 – Fundamentos Programación. Página 4

2. PRÁCTICA 2: NÚMEROS PRIMOS.

En esta práctica presentaremos también un algoritmo sencillo y conocido que permite saber si un número entero entrado por teclado es o no un número primo.

2.1. DESARROLLO DE LA PRÁCTICA.

Crearemos un nuevo archivo C denominado m2p03.c y escribiremos el siguiente código:

//m3p02.c - Determinación de números primos -

#include <stdio.h>

#include <stdlib.h>

#include <math.h>

int Primo (int);

int main(){

int num;

system("clear");

printf("\t Introduzca el numero>... ");

scanf("%d",&num);

if (Primo(num) printf("El numero introducido es primo\n";

else printf("El numero introducido es compuesto\n");

return 0;

}

int Primo(int num) {

//Retorna 1 si el número introducido num es primo.

int lim,divisor=2;

lim=int(sqrt(num));

while(divisor<=lim){

if(!num%dicisor)) return 0;

divisor++;

}

return 1;

}

2.2. CAPTURA DE LA EJECUCIÓN DEL PROGRAMA.

Page 5: Módulo 3 - Fundamentos de Programación en C, C++

MP 11 – UF 01 – Módulo 3 – Fundamentos Programación. Página 5

2.3. EXPLICACIÓN DEL PROGRAMA.

Un número primo es aquel número que sólo se puede dividir entre 1 y sí mismo. La lista de los primeros números primos es 1, 2, 3, 5, 7, 11, 13, 17, ...

El método que se utiliza en este programa para comprobar si un número es o no es primo es hacer la división entre todos los números más pequeños que su raíz cuadrada y comprobar si la resta de estas divisiones es cero (0).

¿Por qué la raíz cuadrada? Evidentemente, un número no puede descomponer en factores todos los mayores de la raíz cuadrada de este número.

El programa se ha definido una función que retorna un valor entero: 0 si el argumento es compuesto y 1 si el argumento es primo. La parte principal de la función es la siguiente:

while(divisor<=lim){

if(!num%dicisor)) return 0;

divisor++;

}

return 1;

Debemos fijarnos que en el cuerpo del bucle hay una sentencia if que comprueba si la variable divisor es o no un divisor de num (si lo fuese, la expresión lógica !(num%divisor) sería verdadera y se saldría de la función con el valor de retorno 0). Si se sale del bucle quiere decir que se ha probado todos los números más pequeños que la raíz cuadrada de num y no se ha encontrado ningún divisor, en este caso, el valor de retorno será 1.

Page 6: Módulo 3 - Fundamentos de Programación en C, C++

MP 11 – UF 01 – Módulo 3 – Fundamentos Programación. Página 6

3. PRÁCTICA 3: ALGORITMO DE EUCLIDES.

Uno de los algoritmos más conocidos y más sencillos que se pueden poner como ejemplo de sentencias de control de flujo es el algoritmo de este gran matemático y filósofo de la antigüedad. Este algoritmo se utiliza para calcular el máximo común divisor de dos números enteros.

3.1. DESARROLLO DE LA PRÁCTICA.

El algoritmo de Euclides para calcular el máximo común divisor de dos números enteros es el siguiente:

Dados dos números enteros m y n (suponiendo m=>n). Si el resto r de la división es 0, el divisor n es el MCD, en caso contrario, n se convierte en dividendo y r en divisor. Volvemos a hacer la división y repetimos el mismo proceso.

En el siguiente programa se crea una función denominada mcd() que utiliza este algoritmo

Abriremos un nuevo archivo denominado m2p03.c. Escribiremos el siguiente código:

//m3p03.c - Algoritmo de Euclides -

#include <stdio.h>

#include <stdlib.h>

int mcd(int,int);

int main(){

int n,m;

system("clear");

printf("Introducir dos números enteros separados por un espacio\n");

scanf("%d %d",&n,&m);

printf("\n\nmcd(%d%d) = %d\n",n,m,mcd(n,m);

return 0;

}

int mcd(int m,int n){

int r;

while(n>0){

r=m%n;

m=n;

n=r;

}

return m;

}

Page 7: Módulo 3 - Fundamentos de Programación en C, C++

MP 11 – UF 01 – Módulo 3 – Fundamentos Programación. Página 7

3.2. EXPLICACIÓN DEL PROGRAMA.

Vamos a analizar la función mcd(). En primer lugar imaginemos que m es más pequeño que n. En este caso, la primera vez que pasa por el bucle se intercambian estas dos variables y pasa a ser m más grande que n. En el caso que m sea más grande que n se ejecuta el algoritmo explicado al principio hasta que la división sea exacta, entonces sale del bucle y retorna el valor de m:

while(n>0){

r=m%n;

m=n;

n=r;

}

return m;

Debemos recordar que la expresión m%n retorna el resto de la división entera entre m y n. Este bucle acaba en el momento que este resto sea cero (0).

Imaginemos que comenzamos con m=20 y n=12. Veremos un esquema de los valores de las diferentes variables al comienzo de cada bucle:

El caso n=20 y m=12 sería:

3.3. CAPTURA DE LA EJECUCIÓN DEL PROGRAMA.

Page 8: Módulo 3 - Fundamentos de Programación en C, C++

MP 11 – UF 01 – Módulo 3 – Fundamentos Programación. Página 8

4. PRÁCTICA 4: COMPROBACIÓN DE DATOS.

Una de las muchas funciones de las sentencias condicionales es la comprobación de datos introducidos por teclado. En esta práctica aprenderemos a hacer la comprobación en algunos casos.

4.1. DESARROLLO DE LA PRÁCTICA.

Crearemos un nuevo archivo C denominado m3p04.c y escribiremos en la ventana de edición el siguiente código:

//m3p04.c - Comprobación de datos -

#include <stdio.h>

#include <stdlib.h>

#include <ctype.h> // Por tolower()

void LimpiaBuffer(){ while(getchar() != '\n'); }

int main(){

int n;

char c;

system("clear");

printf("\nIntroducir un número impar...");

scanf("%d",&n);

if(n%2) printf("Correcto\n");

else printf("No es correcto\n");

printf("\nIntroducir un número entero entre 1 y 5...");

scanf("%d",&n);

if(n>1&&n<5) printf("Correcto\n");

else printf("No es correcto\n");

printf("\nIntroducir una vocal...");

LimpiaBuffer();

c=getc(stdin);

c=tolower(c);

if(c=='a'||c=='e'||c=='i'||c=='o'||c=='u')

printf("Correcto\n");

else printf("No es correcto\n");

printf("\nPresionar la tecla ESC...\n");

LimpiaBuffer();;

c=getc(stdin);

if(c==27)

printf("Correcto\n");

else printf("No es correcto\n");

return 0;

}

Page 9: Módulo 3 - Fundamentos de Programación en C, C++

MP 11 – UF 01 – Módulo 3 – Fundamentos Programación. Página 9

4.2. EXPLICACIÓN DEL PROGRAMA.

La primera comprobación es si el número es o no es impar. Un número impar es aquel en el cual la operación n%2 es diferente de cero (0), y por tanto verdad.

La segunda comprobación es si el número introducido se encuentra en el intervalo (1, 5). Esto se comprueba con los operadores relacionales <i> y con operador lógico &&.

La tercera comprobación es si el carácter introducido es una vocal. Para incluir tanto las mayúsculas como las minúsculas se utiliza la función tolower() que retorna el carácter en minúsculas del carácter que se ponga como argumento. El protocolo de esta función se encuentra en el archivo de cabecera stdlib.h que se debe incluir en el programa.

La cuarta comprobación es si el carácter introducido corresponde a la tecla ESC, el código ASCII de la cual es 27.

LimpiaBuffer();

Utilizamos esta función para limpiar el buffer del teclado. La anterior función scanf("%d",&n);lee el dato y deja sin recoger el carácter '\n' que se queda dentro del buffer. La siguiente instrucción de lectura c=getc(stdin); es el primer carácter que se encuentra, lo captura y entiende que se ha finalizado la entrada. La función que hemos descrito LimpiaBuffer(); vacía de caracteres este buffer y lo deja limpio para la siguiente entrada.

c=getc(stdin);

La macro getc() implementada en la biblioteca estándar stdio.h lee un carácter, en este caso desde el teclado, y lo retorna. Este carácter es asignada a la variable c.

4.3. CAPTURA DE LA EJECUCIÓN DEL PROGRAMA.

Page 10: Módulo 3 - Fundamentos de Programación en C, C++

MP 11 – UF 01 – Módulo 3 – Fundamentos Programación. Página 10

5. PRÁCTICA 5: ALGORITMO RUSO DE LA MULTIPLICACIÓN.

En esta práctica utilizaremos la sentencia do…while para hacer la multiplicación de dos números enteros de una forma especial.

5.1. DESARROLLO DE LA PRÁCTICA.

Crearemos un nuevo archivo denominado m3p05.c del tipo y escribiremos el siguiente código:

//m3p05.c - Algoritmo ruso de la multiplicación -

#include <stdio.h>

int mrus(int a,int b);

int main(){

int a,b;

system("clear");

printf("Introducir los valores a,b>0 ");

scanf("%d %d",&a, &b);

printf ("\n\n%d x %d = %d\n",a,b,mrus(a,b));

return 0;

}

// Función multiplicación rusa

int mrus(int a,int b ){

int suma=0;

do{

if (a%2) suma=suma+b;

a=a/2;b=b*2;

}while(a>0);

return suma;

}

5.2. CAPTURA DE LA EJECUCIÓN DEL PROGRAMA.

Page 11: Módulo 3 - Fundamentos de Programación en C, C++

MP 11 – UF 01 – Módulo 3 – Fundamentos Programación. Página 11

5.3. EXPLICACIÓN DEL PROGRAMA.

Se escriben los dos números, uno al lado del otro y se forman dos columnas aplicando repetidamente la siguiente regla: el primer número se divide por dos y se coloca el cociente entero debajo. El segundo número se multiplica por dos y se coloca debajo. Este procedimiento se continúa hasta que se consigue un 1 en la primera columna.

Por ejemplo, para multiplicar 46 y 43 haremos: 46 43 23 86 11 172 5 344 2 688 1 1376

A continuación se marcan las filas en las que el primero de los números sea par:

46 43 23 86 11 172 5 344 2 688 1 1376

La suma de los números no rallados que quedan en la segunda columna es el resultado:

86 + 172 + 334 + 1376 = 1978.

Puede ser que este algoritmo parezca algo extraño pero se debe tener en cuenta que se puede realizar sin saber las tablas de multiplicar, tan sólo es necesario saber sumar, duplicar y dividir por dos.

Este algoritmo se implementa en el programa a través de la función mrus(int a, int b).

// Función multiplicación rusa

int mrus(int a,int b ){

int suma=0;

do{

if (a%2) suma=suma+b;

a=a/2;b=b*2;

}while(a>0);

return suma;

}

Inicialmente, a y b son los números que queremos multiplicar. Después, los sucesivos valores de a corresponden a los valores de la columna de la izquierda y los sucesivos valores de b a los valores de la columna de la derecha. En el cuerpo del bucle hay una sentencia if que hace que se acumule en la variable suma los valores de b que corresponden a valores de a impares (a%2 diferente de 0).

Page 12: Módulo 3 - Fundamentos de Programación en C, C++

MP 11 – UF 01 – Módulo 3 – Fundamentos Programación. Página 12

6. PRÁCTICA 6: MENÚ DE ENTRADA.

Frecuentemente se utiliza la sentencia switch para procesar ordenes del teclado como un menú. En esta práctica presentaremos un ejemplo de este tipo de uso.

6.1. DESARROLLO DE LA PRÁCTICA.

Crearemos un nuevo archivo denominado m3p06.c y escribiremos el siguiente código:

//m2p06.c - Menú de entrada -

#include <stdio.h>

#include <stdlib.h>

void opcion1(), opcion2(), opcion3(), opcion4();

int main(){

char sel;

system("clear");

printf("Ejemplo de menu\n\n\n");

printf("1. Descripcion seleccion 1\n\n");

printf("2. Descripcion seleccion 2\n\n");

printf("3. Descripcion seleccion 3\n\n");

printf("4. Descripcion seleccion 4\n\n");

printf("esc. salir\n\n");

printf("\n\n Seleccione una opcion...");

do{

sel=getc(stdin);

}while((sel<'1' || sel>'4')&&sel!=27); //esc=ASCII 27

switch(sel){

case '1':

opcion1();

break;

case '2':

opcion2();

break;

case '3':

opcion3();

break;

case '4':

opcion4();

break;

case 27:

//Salida

break;

default:;

//En este caso no hay posibilidad de otro caso, ya que la sentencia

//do...while comprueba que la entrada se encuentre entre 1 y 4.

}

Page 13: Módulo 3 - Fundamentos de Programación en C, C++

MP 11 – UF 01 – Módulo 3 – Fundamentos Programación. Página 13

return 0;

}

void opcion1(){

printf("Ha seleccionado la primera opcion\n");

}

void opcion2(){

printf("Ha seleccionado la segunda opcion\n");

}

void opcion3(){

printf("Ha seleccionado la tercera opcion\n");

}

void opcion4(){

printf("Ha seleccionado la cuarta opcion\n");

}

6.2. CAPTURA DE LA EJECUCIÓN DEL PROGRAMA.

6.3. EXPLICACIÓN DEL PROGRAMA.

En primer lugar se escribe en la pantalla la descripción de las opciones que el usuario debe seleccionar:

printf("Ejemplo de menu\n\n\n");

printf("1. Descripcion seleccion 1\n\n");

printf("2. Descripcion seleccion 2\n\n");

printf("3. Descripcion seleccion 3\n\n");

printf("4. Descripcion seleccion 4\n\n");

printf("esc. salir\n\n");

Page 14: Módulo 3 - Fundamentos de Programación en C, C++

MP 11 – UF 01 – Módulo 3 – Fundamentos Programación. Página 14

También solicita que al usuario que seleccione una de las opciones antes impresas. La opción seleccionada se comprueba con el siguiente bucle:

do{

sel=getc(stdin);

}while((sel<'1' || sel>'4')&&sel!=27); //esc=ASCII 27

Este bucle va leyendo los caracteres hasta que el carácter introducido sea una de las opciones válidas: un número entero entre 1 y 4 o el carácter ASCII 27 que corresponde a la tecla ESC (prestaremos atención al hecho de escribir '1' y '4' entre comilla simple y 27 sin comilla).

Cuando el programa ha salido de este bucle, el contenido de la variable sel es una opción válida del menú. La sentencia switch comprueba el valor de esta variable y envía el control de la ejecución a la sentencia que hay a continuación de la etiqueta case correspondiente.

Por ejemplo, si se ha entrado un 3, la sentencia que se ejecuta es una llamada a la función opcion3 (). Una vez completada la función opcion3 (), el control de la ejecución vuelve al mismo lugar donde se llamó la función y se encuentra una sentencia break que hace un salto incondicional al final de la sentencia switch que, en este caso, coincide con el final del programa.

En esta práctica se ha mostrado el lugar donde se debe poner la etiqueta default, aunque, tal y como se explica en el comentario, no es necesario en este caso, ya que se realizan comprobaciones suficientes para garantizar que el programa nunca pasará por aquí.

Page 15: Módulo 3 - Fundamentos de Programación en C, C++

MP 11 – UF 01 – Módulo 3 – Fundamentos Programación. Página 15

7. PRÁCTICA 7: TODAS LAS PAREJAS.

En muchos casos, los bucles se usan para recorrer todos los casos posibles de una situación concreta. En esta práctica veremos cómo formar todas las parejas posibles de valores de dos variables diferentes.

7.1. DESARROLLO DE LA PRÁCTICA.

Crearemos un nuevo archivo denominado m3p07.c y escribiremos el siguiente código:

//m2p07.c - Todas las parejas. Variaciones y combinaciones -

#include <stdio.h>

int main(){

int n; //Número de elementos.

int vr,va,co; //Contadores de variaciones y combinaciones.

int ct1,ct2; //Contador de los bucles.

system("clear");

printf("Introduzca el numero n de elementos...");

scanf("%d",&n);

//Variaciones con repetición.

vr=0;printf("\n\nVariaciones con repeticion\n");

for (ct1=1;ct1<=n;ct1++){

for(ct2=1;ct2<=n;ct2++){

vr++;

printf("vr%d:(%d, %d)\t",vr,ct1,ct2);

}

}

//Variaciones sin repetición.

va=0;printf("\n\nVariaciones sin repeticion\n");

for (ct1=1;ct1<=n;ct1++){

for(ct2=1;ct2<=n;ct2++){

if(ct1-ct2){

va++;

printf("va%d:(%d, %d)\t",va,ct1,ct2);

}

}

}

//Combinaciones

co=0;printf("\n\nCombinaciones\n");

for (ct1=1;ct1<n;ct1++){

for(ct2=ct1+1;ct2<=n;ct2++){

co++;

printf("co%d:(%d, %d)\t",co,ct1,ct2);

}

}

return 0;

}

Page 16: Módulo 3 - Fundamentos de Programación en C, C++

MP 11 – UF 01 – Módulo 3 – Fundamentos Programación. Página 16

Este programa es un ejemplo típico de uso de bucles encajados. Se trata de formar todas las parejas posibles de un conjunto de n elementos. Haremos los tres casos siguientes:

Con repetición e importando el orden. Este primer caso corresponde a lo que se conoce como variaciones con repetición de n elementos de orden 2 y el número total de éstas son n2.

Sin repetición, pero sin importar el orden. Este caso corresponde a las variaciones sin repetición de n elementos de orden 2 y el número total de éstas son: n (n-1).

Sin repetición, pero importando el orden. El tercer caso corresponde a las combinaciones de n elementos de orden 2 y el número total de éstas es: n (n-1) / 2.

7.2. CAPTURA DE LA EJECUCIÓN DEL PROGRAMA.

7.3. EXPLICACIÓN DEL PROGRAMA.

//Variaciones con repetición.

vr=0;printf("\n\nVariaciones con repeticion\n");

for (ct1=1;ct1<=n;ct1++){

for(ct2=1;ct2<=n;ct2++){

vr++;

printf("vr%d:(%d, %d)\t",vr,ct1,ct2);

}

}

En este primer caso, se ha utilizado un doble bucle encajado con dos variables contadoras (ct1 y ct2) que recorren, cada una de ellas y de forma independiente, todos los valores desde 1 hasta n.

Page 17: Módulo 3 - Fundamentos de Programación en C, C++

MP 11 – UF 01 – Módulo 3 – Fundamentos Programación. Página 17

//Variaciones sin repetición.

va=0;printf("\n\nVariaciones sin repeticion\n");

for (ct1=1;ct1<=n;ct1++){

for(ct2=1;ct2<=n;ct2++){

if(ct1-ct2){

va++;

printf("va%d:(%d, %d)\t",va,ct1,ct2);

}

}

}

En el segundo caso se ha añadido en el cuerpo del segundo bucle una sentencia condicional if para detectar las repeticiones y saltarlas. Si ct1=ct2, la condición ct1-ct2 será falsa y no entrará dentro del bloque.

//Combinaciones

co=0;printf("\n\nCombinaciones\n");

for (ct1=1;ct1<n;ct1++){

for(ct2=ct1+1;ct2<=n;ct2++){

co++;

printf("co%d:(%d, %d)\t",co,ct1,ct2);

}

}

En el tercer caso, como que los elementos de la pareja no se pueden repetir, se ha escogido que el primer elemento es el más pequeño, y va desde 1 hasta n-1 y el segundo elemento, que será más grande, va desde ct1+1 hasta n.

Page 18: Módulo 3 - Fundamentos de Programación en C, C++

MP 11 – UF 01 – Módulo 3 – Fundamentos Programación. Página 18

8. PRÁCTICA 8: ALGORITMO PARA CALCULAR LA RAÍZ CUADRADA.

En esta práctica veremos otra aplicación, se trata de un algoritmo muy eficiente para el cálculo de la raíz cuadrada.

8.1. DESARROLLO DE LA PRÁCTICA.

Crearemos un nuevo archivo denominado m3p08.c y escribiremos el siguiente código:

//m3p08.c - Cálculo de la raíz cuadrada -

#include <stdio.h>

double Raiz, A;

int ct, iter;

system("clear");

printf("A= ");

scanf("%lf",&A);

printf("\nRaiz= "); scanf("%lf",&Raiz);

printf("\Numero de iteraciones"); scanf ("%d",&iter);

for(ct=0;ct<=iter;ct++){

Raiz=a/2/Raiz+Raiz/2;

printf("\n%d: %.20lf",ct,Raiz);

}

return 0;

}

8.2. CAPTURA DE LA EJECUCIÓN DEL PROGRAMA.

Page 19: Módulo 3 - Fundamentos de Programación en C, C++

MP 11 – UF 01 – Módulo 3 – Fundamentos Programación. Página 19

8.3. EXPLICACIÓN DEL PROGRAMA.

Todos nosotros hemos aprendido en la escuela un algoritmo para el cálculo de la raíz cuadrada de un número. La difusión de calculadoras que hacen este cálculo ha hecho que casi todos nos hayamos olvidado de cómo se hacía a mano una raíz cuadrada.

No recordaremos este algoritmo, sólo señalaremos que para encontrar un decimal de la raíz cuadrada tenía que hacer un montón de operaciones: multiplicar por dos, dividir, multiplicar, restar, etc. Aquí presentaremos otro algoritmo mucho más eficiente para el cálculo de la raíz cuadrada. Este algoritmo es un algoritmo iterativo que converge rapidísimamente.

Si se quiere calcular la raíz cuadrada de un número A partiremos de una estimación inicial x0. Si denominamos e al error cometido, tenemos:

A = (x0+e)2 = x02+2x0e+e2

Si x0 no es 0 podemos expresar el error de la siguiente forma:

Si nuestra estimación inicial era suficientemente buena, se puede suponer que:

Con lo que quedará:

Por tanto, la nueva aproximación será:

Se puede probar, con consideraciones que no se expondrán, que el proceso iterativo anterior converge siempre que se verifique que:

Una buena elección de x0 puede ser la aproximación entera por exceso de la raíz de A. Por ejemplo, si A=2, un buen valor inicial es x0=2.

Lo que es realmente sorprendente es que con sólo 3 o 4 veces, este proceso iterativo se puede encontrar la raíz cuadrada con toda la precisión que los números de tipo double pueden tener. Todo el cálculo se hace en este bucle:

for(ct=0;ct<=iter;ct++){

Raiz=a/2/Raiz+Raiz/2;

printf("\n%d: %.20lf",ct,Raiz);

}

Que implementa la fórmula iterativa:

iter es el número de iteraciones que se hará. No hace falta introducir valores altos para esta variable. En la mayoría de los casos un número entre 3 y 5 dará una precisión suficiente para calcular cualquier raíz cuadrada.

Además del valor de a y el número de iteraciones iter es necesario introducir la Raiz o estimación inicial.

Page 20: Módulo 3 - Fundamentos de Programación en C, C++

MP 11 – UF 01 – Módulo 3 – Fundamentos Programación. Página 20

8.4. CAPTURA DE LA EJECUCIÓN DEL PROGRAMA.

Page 21: Módulo 3 - Fundamentos de Programación en C, C++

MP 11 – UF 01 – Módulo 3 – Fundamentos Programación. Página 21

9. PRÁCTICAS DE AMPLIACIÓN.

9.1. MÉTODOS NUMÉRICOS PARA LA RESOLUCIÓN DE ECUACIONES.

No podemos concebir un curso de programación sin citar, aunque sea en una práctica de ampliación, una de las partes más importantes de los métodos numéricos: La resolución de ecuaciones. Veremos dos de los muchos métodos numéricos que existen para esta tarea.

Desde hace mucho tiempo son conocidas fórmulas analíticas exactas para resolver ecuaciones polinómicas de grado menor o igual a 4. Sin embargo, no existen métodos analíticos generales para resolver ecuaciones polinómicas de grado superior a 4 y ecuaciones que incorporan otros tipos de funciones (exponenciales, logarítmicas, trigonométricas, etc.). Los métodos numéricos son imprescindibles para abordar la resolución de este tipo de ecuaciones.

9.1.1. MÉTODO DE BISECCIÓN.

El método más sencillo para resolver una ecuación del tipo: f (x) = 0, en el caso de que la función f(x) sea una función continua , es una aplicación directa de uno de los teoremas más importantes y conocidos de las funciones continuas : el teorema de Bolzano . Este teorema afirma que si una función continua en un intervalo cerrado [a, b] toma valores de distinto signo en los extremos del intervalo, existirá, al menos un punto del interior del intervalo en el que el valor de la función será 0.

El método de la bisección consiste en un proceso iterativo en el que se divide el intervalo en dos partes iguales y se elige uno de los dos subintervalos para continuar el mismo procedimiento. El intervalo inicial es un intervalo en los extremos la función toma valores de distinto signo. El subintervalo elegido es un subintervalo en el que pasa exactamente lo mismo que en el intervalo inicial. Este procedimiento se puede continuar hasta que el tamaño del intervalo sea tan pequeño como se quiera.

9.1.1.1. DESARROLLO DE LA PRÁCTICA.

El código que implementa el método de la bisección es el siguiente:

//m3pa11.c - Método de la bisección -

#include <stdio.h>

#include <stdlib.h>

double Biseccion(double ,double , double);

double Funcion(double);

int main(){

double A,B,Error;

printf("Introduzca los valores de A y B...:");

scanf("%lf %lf",&A,&B);

printf("Introduzca el error máximo admitido...:");

scanf("%lf", &Error);

printf("\nSolucion : %lf",Biseccion(A,B,Error));

return 0;

}

Page 22: Módulo 3 - Fundamentos de Programación en C, C++

MP 11 – UF 01 – Módulo 3 – Fundamentos Programación. Página 22

double Biseccion(double A, double B, double Error){

double FA,FB,FC,C;

if (!funcion(A)) return A;

if (!funcion(B)) return B;

if (funcion(A)*funcio(B)>0){

printf("El intervalo [%lf,%lf] no cumple la condicion",A,B);

return 0;

}

if (A>B){

C=A;A=B;B=C;

}

FA=funcion(A);FB=funcion(B);

while (B-A>Error){

C=(A+B)/2;FC=funcion(C);

if (!FC) return C;

if (FA*FC>0){

A=C;FA=FC;}

else{

B=C;FB=FC;}

}

return (A+B)/2;

}

double funcion(double x){

return x*x*x-5*x-2;

}

9.1.1.2. EXPLICACIÓN DEL PROGRAMA.

En este caso se ha implementado el método de la bisección para encontrar soluciones de la ecuación: x3 - 5x - 2 = 0. Esta ecuación se puede expresar como f(x) = 0, donde f(x) es el primer miembro de la ecuación. Esta ecuación matemática corresponde justamente a la función función() del programa anterior.

El programa solicita como datos iniciales los extremos del intervalo inicial y el error máximo que se quiere admitir.

El cálculo se hace a través de la función Bisección(). Esta función comienza comprobando si los propios extremos del intervalo satisfacen la ecuación o bien si la función no toma signos diferentes en estos extremos. Si no es así dividiendo el intervalo en dos hasta que b-a, el tamaño del intervalo, sea inferior al error preestablecido. En cuanto pasa esto último, la función devuelve como valor de retorno el punto medio del intervalo.

En el caso de la función del ejemplo hay tres raíces. La gráfica de la función f(x) es:

Page 23: Módulo 3 - Fundamentos de Programación en C, C++

MP 11 – UF 01 – Módulo 3 – Fundamentos Programación. Página 23

9.1.1.3. CAPTURA DE LA EJECUCIÓN DEL PROGRAMA.

9.1.2. MÉTODO DE NEWTON.

Aunque pueda parecer que el método anterior encuentra una solución rápidamente, veremos otro método que mejora sustancialmente el método anterior, se trata del método de Newton. Este método es un proceso iterativo que en cada paso da una mejor aproximación a la solución de la ecuación f(x)=0.

Siendo xn una aproximación de la raíz. Trazando la recta tangente a la curva y=f(x) para el punto (xn,f(xn)), la intersección de esta recta con el eje de abscisas será una mejor aproximación de la solución. Si denominamos a este punto xn+1 y volvemos a aplicar este proceso se llega a una sucesión de aproximaciones que, en condiciones habituales, convergen mucho más rápidamente que por el método de la bisección.

El valor de xn+1 se puede encontrar con la intersección de la recta tangente: y-f(xn)=f’(xn)·(x-xn) y el eje de abscisas, dando lugar a la relación:

En el siguiente programa podremos calcular una solución de la ecuación f(x)=0 con el método de Newton. Hemos añadido dos funciones que corresponden a la definición de f(x) y su derivada. En este caso f(x)=ex-2x.

La única raíz de la ecuación f(x)=0 está entre -1 y 0 tal y como se puede comprobar en la gráfica de f(x):

Page 24: Módulo 3 - Fundamentos de Programación en C, C++

MP 11 – UF 01 – Módulo 3 – Fundamentos Programación. Página 24

9.1.2.1. DESARROLLO DE LA PRÁCTICA.

//m3pa12.c - Método de Newton -

#include <stdio.h>

#include <math.h>

#include <stdlib.h>

#define MAX_ITER 20

double Newton(double ,double);

double F(double), DF(double);

int main(){

double x0,Error;

printf("Introduzca x0 y Error...");

scanf("%lf %lf",&x0,&Error);

printf("\nLa solución es %lf\n", Newton(x0, Error)));

return 0;

}

double Newton(double x0,double Error){

int iter=0;

double x;

x=x0;

do{

x0=x;

x=x0-f(x0)/df(x0);

printf("%lf %lf\n",x,x-x0);

}while(fabs(x-x0)>Error&&iter++<MAX_ITER);

return x;

}

double F(double x){ //Definición de F(x)

return exp(x)-x*x;

}

double DF(double x){ //Definición de F'(x)

return exp(x)-2*x;

}

9.1.2.2. CAPTURA DE LA EJECUCIÓN DEL PROGRAMA.

Page 25: Módulo 3 - Fundamentos de Programación en C, C++

MP 11 – UF 01 – Módulo 3 – Fundamentos Programación. Página 25

9.2. ALGUNOS MÉTODOS DE ORDENACIÓN.

Seguramente, con la búsqueda, la ordenación es una de las tareas más importantes y más ampliamente analizadas de todas las tareas de programación. En esta práctica veremos, sin profundizar mucho, dos de los métodos de ordenación más conocidos.

9.2.1. MÉTODO DE LA BURBUJA.

Este método puede que sea uno de los más conocidos de ordenación por cambio. Utiliza repetidas comparaciones y, si es necesario, cambios en los elementos adyacentes.

9.2.1.1. DESARROLLO DE LA PRÁCTICA.

//m3pa21.c - Ordenación por el método de la búrbuja -

#include <stdio.h>

#define MAX_n 100

int main(){

int N, ct1, ct2;

int Num[MAX_n];

int Aux;

do{

printf("Introduzca la cantidad de números a ordenar ");

scanf("%d", &N);

}while(N<=0 || N>MAX_n);

printf("Introduzca los %d numeros\n",N);

for (ct1=0;ct1<n;ct1++){

printf("\nnum %d ",ct1);scanf("%d",&num[ct1]);

}

//Algoritmo principal.

for (ct1=1;ct1<n;ct1++){

for(ct2=n-1;ct2>=ct1;ct2--){

if( num[ct2-1]>num[ct2]){

aux=num[ct2-1];

num[ct2-1]=num[ct2];

num[ct2]=aux;

}

}

}

for (ct1=0;ct1<n;ct1++){

printf("%d ",num[ct1]);

}

return 0;

}

Page 26: Módulo 3 - Fundamentos de Programación en C, C++

MP 11 – UF 01 – Módulo 3 – Fundamentos Programación. Página 26

9.2.1.2. CAPTURA DE LA EJECUCIÓN DEL PROGRAMA.

9.2.1.3. EXPLICACIÓN DEL PROGRAMA.

El algoritmo principal está destacado en un color más oscuro. Para entender cómo funciona este algoritmo veremos cómo se cambian los números en cada paso (cada paso corresponde a una columna). Imaginemos que introducimos 5 números que son: 5, 1, 4, 3, 2. Los sucesivos cambios hasta llegar a la ordenación final son:

9.2.2. ORDENACIÓN POR SELECCIÓN.

Una ordenación por selección escoge el elemento más bajo y lo cambia por el primer elemento. A continuación hace lo mismo con los n-1 elementos restantes. En este método, si un número está ya en su posición no se mueve.

Ejemplo, si los números introducidos son: 5, 1, 4, 3, 2, los sucesivos pasos de ordenación son:

Page 27: Módulo 3 - Fundamentos de Programación en C, C++

MP 11 – UF 01 – Módulo 3 – Fundamentos Programación. Página 27

9.2.2.1. DESARROLLO DE LA PRÁCTICA.

El programa que implementa este algoritmo es el siguiente:

//m3pa22.c - Ordenación per selección -

#include <stdio.h>

#define MAX_N 100

void Interc(int*Num,int N,int M);

int Min(int*Num,int N,int M); //Calcula el mínimo entre N y M.

int main(){

int N,ct;

int Num[MAX_N];

do{

printf("Introducir la cantidad de números a ordenar ");

scanf("%d",&N);

}while(N<=0 || n>MAX_N);

printf("Introducir los %d numeros\n",N);

for (ct=0;ct<n;ct++){

printf("\nNum %d ",ct);scanf("%d",&Num[ct]);

}

for (ct=0;ct<N-1;ct++){

interc(Num,ct,min(Num,ct,N-1));

}

for (ct=0;ct<N;ct++){

printf("%d ",Num[ct]);

}

return 0;

}

int min(int*Num,int N, int M){

int Aux,ctmin,ct;

Aux=Num[N];ctmin=N;

for(ct=N;ct<=M;ct++){

if (Aux>Num[ct]){

Aux=Num[ct];

ctmin=ct;

}

}

return ctmin;

}

void interc(int*Num, int N, int M){

int Aux;

Aux=Num[N];

Num[N]=Num[M];

Num[M]=Aux;

}

Page 28: Módulo 3 - Fundamentos de Programación en C, C++

MP 11 – UF 01 – Módulo 3 – Fundamentos Programación. Página 28

9.2.2.2. CAPTURA DE LA EJECUCIÓN DEL PROGRAMA.

9.2.3. ORDENACIÓN RÁPIDA.

La ordenación rápida fue inventada y llamada de esta forma por C.A.R. Hoare y es un método más eficiente que los dos métodos anteriores. No escribiremos el código de esta ordenación porque esta función ya está implementada en las librerías C estándar. Sólo diremos que en la ordenación rápida se escoge un elemento de comparación y se separan todos los números en dos conjuntos: los que son más grandes que este elemento de comparación y los que son más pequeños. Para cada elemento se vuelve a hacer el mismo proceso con otros elementos de comparación.

Page 29: Módulo 3 - Fundamentos de Programación en C, C++

MP 11 – UF 01 – Módulo 3 – Fundamentos Programación. Página 29

10. RESUMEN TEÓRICO.

10.1. LAS BIFURCACIONES.

En principio, las sentencias de un programa C/C++ se ejecutan secuencialmente; es decir, una a continuación de otra. Un programa con estructura secuencial se suele representar gráficamente de la siguiente forma:

En los programas secuenciales las acciones:

Se ejecutan todas; Se ejecuta cada una sólo una vez; Se ejecutan en el mismo orden en que han sido escritas.

En la mayoría de programas necesitaremos que ejecuten o no una o más acciones, dependiendo que se cumpla o no una condición. Esto se consigue con las sentencias condicionales o bifurcaciones.

10.2. LA SENTENCIA if…else.

Las sentencias condicionales más simples de C/C++ son: la sentencia if y la sentencia if…else que tienen la siguiente sintaxis:

if (<condición>) <sentencia>

if (<condición>) <sentencia1> else <sentencia2>

En el primer caso, la condición, que es una expresión lógica (recordemos que en C/C++, las expresiones lógicas son de hecho expresiones numéricas) es evaluada. Si esta tiene el valor de verdad (valor diferente de 0), se ejecuta la sentencia, en caso contrario: es decir, si el valor de la condición es falso (igual a 0), se salta la ejecución de la sentencia (o bloque) que hay a continuación y pasa a la sentencia inmediatamente posterior.

En el segundo caso, una vez evaluada la condición, si esta es verdad se ejecuta la sentencia1, y a continuación se salta la sentencia2 para ejecutar la sentencia inmediatamente posterior a la sentencia2. Si la condición es falsa se salta la sentencia1 y pasa directamente a la sentencia2.

La representación gráfica de estas dos sentencias es:

Page 30: Módulo 3 - Fundamentos de Programación en C, C++

MP 11 – UF 01 – Módulo 3 – Fundamentos Programación. Página 30

Las sentencias pueden ser, evidentemente, sentencias simples (acabadas en punto y coma) o bloques de sentencias (cerradas entre llaves).

Las sentencias condicionales se pueden encajar dando lugar a estructuras condicionales complejas como:

En el caso de tener más de una sentencia if, una dentro de otra, cada else se refiere al if más cercano.

La sentencia if…else permite una ramificación múltiple, ejecutando una sentencia entre diferentes partes del programa según se encuentra entre diferentes condiciones. La forma general de esta estructura es:

if (<expressión1>) sentencia1; else if (<expressión2>) sentencia2; else if (<expressión3>) sentencia3; ... else sentencia_n;

10.3. EL OPERADOR TERNARIO ?

Muchas veces se puede sustituir una sentencia condicional if…else por el operador ternario ?.... Su sintaxis es: <expr1> ? <expr2> : <expr3>

El valor de esta expresión es <expr2> en el caso que <expr1> sea verdad (diferente de 0) o bien <expr3> si <expr1>=0.

Se llama operador ternario precisamente porque tiene tres operantes. La diferencia principal entre el uso del operador ternario ? y la sentencia if…else, es que los operantes de este operador deben ser expresiones y no sentencias.

Ejemplo: y=x ? 100 : 200;

En este ejemplo, se asignará el valor de la expresión (x ? 100 : 200) a la variable y. El operador ? tiene prioridad respecto al operador de asignación =.

Page 31: Módulo 3 - Fundamentos de Programación en C, C++

MP 11 – UF 01 – Módulo 3 – Fundamentos Programación. Página 31

10.4. LA SENTENCIA switch.

C/C++ incorpora una sentencia de bifurcación múltiple que es más elegante y fácil de leer que muchas sentencias if juntas, se trata de la sentencia switch y tiene la siguiente sintaxis:

switch (<variable>){ case <constante1>): <sentencia1> break; case <constante2>): <sentencia2> break; ... default: <sentencia> }

Cuando se encuentra esta sentencia, es evaluada la variable que acompaña a la palabra switch. Esta variable debe ser de carácter, entera, o bien otro tipo que pueda ser convertida a entera sin ambigüedad. Esta variable es comparada con las expresiones constantes que acompañan a las etiquetas case, si coincide con alguna de estas constantes, se sigue ejecutando a partir de la sentencia que se encuentra a continuación de esta constante. En caso de no coincidir ninguna de las constantes, se ejecuta a partir de la sentencia que se encuentra a continuación de la etiqueta default, si es que ésta existe, si no es así, sigue la ejecución en la sentencia que se encuentre a continuación del bloque de sentencia switch.

Cuando se ha encontrado una coincidencia, el código se sigue ejecutando hasta encontrar una sentencia de salto break. Esta sentencia hace que la ejecución salte incondicionalmente hasta el final del bloque de sentencia switch. La sentencia break es técnicamente opcional, si se omite, la ejecución continuará ignorando otras etiquetas y ejecutando las sentencias asociadas a estas otras etiquetas. Esto puede ser una técnica de programación correcta en C/C++.

Frecuentemente, la sentencia switch se utiliza para procesar órdenes de teclado como los menús.

Al igual que ocurre con la sentencia if…else, las sentencias switch también se pueden encajar creando estructuras condicionales complicadas. El diagrama de una sentencia switch es:

Page 32: Módulo 3 - Fundamentos de Programación en C, C++

MP 11 – UF 01 – Módulo 3 – Fundamentos Programación. Página 32

10.5. LAS ESTRUCTURAS REPETITIVAS O BUCLES.

Las estructuras repetitivas o iterativas, también llamadas bucles, son también un elemento esencial de casi todos los lenguajes informáticos. Las sentencias de repetición que soporta C/C++ son: while, do…while y for.

10.5.1. LA SENTENCIA while.

Esta sentencia permite ejecutar repetidamente una sentencia simple o grupo de sentencias mientras se cumple una determinada condición. Su sintaxis es:

while (<expressión_condición>) sentencia_bucle;

El funcionamiento de esta sentencia es el siguiente: en primer lugar se evalúa la expresión que acompaña a la palabra while y que se encuentra entre paréntesis. Si esta expresión da un resultado falso sale del bucle, es decir, se ejecuta la sentencia que hay a continuación de sentencia_bucle.

Si el valor de la expresión es verdadero, se ejecuta sentencia_bucle y se vuelve a evaluar expressión_condición. El resultado es, por tanto, que la sentencia_bucle ejecuta repetidamente hasta que expressión_condición sea falsa.

Hay que tener en cuenta que la condición se evalúa antes de entrar en el bucle, por lo tanto, si la primera vez que se evalúa la expresión de condición, ésta es falsa, no se llega a entrar en el bucle nunca.

En el siguiente ejemplo, el bucle consiste en la asignación a la variable caro de un carácter leído del teclado. El bucle se ejecuta siempre que este carácter no sea el carácter con código ASCII = 27, que corresponde a la tecla ESC:

char car;

car='\0'; while (car !=27) car = getch();

10.5.2. LA SENTENCIA do…while.

Esta sentencia es similar al anterior con la diferencia que la condición se comprueba después de entrar en el bucle, por tanto, el cuerpo del bucle se ejecuta almenos una vez. Su sintaxis es: do{

sentencia; }while (<expresión_condición>);

Las llaves no son necesarias si la sentencia del bucle es una sentencia simple. No obstante, se puede poner por claridad del código.

En el siguiente ejemplo se leen números hasta que se presione un 0:

int n;

do{

scanf("&d", &n);

}while (n!=0);

Page 33: Módulo 3 - Fundamentos de Programación en C, C++

MP 11 – UF 01 – Módulo 3 – Fundamentos Programación. Página 33

10.5.3. EL BUCLE for.

La sentencia for es la forma más versátil de generar una secuencia iterativa. Su sintaxis es: for (<inicialitzación>;<condición>;<incremento>) sentencia;

Esta sentencia es equivalente a la siguiente construcción:

<inicialización>;

while(<condición>){

sentencia;

<actualización> ;

}

Antes de iniciarse el bucle se ejecuta inicialización, que normalmente se utiliza para asignar valores iniciales a variables de control o contadores. La condición es una expresión que normalmente comprueba la variable de control para determinar si debe salir o no del bucle.

El incremento define la forma de cambiar la variable de control en cada ejecución del bucle. Estas tres partes deben separarse con punto y coma (;). El bucle continuará hasta que la condición sea falsa.

El uso más común de esta sentencia es la ejecución de un bucle un número determinado de veces, por ejemplo:

for (int x=1;x<=100;x++) printf("%d ",x);

En este ejemplo se ha utilizado la parte de inicialización para declarar una variable entera que servirá de contador (declarar variables dentro de esta parte de la sentencia for es una característica propia de C + + y no es compatible con C, donde todas las variables se deben declarar al comienzo).

Esta variable contador es iniciada a 1. Cada vez que se ejecute el bucle, que en este caso consiste sólo en la llamada a la función printf (), el valor de la variable es incrementado en una unidad (x + +) y se comprueba si se cumple la condición. El bucle dejará de ejecutarse cuando x sea 101, por lo tanto, la salida de este programa será la impresión de los números del 1 al 100.

10.5.4. EL OPERADOR COMA.

El operador coma (,) evalúa dos expresiones en lugares donde la sintaxis sólo nos permite una. El valor del operador es el valor de la expresión que aparece a la derecha del operador. El formato de la expresión es: expresión_izquierda , expresión_derecha

Normalmente este operador se utiliza en alguna de las partes de los bucles for, cuando se utiliza más de una variable de control. Por ejemplo:

for (i=0,j=0;i<maxi, j<maxj;i++, j++){

10.5.5. LAS SENTENCIAS break Y continue.

La sentencia break, además utilizarse con la sentencia switch como terminación de un caso (case) se puede usar dentro de un bucle para forzar el fin inmediato de éste, saltando la prueba condicional. Cuando se encuentra una sentencia break el control de ejecución vuelve a la sentencia siguiente al bucle.

Page 34: Módulo 3 - Fundamentos de Programación en C, C++

MP 11 – UF 01 – Módulo 3 – Fundamentos Programación. Página 34

Ejemplo: ct=0;

while (ct<100){

if (a(ct)==1253) {

printf("encontrado");

break;

}

ct++;

}

En este ejemplo se comprueba en un vector de 100 componentes si contiene el dato 1253; una vez encontrado sale del bucle.

La sentencia continue hace que salte cualquier código y fuerza una nueva iteración del bucle.

Ejemplo: for(ct=0; ct<100; ct++){

if(ct%3) continue;

printf("el numero %d es múltiplo de 3\n", ct);

}

En este ejemplo, si la variable ct es múltiplo de 3, ct%3 es 0 (falso) y por tanto se visualizará el mensaje "el número ... es múltiplo de 3". En caso de que ct%3 no sea 0 (verdad) no se visualizará el mensaje y se volverá a entrar en el bucle.

10.5.6. LA SENTENCIA goto.

La sentencia goto hace un salto incondicional a una parte del programa determinada. Su sintaxis es la siguiente: goto etiqueta;

En algún lugar de la misma función debe existir una etiqueta con el mismo nombre. Una etiqueta es un identificador válido de C que acaba en dos puntos. Por ejemplo:

if (codi==0) goto error; printf("No hay error\n"); ......... error: printf("Ha hagut un error\n");

La sentencia goto es una sentencia que se debe evitar siempre que se pueda. Normalmente hace que el código sea más difícil de leer. Sólo se recomienda usarla para salir de una estructura complicada de bucles y condicionales en una situación especial.

Page 35: Módulo 3 - Fundamentos de Programación en C, C++

MP 11 – UF 01 – Módulo 3 – Fundamentos Programación. Página 35

11. EJERCICIOS.

1) Ecuación de segundo grado.

Complementando al Ejercicio 7 del Módulo 1, haremos un programa con las modificaciones que se consideren necesarias para que el programa pueda comprobar los datos introducidos, y:

si el coeficiente a es 0 debe calcular la solución de la ecuación de primer grado bx+c=0;

si el coeficiente a es diferente de 0, entonces:

si el discriminante es positivo debe indicar que hay dos soluciones y escribirlas.

si el discriminante es 0 nos debe indicar que hay una única solución y la debe escribir (en este caso, el hecho de sumar o restar la raíz cuadrada del discriminante no supondrá dos soluciones).

si el discriminante es más pequeño que 0 nos debe indicar que la ecuación no tiene soluciones reales.

Denominaremos al programa m3e1.c dentro de la carpeta Módulo_3.

2) Número "curioso".

Diremos que un número natural es "curioso", si es igual a la suma de un cierto número de naturales consecutivos comenzando por cualquier natural y acabando por cualquier natural más pequeño que el mismo.

Los tres primeros números "curiosos" son: el 3, ya que es la suma de 1+2; el 5, ya que es la suma de 2+3; el 6, ya que es la suma de 1+2+3.

Especificaremos una función denominada void curioso(); que recibirá un número natural n y decidirá si es o no es "curioso".

Denominaremos al programa m3e2.c dentro de la carpeta Módulo_3.

3) El MCD de tres números.

En la Práctica 3 se ha visto una función para calcular el Máximo Común Divisor de dos números. Si tenemos tres números en lugar de dos se puede utilizar que:

MCD (a, b, c) = MCD (MCD (a, b), c))

Por ejemplo, si queremos el MCD de 120, 140 y 210, podemos calcular:

MCD (120,140, 210) = MCD (MCD (120, 140), 210)) = 10;

es lo mismo que: MCD (120,140) = 20 y después MCD (20, 210) = 10

Escribiremos un programa que contenga la función vista en la Práctica 3 y la haga servir para calcular el Máximo Común Divisor de tres números.

Denominaremos al programa m3e3.c dentro de la carpeta Módulo_3.

4) Números "XX".

Diremos que un número entero es "XX" si todos sus divisores (excepto el mismo) son números primos.

Escribiremos un programa que solicite un entero positivo N y que muestre por pantalla todos los números "XX" que hay entre 0 y N (ambos no incluidos). Por ejemplo, si el valor de N es 21 la salida del programa deberá ser: 1,2,3,4,5,6,7,9,10,11,13,14,15,17,19

Denominaremos al programa m3e4.c dentro de la carpeta Módulo_3.

Page 36: Módulo 3 - Fundamentos de Programación en C, C++

MP 11 – UF 01 – Módulo 3 – Fundamentos Programación. Página 36

5) Números "Amigos".

Diremos que dos números enteros positivos a y b son "amigos" si la suma de los divisores del primero excepto él mismo es igual a la suma de los divisores del segundo excepto él mismo.

Por ejemplo, para el número 33 encontramos que su "amigo" es el 16:

Número Divisores Suma de los divisores

33 1, 3, 11 15

16 1, 2, 4, 8 15

Para el 128 encontramos que son "amigos" el 1469, el 1853 y el 2033, entre otros:

Número Divisores Suma de los divisores

128 1, 2, 4, 8, 16, 32, 64 127

1469 1, 13, 113 127

1853 1, 17, 109 127

2033 1, 19, 107 127

Haremos un programa que solicite un número entero positivo y nos muestre a un "amigo". Entonces programa dejará de ejecutarse. No es necesario encontrarlos todos.

Denominaremos al programa m3e5.c dentro de la carpeta Módulo_3.

11.1. PROBLEMAS COMPLEMENTARIOS.

6) Cambios de base de numeración.

Debemos escribir un programa que lea un número natural más pequeño de 256 y escriba su representación en binario. Para ello debemos hacer divisiones sucesivas por 2 y quedarnos con los diferentes restos.

Por ejemplo, para calcular la representación binaria del número 100, haremos:

3/2=1 6/2=3 12/2=6 25/2=12 50/2=25 100/2=50

1 3%2=1 6%2=0 12%2=0 25%2=1 50%2=0 100%2=0

Por tanto, 10010 = 11001002.

Haremos que el programa realice la comprobación que el número introducido sea más pequeño que 256 y, en caso contrario, que presente un mensaje de error y vuelva a solicitar otro número.

Añadiremos al programa anterior una función que permita hacer la representación decimal de un número en base 2. Por eso sólo debemos sumar 2n si en la posición n del número binario hay un 1. Ejemplo: 11001002=26+25+22=10010

Denominaremos al programa m3e6.c dentro de la carpeta Módulo_3.

7) Progresiones geométricas.

Una progresión geométrica es una sucesión, el término general de la cual es: a(n) = a · r n

Por ejemplo, si a=2 y r=3, los primeros n=4 términos de la progresión serán: {6, 18, 54, 162}

La suma de los n primeros términos de una sucesión geométrica se puede calcular con la fórmula siguiente, donde a(1) es el primer término, r la razón y n el número de términos:

Page 37: Módulo 3 - Fundamentos de Programación en C, C++

MP 11 – UF 01 – Módulo 3 – Fundamentos Programación. Página 37

Escribiremos un programa que, introducidos los valores de a, r (tipo double) y n (tipo int), imprima los n primeros términos de la sucesión a(n) y calcule la suma de estos n primeros términos haciendo la suma de todos los términos.

Añadiremos alguna línea para poder comprobar la fórmula dada de la suma de los n primeros términos de una progresión geométrica.

Denominaremos al programa m3e7.c dentro de la carpeta Módulo_3.

8) Algoritmo 3n+1.

El problema que se plantea aquí es estudiar uno de los algoritmos más clásicos no resueltos de la ciencia de Algoritmia: el Algoritmo 3n+1. Consideremos el siguiente algoritmo:

1. Entrar n. 2. Imprimir n. 3. Si n=1 entonces ACABA EL PROGRAMA. 4. Si es impar entonces n:=3n+1. 5. En caso contrario, es decir, si es par, entonces n:=n/2. 6. Volver a la línea 2.

Por ejemplo, dado el número 22 el programa imprimiría la siguiente secuencia de números: 22 11 34 17 52 26 13 40 20 10 5 16 8 4 2 1. Esta secuencia recibe el nombre de ciclo del número 22, que es un ciclo de longitud 16.

Es una conjetura no demostrada que este algoritmo acaba siempre, es decir, que el ciclo de todo número entero es un ciclo finito.

Haremos un programa que escriba el ciclo de un número introducido por teclado. Podemos utilizar el programa para investigar ciclos grandes y ciclos pequeños. Por ejemplo, los números de la forma 2n tienen un ciclo corto...

Denominaremos al programa m3e8.c dentro de la carpeta Módulo_3.