You are on page 1of 22

Pontificia Universidad Católica del Perú

Estudios Generales Ciencias

1INF01 - Fundamentos de Programación

Guía de laboratorio #10


Depuración

17 de noviembre de 2018
i

Índice general

Historial de revisiones 1

Siglas 2

1. Guía de Laboratorio #10 3


1.1. Introducción . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3
1.2. Materiales y métodos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3
1.3. Depuración . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3
1.3.1. Nociones previas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3
1.3.2. Depuración de programas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4
1.4. Uso de los depuradores . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4
1.4.1. Depuración en PSeint . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4
1.4.2. Depuración en Dev C++ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6
1.4.3. Tabla de Iteraciones . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7
1.5. Evaluar si un número es primo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9
1.5.1. Evaluar si un número es primo en una función . . . . . . . . . . . . . . . . . . . . . . . 10
1.6. Los números casi primos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11
1.6.1. Leyendo los datos de entrada . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11
1.6.2. Creamos la función f_EvaluarCasiPrimo . . . . . . . . . . . . . . . . . . . . . . . . . . . 11
1.6.3. Usando la función f_EvaluarSiEsPrimo . . . . . . . . . . . . . . . . . . . . . . . . . . . 12
1.6.4. Elaborando la tabla de iteraciones de la función main. . . . . . . . . . . . . . . . . . . . 13
1.7. La conjetura de Goldbach . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14
1.7.1. Leyendo los datos de entrada . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14
1.7.2. Aplicando técnica de fuerza bruta . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14
1.7.3. Creamos la función f_ParPrimosGoldbach . . . . . . . . . . . . . . . . . . . . . . . . . . 14
1.7.4. Usando la función f_EvaluarSiEsPrimo . . . . . . . . . . . . . . . . . . . . . . . . . . . 15
1.8. Ejercicios propuestos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16
1.8.1. Números primos de Sophie Germain . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16
1.8.2. Sucesiones de números . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17
1.8.3. Calculadora de conversión de unidades . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18
Capítulo 0: Índice general ii

1.8.4. Sumatorias de números naturales . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19


1

Historial de Revisiones

Revisión Fecha Autor(es) Descripción


1.0 14.11.2018 D.Allasi Versión inicial.
1.0 17.11.2018 E.Quispe Versión inicial.
2

Siglas

EEGGCC Estudios Generales Ciencias


IDE Entorno de Desarrollo Integrado
PUCP Pontificia Universidad Católica del Perú
3

Capítulo 1

Guía de Laboratorio #10

1.1. Introducción

Esta guía ha sido diseñada para que sirva como una herramienta de aprendizaje y práctica para el curso de
Fundamentos de Programación de los Estudios Generales Ciencias (EEGGCC) en la Pontificia Universidad
Católica del Perú (PUCP). En particular se focaliza en el tema “Depuración”.
Se busca que el alumno resuelva paso a paso las indicaciones dadas en esta guía contribuyendo de esta manera a
los objetivos de aprendizaje del curso, en particular con la comprensión de la etapa de depuración de programas.
Al finalizar el desarrollo de esta guía y complementando lo que se realizará en el correspondiente laboratorio,
se espera que el alumno:

Comprenda el funcionamiento de la depuración de programas.


Elabore tabla de iteraciones para seguimiento de variables.

1.2. Materiales y métodos

Como herramienta para el diseño de pseudocódigos y diagramas de flujo se utilizará PSeInt1 . El PSeInt deberá
estar configurado usando el perfil PUCP definido por los profesores del curso. Como lenguaje de programación
imperativo se utilizará el lenguaje ANSI C. Como Entorno de Desarrollo Integrado (IDE) para el lenguaje
ANSI C se utilizará Dev C++2 . No obstante, es posible utilizar otros IDEs como Netbeans y Eclipse.

1.3. Depuración

1.3.1. Nociones previas

Los programas escritos en el paradigma imperativo se basan en el cambio de estado de las variables definidas
en los programas. Todo programa sigue un flujo el cual puede ser cambiado a partir de estructuras de control
de flujo. Las estructuras de control de flujo pueden ser de dos tipos: estructuras algoritmicas selectivas y
estructuras algoritmicas iterativas.
Las estructuras selectivas, vistas anteriormente, permiten que los programas ejecuten un conjunto de instruc-
ciones si es que cumplen determinada condición. Es decir, el conjunto de instrucciones se ejecuta una sola vez
1 http://pseint.sourceforge.net/
2 http://sourceforge.net/projects/orwelldevcpp
Capítulo 1: Guía de Laboratorio #10 4

si cumple la condición. Por otra parte, las estructuras iterativas permiten que los programas ejecuten un con-
junto de instrucciones tantas veces como sea necesario, dependiendo de determinada condición. Dependiendo
de cuándo se realiza la verificación de esta condición, podemos tener ciclos iterativos con entrada controlada
y ciclos iterativos con salida controlada.
Aunque un programa en primera instancia no marque errores de sintaxis, es fundamental incorporar una etapa
de depuración, la cual nos ayudará a realizar seguimiento a las variables que se manejan en el programa y nos
permitirá verificar si el programa cumple con el objetivo requerido.

1.3.2. Depuración de programas

