Objetivos
Material requerido
Kit inicio UNO | |
Kit Inicio Mega |
Planteando un programa un poco más complicado
Hemos visto ya como definir funciones. En esta sesión vamos a plantear un programa que acepte un número desde la consola y compruebe si es o no primo. Y en caso de no serlo, nos calcule cuales son los divisores primos de este.
Normalmente para resolver un problema complejo es buena política partirlo en otros problemas más pequeños que podamos resolver más fácilmente. En este caso vamos a plantear al menos 3 funciones:
La idea es, que nuestro programa empiece comprobando si un numero es primo. Si lo es, bien por el. SI no llamaremos a una función que calcule cuales son sus divisores. Y por ultimo necesitamos de algo que nos pueda dar un número desde la consola para probarlo y por eso escribimos otro programa que nos permita recibir cómodamente este numero de entrada.
Fijaros que casi sin haber escrito una linea de programa, ya he decidido como partirlo en bloques mas sencillos de manejar y programar. En otras palabras, he buscado una estrategia, de resolución
Operando con Arrays
Con la función Primo() que vimos en la sesión anterior, a medida que el tamaño del número a probar, crece, el tiempo que tarda en determinar si es primo también, ya que dividimos por todos los números que le preceden.
Una manera más eficaz de calcular si un número es primo, es dividirlo solo por los números primos menores que el. Pero para esto necesitaríamos un modo de archivar estos primos.
Podríamos ejecutar primero el programa Prog_8.3 para hallar los N primeros números primos, y si dispusiéramos de algún medio para guardarlos, tendríamos un sistema más eficaz para decidir si un número es o no primo.
Una manera de archivar estos números es definir un array.
Un array es simplemente una colección de elementos organizados como una matriz, y pueden definirse con varias dimensiones. Empecemos con un array de una sola dimensión. Para definirlo podemos optar por dos maneras:
int serie1 [ 5] ; //Creamos una colección de 5 enteros int serie2[] = { 3,5,6,12, 23} ;
En el primer caso definimos un array de enteros, de una sola dimensión con 5 elementos, sin asignar valores de momento.
En el segundo caso asignamos un array de enteros a los valores que le pasamos entre llaves, sin especificar cuantos, porque le dejamos a C++ la tarea de contarlos. Decimos que definimos el array por enumeración.
Para asignar o leer los valores de un array se utiliza un índice entre corchetes. Veamos este programa [highlight variation=»orange»]Descargar[/highlight]:
int serie2[] = { 3,5,6,12, 23} ; // Prog_9_1 void setup() { Serial.begin(9600) ; } void loop() { for (int i=0 ; i<5 ; i++) Serial.println("Posicion " + String(i)+ ": "+ String(serie2[i])) ; }
El programa imprime el contenido del array recorriendo sus 5 posiciones.
Un error muy peligroso, y difícil de detectar sería algo así (Prog_9.2):
int serie2[] = { 3,5,6,12, 23} ; for (int i=0 ; i<99 ; i++) Serial.println("Posicion " + String(i)+ ": "+ String(serie2[i])) ;
Uno esperaría que C++ generase un error, ya que definimos un array de 5 elementos y hacemos referencia a 100, pero no. Nuevamente C++ nos sorprende devolviendo correctamente los 5 primeros valores y luego sigue leyendo posiciones de memoria consecutivas tan tranquilo, como si tuvieran sentido.
Por último, mencionar que podemos manejar arrays de varias dimensiones:
Int Tablero[ 8, 8 ] ;
Imaginad que Tablero representa las posiciones de una partida de ajedrez y cada valor que contiene esa posición corresponde a una pieza que se encuentra en esa casilla.
Afinando la función Primo()
Si corremos el programa Prog_8.3 nos dará en el monitor una lista de primos hasta el 1024 (o hasta el número que deseemos modificando el valor de máximo) y seleccionándolos con el ratón podremos copiar esos valores y pegarlos en el IDE para crear un array con ellos (Prog_9.3):
int P[] = { 2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97, 101, 103, 107, 109, 113, 127, 131, 137, 139, 149, 151, 157, 163, 167, 173, 179, 181, 191, 193, 197, 199, 211, 223, 227, 229, 233, 239, 241, 251, 257, 263, 269, 271, 277, 281, 283, 293, 307, 311, 313, 317, 331, 337, 347, 349, 353, 359, 367, 373, 379, 383, 389, 397, 401, 409, 419, 421, 431, 433, 439, 443, 449, 457, 461, 463, 467, 479, 487, 491, 499, 503, 509, 521, 523, 541, 547, 557, 563, 569, 571, 577, 587, 593, 599, 601, 607, 613, 617, 619, 631, 641, 643, 647, 653, 659, 661, 673, 677, 683, 691, 701, 709, 719, 727, 733, 739, 743, 751, 757, 761, 769, 773, 787, 797, 809, 811, 821, 823, 827, 829, 839, 853, 857, 859, 863, 877, 881, 883, 887, 907, 911, 919, 929, 937, 941, 947, 953, 967, 971, 977, 983, 991, 997, 1009, 1013, 1019, 1021 } ;
Hemos definido un array enumerando sus elementos, entre llaves y separados por comas.
int size = sizeof(P) / sizeof(int);
- Donde P es nuestro array y dividimos por sizeof(int) porque definimos P como int. Y para este caso devuelve un valor de 172 elementos
Ahora bastaría dividir el número a probar por aquellos elementos del array P, menores que él:
bool Primo(int x) { int index = 0 ; while ( P[index] < x) { if ( x % P[index++] == 0) return(false); } return(true); }
Recorremos los valores almacenados en el array P[] mediante index al que vamos incrementando en cada iteración, hasta que nos toque probar un primo mayor que el valor que comprobamos.
Función Divisores
Esta función va a recorrer los elementos del array en tanto en cuanto sean menores (posibles divisores) que el número que probamos. Si encontramos divisores primos los guardamos en un array que hemos llamado Div[] al que le asignamos un máximo de 32 divisores:
int Div[32] ; int Divisores(int x) { int index = 0 ; //Apunta a la posicion del array P[] int pos = 0 ; //Para apuntar al array de divisores Div[] while ( P[index] < x) { int k = P[index++] ; if ( x % k == 0) Div[pos++]= k ; //Guardamos el divisor en en el array Div[]. } // para uso posterior return(pos); //Devolvemos el numero de divisores encontrado }
Cuando queramos imprimir los divisores, basta con recorrer Div[].
La función GetLine()
Aunque ya comentamos que podemos usar una función parseInt () incluida en Arduino para recoger un valor del puerto serie, tiene el inconveniente de que si no recibe una entrada salta al cabo de un tiempo ( muy escasito) y devuelve 0, por lo que tendríamos que controlar el valor devuelto para que no se repitiese continuamente.
Por eso vamos a escribir una función de uso general que nos permita recoger una cadena de texto de la puerta serie sin que salga hasta que reciba un String que vamos a hacer finalice en intro. De hecho ya vimos este programa, aunque no como función en la sesión Comunicación con el exterior
String GetLine() { String S = "" ; if (Serial.available()) { char c = Serial.read(); ; while ( c != '\n') //Hasta que el character sea intro { S = S + c ; delay(25) ; c = Serial.read(); } return(S) ; } }
Definimos Getline() de tipo String, porque queremos que nos devuelva un texto. Comprobamos que hay algo disponible en la puerta serie, y en caso afirmativo construimos un String S añadiéndole cada uno de los caracteres que leemos del puerto serie, hasta que encontremos un intro.
Al encontrar el intro, se cumple la condición de salida del while y termina la función devolviendo la cadena construida (sin el intro).
El programa principal
Podemos ya escribir nuestra función principal loop(), que llame a las funciones que hemos definido a lo largo de esta sesión, para determinar si un numero que le pasamos por la puerta serie es primo o no y en caso negativo que nos muestre los divisores primos encontrados.
Podría ser algo así:[highlight variation=»orange»]Calculo de numeros primos en arduino[/highlight]:
void loop() { if (Serial.available()) { String s = GetLine(); int i = s.toInt() ; //Como esperamos un numero, convertimos el texto a numero if ( Primo(i)) Serial.println(String(i) + " Es primo."); else { Serial.println(String(i) + " No es primo."); Serial.println("Sus divisores son: "); int j = Divisores(i); //Recogemos el numero de divisores encontrados for (int n =0 ; n<j ; n++) //Imprimimos los divisors del Div[] Serial.print(String(Div[n]) + ",\t"); Serial.println(" "); // Al acabar salta de linea } } }
Empezamos comprobando si hay algo sin leer en la puerta serie y si es asi llamamos a GetLine() para que nos consiga lo que hay.
Como GetLine() nos devuelve un tipo String() usamos la función estándar de Arduino C++, s.toInt() que convierte el contenido String a tipo numérico int.
Después llamamos a Primo() para que compruebe este número. Si es primo, simplemente imprime un mensaje para confirmarlo. En caso contrario llamamos a Divisores() que busca y almacena en el array Div[] los divisores primos que encuentra.
Cuando divisores regresa, nos devuelve el número de divisores encontrados y podemos imprimirlos con un sencillo bucle for.
Resumen de la sesión