Cuando se escriben programas, es altamente probable que se puedan cometer errores, por lo que la probabilidad
de que el programa funcione en la primera ejecución es baja. Los errores que podemos comenter al momento
de escribir un programa pueden ser de los siguientes tipos: sintaxis, ejecución y lógicos.
Los errores de sintaxis son detectados por el compilador del IDE que estamos utilizando para implementar
nuestro programa, por ello son los más fáciles de detectar y solucionar. Sin embargo, no les ha pasado que
muchas veces nuestro programa compila, es decir no tiene errores de sintaxis, pero no cumple con el objetivo
buscado. ¿Qué es lo que sucede entonces en esos casos? Lo más probable es que hayamos cometido algún error
al momento de analizar y/o diseñar nuestro algoritmo y esto nos lleva a los errores de ejecución y lógicos.
Los errores de ejecución son aquellos que el computador puede entender pero no ejecutar, algunos ejemplos de
ellos son: división entre cero y la raíz cuadrada de un número negativo. Estos tipos de errores se dan porque
en algun punto del programa, alguna variable ha tomado un valor que no permite ejecutar alguna instrucción
definida (división entre cero) y el computador indica ello al momento de la ejecución. Los errores lógicos son
más difíciles de encontrar y es necesario realizar un seguimiento paso a paso de nuestro programa para poder
detectarlos y solucionarlos, dando paso con esto a la etapa de depuración.
Por lo tanto, el depurar un programa consiste en realizar un seguimiento, paso a paso, de todas las instrucciones
y variables del programa, con la finalidad de conocer los valores que estas pueden tener en un momento
determinado de la ejecución y nos permita validar que el programa este funcionando correctamente ó nos
permita encontrar aquellos errores lógicos que debemos solucionar.
Para realizar depuración de programas, PSeint y Dev C++ poseen un depurador, el cual es una herramienta
que permite intervenir durante la ejecución de un programa y saber como se esta ejecutando. Adicional a
ello, también podemos realizar un seguimiento manual de variables, sobre todo cuando utilizamos estructuras
iterativas, utilizando una tabla de iteraciones. El uso de los depuradores y de la tabla de iteraciones los veremos
posteriormente.

1.4. Uso de los depuradores

A continuación se revisará como se pueden utilizar los depuradores en los programas PSeint y Dev C++.

1.4.1. Depuración en PSeint

Como ya sabemos, PSeint es una herramienta que nos permite diseñar algoritmos, en pseudocodigo o diagrama
de flujo, para dar solución a un problema planteado.
También nos permite realizar depuración a nuestros algoritmos, de manera que podamos tener la certeza de
que cumplen correctamente con el objetivo requerido. El depurador se puede ejecutar de 2 maneras:

Dar click en el menú Ejecutar y seleccionar la opción Ejecutar paso a paso o presionar F5, ver figura 1.1.

Dar click en el panel Ejecutar paso a paso (dos pies) que se encuentra en la barra de herramientas, ver
figura 1.2.
Capítulo 1: Guía de Laboratorio #10 5

Figura 1.1: PSeInt: Depurador - Menú Ejecutar - Ejecutar paso a paso

Figura 1.2: PSeInt: Depurador - Ejecutar paso a paso

Una vez iniciado el depurador, en el lado derecho de la pantalla aparece una ventana Paso a paso, ver figura
1.3, en la cual se muestra los siguientes controles:

Comenzar.- Al presionar este control se inicia la ejecución paso a paso del algoritmo en forma continua.
Pausar.- Al presionar este control se detiene la ejecución paso a paso del algoritmo, quedando el cursor
de ejecución en la línea correspondiente.

Primer Paso/Avanzar un Paso.- Al presionar este control se inicia la ejecución paso a paso del
algoritmo, ejecutando una linea de instrucción y deteniendo la ejecución inmediatamente.
Evaluar.- Este control se habilita cuando la depuración se encuentra en pausa. Al presionar este control
aparece una nueva ventana donde nosotros podemos colocar el nombre de la variable cuyo valor que
queremos evaluar, presionamos Enter y nos muestra el valor que tiene la variable al momento de la
pausa.
Velocidad.- Este control permite calibrar la velocidad de ejecución de cada instrucción al presionar el
control Comenzar.
Entrar en subprocesos.- Cuando este control se encuentra habilitado (check), el depurador también
ejecuta paso a paso las instrucciones de las funciones que pueda tener la solución.

Prueba de Escritorio.- Cuando este control se encuentra habilitado (check), permite crear una tabla
de inspección de variables, permitiendo de esta manera evaluar más de una variable a la vez al ejecutar
cada instrucción de manera continua.
Explicar en detalle c/paso.- Cuando este control se encuentra habilitado (check), se muestra una
sección donde el depurador indica paso a paso lo que esta ejecutando en la linea de instrucción. Esta
ejecución se realiza de forma pausada y deteniendose en cada linea.

Figura 1.3: PSeInt: Depurador - Sección Paso a paso

El depurador, en PSeint, se puede ejecutar tanto para pseudocódigo (ver figura 1.4) como diagrama de flujo
(ver figura 1.5)
Capítulo 1: Guía de Laboratorio #10 6

Figura 1.4: Depuración en pseudocódigo

Figura 1.5: Depuración en diagrama de flujo

1.4.2. Depuración en Dev C++

Al igual que PSeInt, el IDE Dev C++ también posee una herramienta para depurar programas. Esta herra-
mienta de depuración posee un conjunto de funcionalidades que nos permiten detectar y corregir errores de
ejecución o lógicos. Entre las funcionalidades más importantes tenemos:

Colocar un punto de parada.- El colocar un punto de parada (breakpoint) obliga al programa,


durante su ejecución, a detenerse cuando alcanza dicho punto. Los puntos de parada son útiles en la
depuración, pues permiten al programador observar el estado (valores) de las variables en un momento
concreto de la ejecución del programa y no solo el estado final de los mismos. Hay 2 formas de activar
un punto de parada. La más sencilla es dar click con el mouse en la zona del margen junto a la línea
en la que queremos situar el punto de parada. Otra forma es posicionar el cursor en la línea en la cual
queremos que aparezca un punto de parada y dar click en el menú Ejecutar, opción Añadir/Quitar punto
de interrupción. En cualquiera de las 2 formas de activación, se visualizará una línea con fondo rojo, y
un ícono rojo con un check de color verde en su interior en la zona del margen izquierdo, tal como se
muestra en la figura 1.6. Cabe precisar que un punto de parada se puede colocar tanto en la función
main como en las otras funciones implementadas.

Figura 1.6: Depuración DevC++ Punto de Parada

Ejecución de un programa en modo depuración.- Hay 3 formas de activar esta opción. Una forma
consiste en pulsar el ícono de check, ver figura 1.7, que se encuentra en la barra de herramientas. Otra
forma es hacerlo dando click en el menú Ejecutar, opción Depurar y la última es presionando F5. En
cualquiera de los 3 casos el programa se ejecutará hasta alcanzar un punto de parada. Solo en dicho
momento, el control de la ejecución vuelve al entorno del Dev C++ mostrándose la línea de ejecución
actual con fondo azul con un icono en forma de flecha en el margen, tal como se muestra en la figura 1.8.
Capítulo 1: Guía de Laboratorio #10 7

Figura 1.7: Depuración DevC++ - Ejecución en modo depuración

Figura 1.8: Depuración DevC++

Ejecución Paso a paso.-Es la funcionalidad más importante del depurador. Mediante ella podemos
ejecutar una y solo una instrucción del programa a la vez, permitiendonos tener el control de la ejecución
de manera que podemos comprobar los valores de las variables y verificar si los efectos de la instrucción
realizada son los deseados. Para ejecutar paso a paso podemos pulsar la opción de ’Saltar Paso’, la cual
nos lleva a seguir ejecutando el programa hasta encontrar un nuevo punto de parada. También podemos
pulsar la opción ’Siguiente Paso’ o ’Avanzar paso a paso’, de manera que avancemos la ejecución paso
a paso por cada instrucción. La diferencia entre la opción ’Siguiente Paso’ y ’Avanzar paso a paso’
consiste básicamente en que si la instrucción a ejecutar contiene una llamada a una función, con la
opción ’Siguiente Paso’ podemos omitir el ingreso a ejecutar las instrucciones de la función y ejecutar la
sentencia como única, obteniendo de esta manera el resultado de la función. Con la opción ’Avanzar paso
a paso’ si se ejecutarán paso a paso las instrucciones de la función invocada. Estas opciones se pueden
apreciar en la figura 1.9. Tal como dijimos anteriormente, la ejecución del programa paso a paso se realiza
con la línea en fondo azul, la cual se marca automáticamente conforme vamos avanzando en la ejecución.
Si se desea suspender la ejecución paso a paso, se debe presionar el icono X (rojo), ver figura1.7, que
se encuentra en la barra de herramientas, dando click en el menú Ejecutar, opción Parar Ejecución o
presionando F6.

Figura 1.9: Depuración DevC++ - Controles paso a paso

Inspeccionar las variables (watch).-Si se desea monitorear una o varias variable durante la depura-
ción de nuestro programa, se debe agregar una inspección (watch) a dicha variable, por lo tanto siempre
habrá un watch por cada variable que se desea monitorear. Para ello se presiona la opción Añadir watch
del menú Ejecutar. En ese momento aparecerá un cuadro de diálogo en donde escribiremos el nombre
de la variable del ’wacth’. En la columna de la izquierda aparecerá la variable observada junto al valor
que va teniendo durante la ejecución. Para eliminar un watch se presiona la opción Quitar watch del
menú Ejecutar. La creación y/o eliminación de un ’watch’ se puede dar en cualquier momento, durante
la depuración de un programa.

1.4.3. Tabla de Iteraciones

Es posible realizar un seguimiento de forma manual a las variables que se utilizan dentro de nuestro programa
o algoritmo. Para realizar este seguimiento se recomienda crear una tabla de iteraciones, la cual nos permite
ver el comportamiento de las variables, conforme se va ejecutando las instrucciones de las estructuras iterativas
y a lo largo del programa. Para mostrar su uso, realizaremos una depuración (seguimiento) a un programa que
Capítulo 1: Guía de Laboratorio #10 8

imprime los primeros ’n’ numeros primos. Donde ’n’ es un número entero mayor que cero y el cual es ingresado
por el usuario.
A continuación se muestra el código del programa
Programa 1.1: Imprimir los primeros ’n’ números primos
1 #include <s t d i o . h>
2
3 i n t f_EvaluarSiEsPrimo ( i n t numero ) {
4 i n t c a n t i d a d _ D i v i s o r e s =0 , i =1 , esPrimo =0;
5 while ( i <=numero ) {
6 i f ( ( numero % i ) ==0)
7 c a n t i d a d _ D i v i s o r e s ++;
8 i ++;
9 }
10 i f ( c a n t i d a d _ D i v i s o r e s ==2)
11 esPrimo = 1 ;
12 return esPrimo ;
13 }
14
15 i n t main ( ) {
16 i n t c a n t i d a d , numero =2 , contador_Primos =0 , esPrimo ;
17 p r i n t f ( " I n g r e s e l a c a n t i d a d de p r i m e r o s numeros primos que d e s e a m o s t r a r : " ) ;
18 s c a n f ( " %d" ,& c a n t i d a d ) ;
19 while ( contador_Primos<c a n t i d a d ) {
20 esPrimo = f_EvaluarSiEsPrimo ( numero ) ;
21 i f ( esPrimo==1) {
22 p r i n t f ( " %d \n" , numero ) ;
23 contador_Primos++;
24 }
25 numero++;
26 }
27 return 0 ;
28 }

Como se puede apreciar en el programa, se ha implementado una función llamada f_EvaluarSiEsPrimo que
recibe como parámetro un número y retorna el valor de 0 si el número recibido como parámetro no es primo y
el valor de 1 si el número si es primo. Adicionalmente, la función main va recorriendo uno a uno los números,
evalúa si es primo y en caso de serlo lo imprime. Para la evaluación de si el número es primo invoca (llama)
a la función f_EvaluarSiEsPrimo. Iniciaremos con el seguimiento a la función main, para ello elaboramos la
tabla de iteraciones, tal como se muestra en la figura 1.10.

Figura 1.10: Imprimir Números Primos: Tabla de Iteraciones del main

Como se puede observar en la figura 1.10, se ha elaborado la tabla de iteraciones de la función main. La iteración
0 muestra los valores iniciales de las variables antes de entrar al ciclo iterativo con entrada controlada while.
Se puede apreciar que para un valor de cantidad=5 ingresado por el usuario, el ciclo iterativo de la función
main realiza 10 iteraciones en total para calcular e imprimir los 5 primeros números primos.
Ahora elaboraremos la tabla de iteraciones de la función f_EvaluarSiEsPrimo, para ello seleccionaremos uno
de los valores de la variable número, la cual es colocada como parámetro de la función f_EvaluarSiEsPrimo
cuando es invocada desde la función main. Para la elaboración consideraremos a número=6. La tabla de
iteraciones elaborada se muestra en la figura 1.11.
Capítulo 1: Guía de Laboratorio #10 9

Figura 1.11: Imprimir Números Primos: Tabla de Iteraciones de la función f_EvaluarSiEsPrimo

En la figura 1.11 se puede apreciar que la función f_EvaluarSiEsPrimo cuando es invocada para el número 6,
realiza 5 iteraciones para determinar que el número 6 no es primo. Al finalizar la 5ta iteración sale del ciclo
iterativo y evalua la estructura selectiva de la línea 10, al no cumplirse la condición, la variable esPrimo se
mantiene con su valor inicial de 0, lo cual significa que el número 6 no es primo.
Como pueden observar, hemos dado seguimiento de forma manual a las variables del programa y para ello
utilizamos las tablas de iteraciones.

1.5. Evaluar si un número es primo

En matemáticas, un número primo es un número natural mayor que 1 que tiene únicamente 2 divisores: el
número 1 y él mismo.
Este concepto de número primo es muy importante en la teoría de números, estando presentes en algunas
conjeturas centenarias como las hipótesis de Riemann y la conjetura de Goldbach. También se encuentran
presentes en las definiciones de otros números primos como los números primos de Mersenne, los números
primos gemelos, los números primos de Sophie Germain, los números casi primos, entre otros.
A continuación se muestra un algoritmo que permite evaluar si un número es primo o no.

Programa 1.2: Evaluar si un número es primo


1 #include <s t d i o . h>
2
3 i n t main ( ) {
4 i n t c a n t _ d i v i s o r e s =0 , numero , i ;
5 p r i n t f ( " I n g r e s e e l numero a e v a l u a r : " ) ;
6 s c a n f ( " %d" ,&numero ) ;
7 i f ( numero > 1 ) {
8 while ( i <=numero ) {
9 i f ( numero % i == 0 )
10 c a n t _ d i v i s o r e s ++;
11 i ++;
12 }
13 i f ( c a n t _ d i v i s o r e s == 2 )
14 p r i n t f ( " El numero %d e s primo " , numero ) ;
15 else
16 p r i n t f ( " El numero %d no e s primo " , numero ) ;
17 }
18 else
19 p r i n t f ( " El numero %d no e s primo " , numero ) ;
20 return 0 ;
21 }

La salida que genera este programa es la siguiente:

Ingrese el numero a evaluar: 7


El numero 7 es primo
Capítulo 1: Guía de Laboratorio #10 10

Recordar que:

Un número es primo si solo es divisible entre si mismo y 1.

¿Qué ocurre si queremos utilizar la evaluación que acabamos de realizar como parte de otros programas?. Para
el siguiente ejemplo implementaremos un programa que imprima los primeros ’n’ numeros primos, donde ’n’
es un número natural mayor que 0, el cual es ingresado por el usuario.

1.5.1. Evaluar si un número es primo en una función

Podemos modificar el Programa 1.2 de manera que la evaluación de si un número es primo se realice dentro
de una función f_EvaluarSiEsPrimo, la cual retornará el valor de 1 si el número evaluado es primo y 0 si no
lo es. El programa desarrollado se muestra a continuación.

Programa 1.3: Evaluar si un número es primo dentro de una función


1 #include <s t d i o . h>
2
3 i n t f_EvaluarSiEsPrimo ( i n t numero ) {
4 i n t c a n t _ d i v i s o r e s =0 , i , esPrimo =0;
5 i f ( numero >1){
6 f o r ( i =1; i <=numero ; i ++){ /∗ Se c a l c u l a l a c a n t i d a d de d i v i s o r e s d e l n m e r o ∗/
7 i f ( numero %i ==0)
8 c a n t _ d i v i s o r e s ++;
9 }
10 i f ( c a n t _ d i v i s o r e s ==2)
11 esPrimo = 1 ;
12 }
13 return esPrimo ;
14 }
15
16 i n t main ( ) {
17 i n t i =2 , cant_Primos , n , esPrimo ;
18 p r i n t f ( " I n g r e s e l a c a n t i d a d de n p r i m e r o s numeros primos a m o s t r a r : " ) ;
19 s c a n f ( " %d" ,&n ) ;
20 i f ( n>0){
21 while ( cant_Primos<=n )
22 {
23 esPrimo = f_EvaluarSiEsPrimo ( i ) ;
24 i f ( esPrimo )
25 {
26 cant_Primos++;
27 p r i n t f ( " El numero %d e s primo \n" , i ) ;
28 }
29 i ++;
30 }
31 }
32 else
33 p r i n t f ( " El numero i n g r e s a d o no e s c o r r e c t o " ) ;
34 return 0 ;
35 }

Podemos apreciar que la funcion main realiza la evaluación de si un número es primo o no, utilizando a la
función f_EvaluarSiEsprimo.
Adicionalmente, en la función main se ha creado una variable i que manejará los números que serán enviados
como parámetros a la función f_EvaluarSiEsPrimo para determinar si se trata de un número primo. Si la
función devuelve el valor de 1, entonces el número enviad es primo y se contabiliza dicho número como primo
(línea 26), imprimiéndose el mensaje correspondiente. Se aumenta el valor de la variable i para evaluar el
siguiente número. Este ciclo iterativo termina cuando contemos con la cantidad de números primos solicitados.
Si revisamos la estructura de nuestro programa, podemos apreciar que:

La función f_EvaluarSiEsPrimo declarada en la línea 3 tiene tipo de retorno int


Capítulo 1: Guía de Laboratorio #10 11

La función f_EvaluarSiEsPrimo recibe un parámetro del tipo int que representa al número que va a
evaluar si es primo o no.
En la línea 23, la variable esPrimo es del mismo tipo que la función (int).

1.6. Los números casi primos

En diversos concursos de programación, la definición del número casi primo esta presente y suele variar algunas
veces. Un número casi primo es aquel número que no es primo y que dentro de sus divisores solo tiene un
número primo.
Por ejemplo:

El número 4 es un número casi primo, debido a que no es primo y dentro de sus divisores que son el 1,
2 y 4 solo tiene un número primo, que es el 2.
El número 6 no es un número casi primo, debido a que si bien es cierto no es un número primo, dentro
de sus divisores que son el 1, 2, 3 y 6 tiene 2 divisores que son primos, el 2 y 3.

El usuario ingresará 2 números a y b, donde a>0 y b>0 y a<b, con lo cual se busca representar un rango de
números. El programa debe mostrar los números casi primos que existen en ese rango e indicar cuantos son.

1.6.1. Leyendo los datos de entrada

Para resolver el problema será necesario conocer los valores de a y b que representan el rango de números a
evaluar. Estos valores serán ingresados a través de la consola para luego ser usados en nuestro programa. Una
versión inicial de nuestro programa podría ser la siguiente:

Programa 1.4: Lectura y validación de datos


1 #include <s t d i o . h>
2
3 i n t main ( ) {
4 int i , a , b ;
5 p r i n t f ( " I n g r e s e e l rango a y b : " ) ;
6 s c a n f ( " %d %d" ,&a ,&b ) ;
7 i f ( a>0 && b>0 && a<b ) {
8 /∗ Completar ∗/
9 }
10 else
11 p r i n t f ( " Los d a t o s i n g r e s a d o s no son c o r r e c t o s " ) ;
12 return 0 ;
13 }

1.6.2. Creamos la función f_EvaluarCasiPrimo

Lo que se solicita en el problema es hallar los números casi primos que existen en un rango, por lo que sería
una buena idea encapsular la evaluación de si un número es casi primo en una función, la cuál recibiría como
parámetro el número a evaluar y devolvería 1 si el número en evaluación es casi primo y 0 si no lo es. Esta
función sería utilizada en la función main.
De la definición de un número casi primo podemos inferir que, para determinar si un número es casi primo
debemos considerar dentro de la evaluación si dicho número es primo o no, y en caso no lo fuese debemos saber
cuantos divisores primos tiene. Entonces vemos claramente que dentro de esta función utilizaremos nuestra
función f_EvaluarSiEsPrimo definida anteriormente.
Una segunda versión de nuestro programa podría quedar así.
Capítulo 1: Guía de Laboratorio #10 12

Programa 1.5: Lectura y validación de datos


1 #include <s t d i o . h>
2
3 i n t f_EvaluarCasiPrimo ( i n t numero )
4 {
5 i n t e s C a s i P r i m o =1; /∗ P a r t i r e m o s asumiendo que e l n m e r o s i e s c a s i primo ∗/
6 /∗ Debemos e v a l u a r l a s c o n d i c i o n e s de c a s i primo u t i l i z a n d o l o s c o n c e p t o s de n m e r o s
primos ∗/
7 /∗ Completar ∗/
8
9 return e s C a s i P r i m o ;
10 }
11
12 i n t main ( ) {
13 i n t i , a , b , c o n t _ c a s i P r i m o s =0 , e s C a s i P r i m o ;
14 p r i n t f ( " I n g r e s e e l rango a y b : " ) ;
15 s c a n f ( " %d %d" ,&a ,&b ) ;
16 i f ( a>0 && b>0 && a<b ) {
17 /∗ A q u r e c o r r e m o s con l a v a r i a b l e i t o d o s l o s n m e r o s que e s t a n en e l rango
de a y b ∗/
18 f o r ( i=a ; i <=b ; i ++){
19 e s C a s i P r i m o = f_EvaluarCasiPrimo ( i ) ;
20 i f ( esCasiPrimo ) {
21 p r i n t f ( " El numero %d e s c a s i primo \n" ) ;
22 c o n t _ c a s i P r i m o s ++;
23 }
24 }
25 p r i n t f ( "En e l rango de %d a %d e x i s t e n %d numeros c a s i primos " , a , b ,
cont_casiPrimos ) ;
26 }
27 else
28 p r i n t f ( " Los d a t o s i n g r e s a d o s no son c o r r e c t o s " ) ;
29 return 0 ;
30 }

1.6.3. Usando la función f_EvaluarSiEsPrimo

Dentro de la evaluación de si un número es casi primo se necesita evaluar si dicho numero no es primo y
si tiene exactamente solo 1 divisor primo. Teniendo en cuenta que anteriormente hemos implementado una
función que evalua si un número es primo, podemos incluir dicha función dentro de la lógica de la función
f_EvaluarCasiPrimo y llamarla donde sea necesario.
Con este cambio, nuestro programa finalmente quedaría así:

Programa 1.6: Lectura y validación de datos


1 #include <s t d i o . h>
2
3 i n t f_EvaluarSiEsPrimo ( i n t numero ) {
4 i n t c a n t _ d i v i s o r e s =0 , i , esPrimo =0;
5 i f ( numero >1){
6 f o r ( i =1; i <=numero ; i ++){ /∗ Se c a l c u l a l a c a n t i d a d de d i v i s o r e s d e l n m e r o ∗/
7 i f ( numero %i ==0)
8 c a n t _ d i v i s o r e s ++;
9 }
10 i f ( c a n t _ d i v i s o r e s ==2)
11 esPrimo = 1 ;
12 }
13 return esPrimo ;
14 }
15
16 i n t f_EvaluarCasiPrimo ( i n t numero )
17 {
18 i n t e s C a s i P r i m o =0 , cant_Div_Primos =0 , i , esPrimo , e s D i v i s o r P r i m o ;
19 esPrimo = f_EvaluarSiEsPrimo ( numero ) ;
20 i f ( ! esPrimo ) {
21 f o r ( i =1; i <=numero ; i ++){
Capítulo 1: Guía de Laboratorio #10 13

22 i f ( numero %i ==0){
23 e s D i v i s o r P r i m o = f_EvaluarSiEsPrimo ( i ) ;
24 i f ( e s D i v i s o r P r i m o ==1)
25 cant_Div_Primos++;
26 }
27 }
28 /∗ Se cumple ambas c o n d i c i o n e s , e l n m e r o no e s primo y s o l o t i e n e 1 d i v i s o r
primo ∗/
29 i f ( cant_Div_Primos==1)
30 e s C a s i P r i m o =1;
31 }
32 return e s C a s i P r i m o ;
33 }
34
35 i n t main ( ) {
36 i n t i , a , b , c o n t _ c a s i P r i m o s =0 , e s C a s i P r i m o ;
37 p r i n t f ( " I n g r e s e e l rango a y b : " ) ;
38 s c a n f ( " %d %d" ,&a ,&b ) ;
39 i f ( a>0 && b>0 && a<b ) {
40 /∗ A q u r e c o r r e m o s con l a v a r i a b l e i t o d o s l o s n m e r o s que e s t a n en e l rango
de a y b ∗/
41 f o r ( i=a ; i <=b ; i ++){
42 e s C a s i P r i m o = f_EvaluarCasiPrimo ( i ) ;
43 i f ( esCasiPrimo ) {
44 p r i n t f ( " El numero %d e s c a s i primo \n" , i ) ;
45 c o n t _ c a s i P r i m o s ++;
46 }
47 }
48 p r i n t f ( "En e l rango de %d a %d e x i s t e n %d numeros c a s i primos " , a , b ,
cont_casiPrimos ) ;
49 }
50 else
51 p r i n t f ( " Los d a t o s i n g r e s a d o s no son c o r r e c t o s " ) ;
52 return 0 ;
53 }

Para pensar (A)

¿Será posible obtener una solución distinta a la propuesta?

1.6.4. Elaborando la tabla de iteraciones de la función main.

Con la finalidad de realizar un seguimiento al programa desarrollado, elaboraremos la tabla de iteraciones de


la función main para validar que todo funcione correctamente. Se considera como valores de a y b los números
2 y 10 respectivamente. La tabla elaborada se muestra en la figura 1.12.

Figura 1.12: Números Casi Primos: Tabla de Iteraciones de la función main


Capítulo 1: Guía de Laboratorio #10 14

1.7. La conjetura de Goldbach

La conjetura de Golbach fue enunciada en 1742 por Christian Goldbach y se basa en el siguiente enunciado:
Todo número par mayor que 2 puede escribirse como suma de dos números primos. Este enunciado sencillo es,
en la teoría de números, uno de los problemas abiertos más antiguos en matemáticas.
Utilizando este enunciado, vamos a implementar un programa que solicitará al usuario que ingrese un número
’n’, el cual debe ser mayor que 0 y que representará a la cantidad de números pares que vamos a procesar, y
determine para cada numero, un par de números primos que cumplen con la conjetura de Goldbach. Considere
que el primer número par a evaluar será el número 4.

1.7.1. Leyendo los datos de entrada

Para resolver el problema será necesario conocer el valor de ’n’ que representa la cantidad de números pares a
evaluar iniciando desde el número 4. Este valor será ingresado a través de la consola para luego ser utilizado
en nuestro programa. Una versión inicial del mismo podría tener la siguiente forma:

Programa 1.7: Lectura y validación de datos


1 #include <s t d i o . h>
2
3 i n t main ( )
4 {
5 int n ;
6 p r i n t f ( " I n g r e s e l a c a n t i d a d de numeros p a r e s a e v a l u a r : " ) ;
7 s c a n f ( " %d" ,&n ) ;
8 i f ( n>0)
9 {
10 /∗ Completar ∗/
11 }
12 else
13 p r i n t f ( "La c a n t i d a d i n g r e s a d a e s i n v a l i d a " ) ;
14 return 0 ;
15 }

1.7.2. Aplicando técnica de fuerza bruta

Al analizar el problema, notamos que se nos esta pidiendo encontrar, por cada número par, la combinación
de 2 números primos que sumados den el número a evaluar. ¿Comó lograríamos hallar estos números primos?
Una forma seria realizar la mayor cantidad de combinaciones posibles de números primos. ¿Cómo realizamos
esto?
Lo realizaremos aplicando la técnica de fuerza bruta, la cual nos permite evaluar todas las posibles combi-
naciones candidatas para resolver el problema. Las combinaciones la realizaremos hasta encontrar un par de
números primos que sumados den el número a evaluar.
Para dividir la solución del problema, crearemos una función llamada f_ParPrimosGoldbach que reciba como
parámetros un número a evaluar y devuelva 2 números primos que satisfagan la conjetura de Goldbach. Note
que como la función devolverá más de un valor, estos no pueden ser devueltos de forma natural, por lo que
deben ser considerados dentro de los parámetros de la misma como direcciones de memoria (uso del *), de
manera que los valores de dichas variables puedan ser modificados dentro de la función y devueltos hacia la
función main.

1.7.3. Creamos la función f_ParPrimosGoldbach

De acuerdo a lo explicado en el punto anterior, la función f_ParPrimosGoldbach a crear recibirá como paráme-
tro un número a evaluar y devolverá, también como parámetros, el par de números que satisfacen la conjetura
de Goldbach.
Capítulo 1: Guía de Laboratorio #10 15

Dentro de esta función aplicaremos la técnica de fuerza bruta para hallar dicho par de números y nos deten-
dremos en la búsqueda cuando encontremos un par de ellos.
Una segunda versión del programa es la siguiente:

Programa 1.8: Lectura y validación de datos


1 #include <s t d i o . h>
2
3 i n t f_ParPrimosGoldbach ( i n t numero , i n t ∗num1 , i n t ∗num2 ) {
4 /∗ T cnica de Fuerza Bruta con 2 f o r para r e a l i z a r t o d a s l a s c o m b i n a c i o n e s ∗/
5 f o r ( i =1; i <=numero ; i ++){
6 /∗Tenemos que e v a l u a r s i l a v a r i a b l e i e s primo ∗/
7 f o r ( j =1; j<=numero ; j ++){
8 /∗Tenemos que e v a l u a r s i l a v a r i a b l e j e s primo − Completar ∗/
9 }
10 }
11 }
12
13 i n t main ( )
14 {
15 int n ;
16 p r i n t f ( " I n g r e s e l a c a n t i d a d de numeros p a r e s a e v a l u a r : " ) ;
17 s c a n f ( " %d" ,&n ) ;
18 i f ( n>0)
19 {
20 /∗ Completar ∗/
21 }
22 else
23 p r i n t f ( "La c a n t i d a d i n g r e s a d a e s i n v a l i d a " ) ;
24 return 0 ;
25 }

1.7.4. Usando la función f_EvaluarSiEsPrimo

Dentro del cálculo de un par de números que cumplan con la conjetura de Goldbach se necesita evaluar si dichos
numeros son primos y, teniendo en cuenta que anteriormente hemos implementado una función que evalua si
un número es primo, podemos incluir dicha función dentro de la lógica de la función f_ParPrimosGoldbach y
llamarla donde sea necesario.
Con este cambio, nuestro programa finalmente quedaría así:

Programa 1.9: Lectura y validación de datos


1 #include <s t d i o . h>
2
3 i n t f_EvaluarSiEsPrimo ( i n t numero ) {
4 i n t c a n t _ d i v i s o r e s =0 , i , esPrimo =0;
5 i f ( numero >1){
6 f o r ( i =1; i <=numero ; i ++){ /∗ Se c a l c u l a l a c a n t i d a d de d i v i s o r e s d e l n m e r o ∗/
7 i f ( numero %i ==0)
8 c a n t _ d i v i s o r e s ++;
9 }
10 i f ( c a n t _ d i v i s o r e s ==2)
11 esPrimo = 1 ;
12 }
13 return esPrimo ;
14 }
15
16 i n t f_ParPrimosGoldbach ( i n t numero , i n t ∗num1 , i n t ∗num2 ) {
17 i n t i , j , esPrimoI , esPrimoJ ;
18 /∗ T cnica de Fuerza Bruta con 2 f o r para r e a l i z a r t o d a s l a s c o m b i n a c i o n e s ∗/
19 f o r ( i =1; i <=numero ; i ++){
20 e s P r i m o I = f_EvaluarSiEsPrimo ( i ) ;
21 i f ( esPrimoI ) {
22 f o r ( j =1; j<=numero ; j ++){
23 esPrimoJ = f_EvaluarSiEsPrimo ( j ) ;
24 i f ( esPrimoJ )
Capítulo 1: Guía de Laboratorio #10 16

25 {
26 i f ( i+j==numero ) {
27 ∗num1 = i ;
28 ∗num2 = j ;
29 }
30 }
31 }
32 }
33 }
34 }
35
36 i n t main ( ) {
37 i n t n , numero =4 , i =1 , num1 , num2 ;
38 p r i n t f ( " I n g r e s e l a c a n t i d a d de numeros p a r e s a e v a l u a r : " ) ;
39 s c a n f ( " %d" ,&n ) ;
40 i f ( n>0){
41 while ( i <=n ) {
42 f_ParPrimosGoldbach ( numero ,&num1,&num2 ) ;
43 i f ( num1>0 && num2>0)
44 p r i n t f ( "La suma de %d + %d = %d . Se cumple l a c o n j e t u r a de
Goldbach \n" , num1 , num2 , numero ) ;
45 numero = numero + 2 ;
46 i ++;
47 }
48 }
49 else
50 p r i n t f ( "La c a n t i d a d i n g r e s a d a e s i n v a l i d a " ) ;
51 return 0 ;
52 }

Note que en la función main, en la llamada a la función f_ParPrimosGoldbach se esta colocando el símbolo &
delante de las variables que serán devueltas por la función. De esta manera la función main recibirá, en dichas
variables, los valores obtenidos al terminar de ejecutarse la función f_ParPrimosGoldbach.

1.8. Ejercicios propuestos

Para los siguientes ejercicios propuestos y utilizando funciones, elabore un algoritmo expresado en pseudocó-
digo, en diagrama de flujo así como la implementación del mismo a través de un programa en ANSI C.

1.8.1. Números primos de Sophie Germain

Se solicita implementar un programa que permita ingresar un número n mayor que 0 y muestre los primeros
n pares de números primos de Sophie Germain.
Un número primo p es un número primo de Sophie Germain si 2p + 1 también es un número primo. Ejemplo:
Con p = 2, 2 ∗ 2 + 1 = 5 que también es un número primo. Los números primos de Sophie Germain recibieron
su nombre por la matemática francesa que demostró que el teorema de Fermat era cierto para estos números.
Un número es primo si solamente es divisible entre el mismo número y 1.
Por ejemplo: Si usuario ingresa n = 5, se debe mostrar o imprimir los primeros 5 pares de números primos de
Sophie Germain: (2, 5), (3, 7), (5, 11), (11, 23), (23, 47).

Figura 1.13: Diagrama de módulos - Números primos de Sophie Germain


Capítulo 1: Guía de Laboratorio #10 17

Sugerencia

Para la implementación utilice el diagrama de módulos de la figura 1.13.

1.8.2. Sucesiones de números

Las sucesiones de números son secuencias de números naturales que siguen unas reglas de formación determi-
nadas. Existen algunas sucesiones que tienen utilidad práctica como la sucesión de Padovan. La sucesión de
Padovan 3 fue nombrada por el matemático Richard Padovan y tiene varios usos como por ejemplo el mostrado
en la figura 1.14.

Figura 1.14: Espiral de triángulos equiláteros dónde la longitud de los lados siguen la sucesión de Padovan

Para calcular los números de la sucesión de Padovan se utiliza esta función padovan(n).


 1 n=0

1 n=1
padovan(n) =


 1 n=2
padovan(n − 2) + padovan(n − 3) n ≥ 3

Aquí se muestran los primeros 20 números de la sucesión de Padovan: 1, 1, 1, 2, 2, 3, 4, 5, 7, 9, 12, 16, 21, 28,
37, 49, 65, 86, 114, 151.
También existe la sucesión de Perrin que 4 es una sucesión con la misma regla de formación que la de Padovan,
pero que utiliza diferentes valores iniciales, fue estudiada por el matemático francés Edouard Lucas en 1876.
En 1899 sus ideas fueron desarrolladas por R. Perrin y por ello la sucesión se conoce como sucesión de Perrin.
Para calcular los números de la sucesión de Perrin se utiliza esta función perrin(n).

3


n=0
0 n=1
perrin(n) =
2
 n=2

perrin(n − 2) + perrin(n − 3) n ≥ 3

Aquí se muestran los primeros 20 números de la sucesión de Perrin: 3, 0, 2, 3, 2, 5, 5, 7, 10, 12, 17, 22, 29, 39,
51, 68, 90, 119, 158, 209.
3 http://diposit.ub.edu/dspace/bitstream/2445/1323/1/217.pdf
4 http://diposit.ub.edu/dspace/bitstream/2445/1323/1/217.pdf
Capítulo 1: Guía de Laboratorio #10 18

Se solicita implementar un programa que permita al usuario elegir mostrar el mayor y menor número impar
de los primeros n números ya sea de la sucesión de Padovan o de la sucesión de Perrin, el programa deberá
solicitar al usuario siempre la elección anterior hasta que se ingrese un centinela para salir. El número natural
n ingresado debe ser mayor que cero.
Si el usuario elige sucesión de Padovan e ingresa n=15, se debe mostrar el menor y mayor número impar de
los primeros 15 números de la sucesión de Padovan: 1, 1, 1, 2, 2, 3, 4, 5, 7, 9, 12, 16, 21, 28, 37. Los valores a
mostrar serian: 1 y 37.
Si el usuario elige sucesión de Perrin e ingresa n=15, se debe mostrar el menor y mayor número impar de los
primeros 15 números de la sucesión de Perrin: 3, 0, 2, 3, 2, 5, 5, 7, 10, 12, 17, 22, 29, 39, 51. Los valores a
mostrar serían: 3 y 51.

Figura 1.15: Diagrama de módulos - Sucesiones de números

Sugerencia

Para la implementación utilice el diagrama de módulos de la figura 1.15.

Para pensar (C)

Observe qué son similares las reglas de formación de números de la sucesión de Padovan y de Perrin.
¿Será posible tener una sola función que sea llamada desde el programa principal? ¿Cómo lo haría?

1.8.3. Calculadora de conversión de unidades

Se solicita implementar un programa que permita al usuario elegir entre diferentes tipos de conversión de
unidades. Deben estar disponibles la conversión de grados sexagesimales a radianes, de grados centígrados a
grados fahrenheit y grados kelvin, de centímetros a pulgadas y de metros a pies. El programa deberá mostrar
las opciones de conversión hasta que el usuario lo desee. Según la opción elegida se deberá pedir al usuario
ingresar el valor n a convertir en las unidades respectivas y mostrar el valor convertido.
Las fórmulas para realizar la conversión de unidades son las siguientes:

n grados sexagesimales = n ∗ P I/180 radianes


n grados centígrados = (n ∗ 9/5) + 32 grados fahrenheit
n grados centígrados = n + 213.15 grados kelvin
n centímetros = n/2.54 pulgadas
n metros = n ∗ 3.28084 pies

Sugerencia

Para la implementación utilice el diagrama de módulos de la figura 1.16.


Capítulo 1: Guía de Laboratorio #10 19

Figura 1.16: Diagrama de módulos - Calculadora de conversión de unidades

1.8.4. Sumatorias de números naturales

Se solicita implementar un programa que muestre un menú con las siguientes opciones de forma iterativa:

1. Suma numeros naturales compuestos

2. Suma cuadrados de numeros naturales


3. Salir

Si se elige la opción 1 ó la opción 2, se debe solicitar el ingreso de dos números naturales a y b, ambos mayores
que cero y que se cumpla que a < b. Si se elige la opción 3 debe salir de la iteración y del programa.
Si se elige la opción 1 se debe realizar la suma de los números naturales compuestos comprendidos entre a y
b. Un número compuesto es cualquier número natural no primo, a excepción del 1. Es decir, tiene uno o más
divisores distintos a 1 y a sí mismo. Los primeros números compuestos son: 4, 6, 8, 9, 10, 12, 14, 15, 16, 18.
Por ejemplo si a = 5 y b = 8, se deberá mostrar sumatoria = 10 + 12 + 14 + 15 = 51, menor= 10 y mayor=15.
Si se elige la opción 2 se debe realizar la suma de los cuadrados de los números naturales comprendidos entre
a y b. Por ejemplo si a = 5 y b = 10, se deberá retornar sumatoria = 52 + 62 + 72 + 82 + 92 + 102 =
25 + 36 + 49 + 64 + 81 + 100 = 355, menor= 25 y mayor = 100.

Figura 1.17: Diagrama de módulos - Sumatorias de números naturales

Sugerencia

Para la implementación utilice el diagrama de módulos de la figura 1.17.

You might also like