martes, 7 de octubre de 2014

Números primos del 0 al 511

Un número primo es aquel número que solo es divisible por si mismo y por la unidad.  Se asume que el número 1 no es primo, aunque hay discusión al respecto.  Así, los veinte primeros números primos son: 2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67 y 71.

Para conocer la historia de los números primos y detalles se recomienda consultar la página  http://es.wikipedia.org/wiki/N%C3%BAmero_primo.


La famosa Criba de Eratóstenes, matemático griego que ideo una forma fácil y sencilla de identificar a los números primos, eliminando los múltiplos de los primeros números primos (como son el 2, 3, 5, 7, 11, 13, 23, etc.) mayores o iguales que el número primo elevado al cuadrado, consultar  en http://amigosdelamatematica.blogspot.es/1202842500/

Veamos como podemos implementar un algoritmo en Arduino que nos permita  dado un número, conocer si este es un número primo o no. Para ello inicialmente elaboramos el código para un contador binario ascendente de 9 bits implementado en Arduino mega 2560:

int outPin[] = {37, 39 ,41, 43, 45,47,49,51,53};
int tiempo = 2000;

void setup()
{
  int i = 0;
  for ( i = 0; i < 9; i++)
  pinMode(outPin[i], OUTPUT);
}

void loop()
{
 int i = 0, j=0;
 for ( i = 0; i <512; i++)
 {
 for ( j = 0; j < 9; j++)
 {
 if ( ( (i >> j) & 1 )  == 1 )
 digitalWrite(outPin[j], HIGH);
 else digitalWrite(outPin[j], LOW);
 }
 delay(tiempo);
 }}

Vamos a analizar con todo detalle el programa hasta aquí, trabajando con una plaqueta Arduino Mega2560:

Lo primero que hacemos es hacer un arreglo o lista de valores, indicando los pines correspondientes a cada led.

La  instrucción   Int outPin[]= {37,39,41,43,45,47,49,51,53}; nos evita tener que declarar por separado para cada led su pin de conexión.

La siguiente instrucción int  tiempo = 2000; determina el tiempo que transcurre para el cambio de un número binario al siguiente, ( 2 segundos)
A continuación  para la   configuración de pines como salidas se tiene la instrucción
 void setup() { int i=0; for (i = 0; i < 9;i++); pinMode(outPin[i],OUTPUT) ;} , donde el primer led  a la derecha, para nuestro caso  correspondiente al bit menos significativo, el LED 0   lo conecta al pin 37, el siguiente, el LED 1 al 39, y así sucesivamente hasta conectar el led correspondiente al bit mas  significativo, LED 8, al extremo izquierdo, en el pin 53.

 Los nueve  leds junto con su valor en paréntesis los vamos a denominar LED 0 (1), LED 1 (2), LED 2(4), LED 3 (8), LED 4 (16), LED 5 (32), LED 6(64), LED 7 (128), LED 8 (256).

Luego pasamos al bucle principal:

void loop() {
int i=0, j=0;
 for (i=0;i<512;i++)
{ for(j=0;j<9;j++)
{ if ( ( ( i >> j ) & 1) ==1)
digitalWrite(outPin[j], HIGH);
else digitalWrite(outPin[j],LOW);}
delay(tiempo);} }

Las instrucciones for anidadas  permiten que el contador avance por todos los 512  números correspondientes a los 9  bits, desde el 0, (000000000)2  hasta el 511, (111111111)2, pero en cada número hace un recorrido del 0 al 8, correspondiente a los 9 bits de cada número en binario manejados por los 9 LEDs.

 La función loop() al  ejecutarse una y otra vez, lo que hace es iterar por los 512 números que podemos mostrar, ya que tenemos 9 Leds, y 2 elevado a 9 es igual a 512.

Fijaremos nuestra mayor atención en la instrucción:

{ if ( ( ( i >> j ) & 1) ==1)
digitalWrite(outPin[j], HIGH);
else digitalWrite(outPin[j],LOW);}

Para que por cada número, se encienda  o apague  cada led correspondiente, la clave está en el bucle que realiza la operación “(i >> j) & 1″.

  La instrucción  ( i >> j ) maneja el  operador de desplazamiento a la derecha >> de los bits de la expresión binaria correspondiente al decimal en referencia.

Por ejemplo al llegar el contador al decimal 100 que ya vimos corresponde al binario 0011001002,  la instrucción (100>>0) establece que el binario permanece sin alteración; 
luego (100>>1) quiere decir que el binario 0011001002 se desplaza hacia la derecha una posición colocando un 0 como bit mas significativo o sea queda  0001100102 que equivale al 50, o sea 100 dividido por 2; luego la instrucción (100>>2) al desplazar al 100 en binario 2 posiciones a la derecha rellenando con cero a la izquierda queda 0000110012 , o sea el 25 decimal, 100 dividido por 4, o lo que es lo mismo 50 dividido por 2. Esto nos lleva a la conclusión que la instrucción ( i >> j ) nos sirve para efectuar las sucesivas divisiones por 2 que hay que realizar con cualquier número en decimal para su conversión en binario.

&1 es un operador AND.  El 1 en 9  bits es 0000000012.  La operación lógica  AND obedece a la siguiente tabla: 0&0=0; 0&1=0; 1&0=0; 1&1=1.  Para que dé un 1 los 2 bits deben ser 1, de lo contrario la AND da 0.

La instrucción (i >> j) & 1 realiza la operación AND bit por bit entre el binario establecido al aplicar el desplazamiento a la derecha del decimal en cuestión con el 1 en binario. Veamos con ejemplos:   (100>>1)&1 = 0  pues la AND bit a bit entre 0001100102 y 0000000012 es igual a 0.  Por otra parte, (100>>2)&1=1, pues la AND bit a bit entre 25 y 1 es igual a 1, pues la AND entre 0000110012 y 0000000012 es igual a 1, por ser ambos 1 los bits menos significativos de las dos expresiones binarias. Vemos entonces que de esta manera se pueden tomar los residuos  al efectuar la divisiones sucesivas  por 2.

Veamos ahora en su conjunto la instrucción completa :

{ if ( ( ( i >> j ) & 1) ==1)
digitalWrite(outPin[j], HIGH);
else digitalWrite(outPin[j],LOW);}

 Cada iteración del bucle divide el número en cuestión por 2 y se queda únicamente con el bit de menor valor. Así  sabemos si tenemos que encender o no el LED.

Por ejemplo, para mostrar el número 341, que en binario es (101010101) deberíamos encender los Leds  0, 2,  4, 6 y 8,  dejando  apagados el resto.  El calculo sería:

LED 0: 341 (101010101 & 1) -> 1, encendido
LED 1: 341 / 2 = 170 (010101010 & 1) -> 0, apagado
LED 2: 170 / 2 = 85 (001010101& 1) -> 1, encendido
LED 3: 85 / 2 =   42 (000101010 & 1) -> 0, apagado
LED 4: 42 / 2 =   21 (000010101 & 1) -> 1, encendido
LED 5: 21 / 2 =   10 (000001010 & 1) -> 1, apagado
LED 6: 10 / 2 =  5    (000000101 & 1) -> 1, encendido
LED 7: 5 / 2 =    2    (000000010 & 1) -> 1, apagado
LED 8: 2 / 2 =    1    (000000001 & 1) -> 1, encendido

Como vemos, el resultado del cálculo es correcto. Básicamente lo que se hace es ir desplazando los bits hacia la derecha y quedándonos con el bit menos significativo.




Lo que vamos a hacer luego  es recorrer todos los números entre el 0 y el número sobre el que queremos saber si es primo o no.

 Dentro de un bucle comprobaremos el principio del número primo. "Divisible por si mismo y por la unidad". Es decir, que si encontramos un número que es divisible por el número evaluado, este dejará de ser primo.

Por ejemplo, el número 10 no es primo. Ya que 10 es divisible por 2 y 5. Esto, expresado en términos matemáticos vendría a decir, que el  residuo  entre los dos números es 0.

Veámoslo:

10/2 = 5, residuo 0
10/5 = 2, residuo 0

La función que nos ayuda a conocer el residuo entre dos números es el módulo. Y en Arduino se representa con el tanto por ciento.

Así:

10%2 = 0
10%5 = 0
10%3 = 1 (Ya que 10/3 = 3 y  el residuo es 1)

Adicional a la identificación de si el número desde el 0 al 511 (9 bits) es primo, en el monitor del puerto serial del Arduino se va  a imprimir el número en sistema decimal, binario, octal, hexadecimal, y su respectivo carácter en código ASCII; así mismo se visualiza en 9 leds el encendido y apagado de los leds en binario, con cambio en el contador cada 5 segundos, y un buzzer electromagnético con un led jumbo se activan durante 150 ms cuando se detecta que el número es primo.

El programa respectivo, completo, es el siguiente:

int outPin[] = {37, 39 ,41, 43, 45,47,49,51,53};//9 leds del contador binario
int tiempo = 5000; //tiempo de reloj del contador
int k = 0;
int residuo = 0;
int nc = 0; //contador de divisiones exactas
int buzzer = 9;
int led = 8;

void setup()
{
  int i = 0;
  for ( i = 0; i < 9; i++)
  pinMode(outPin[i], OUTPUT);
  Serial.begin(9600);
  pinMode(buzzer,OUTPUT);
  pinMode(led,OUTPUT);
}

void loop()
{
 Serial.println("   NUMEROS DEL 0 AL 511 EXPRESADOS EN DECIMAL,BINARIO OCTAL, HEXADECIMAL, CARACTER ASCII,CON IDENTIFICACION SI ES NUMERO PRIMO");
 int i = 0, j=0;
 for ( i = 0; i <512; i++)
 {
 for ( j = 0; j < 9; j++)
 {
 if ( ( (i >> j) & 1 )  == 1 )
 digitalWrite(outPin[j], HIGH);
 else digitalWrite(outPin[j], LOW); 
  }

 for ( k = 1; k <= i; k++)
 {
   residuo = i % k;  //residuo al dividir el número i entre el número k
   if (residuo == 0)
   {
     nc = nc + 1; //contador de divisiones exactas, sin residuo, se incrementa
    }
 }

 if ((nc <= 2)&&( nc > 1))  //contador menor o igual a 2 y a la vez mayor a 1 (número primo)
 {
  Serial.println ("    ");
  Serial.print ("                ");
  Serial.print ("PRIMO.....");
  Serial.print (i,DEC);
  Serial.print ("  Decimal       ");
  Serial.print (i,BIN);
  Serial.print (" Binario        ");
  Serial.print (i,OCT);
  Serial.print ("  Octal      ");
  Serial.print (i,HEX);
  Serial.print(" Hexadecimal      ");
  Serial.print (i,BYTE);
  Serial.print(":Caracter ASCII   ");
  digitalWrite(buzzer,HIGH);
  digitalWrite(led,HIGH);
  delay (150);
  digitalWrite(buzzer,LOW);
  digitalWrite(led,LOW);
  delay(tiempo);
  nc = 0;
   }

  else if (nc != 2)  //contador de divisiones exactas, diferente de 2 (al no ser primo)
 {
  Serial.println ("        " );
  Serial.print ("                          ");
  Serial.print (i,DEC);
  Serial.print ("  Decimal       ");
  Serial.print (i,BIN);
  Serial.print (" Binario        ");
  Serial.print (i,OCT);
  Serial.print ("  Octal      ");
  Serial.print (i,HEX);
  Serial.print(" Hexadecimal      ");
  Serial.print (i,BYTE);
  Serial.print(":Caracter ASCII   ");
  delay(tiempo);
  nc = 0;
   }
 
  }
 }

Importante que el lector entienda el algoritmo para la identificación del número primo, punto crucial del presente proyecto. La instrucción  ((nc <= 2)&&( nc > 1))  establece cuando el número es primo, excluyendo el cero y el uno, permitiendo que estos dos números sean evaluados.

La instrucción (nc != 2)  indica que al ser el contador nc diferente a 2, el número de divisiones exactas, o sea sin residuo, diferente a 2,  ello significa que el número no es primo.
Este proyecto nos invita a estudiar los sistemas numéricos decimal, binario, octal, y hexadecimal, y la conversión de un sistema a otro.

Al ser posicional, el sistema decimal es un sistema de numeración en el cual el valor de cada dígito depende de su posición dentro del número. Al primero corresponde el lugar de la unidades, el dígito se multiplica por 100 (es decir 1); el siguiente las decenas (se multiplica por 10); centenas (se multiplica por 100), etc.


Respecto al sistema binario, en base 2, del cual ya se ha trabajado en proyectos anteriores, sabemos que solo utiliza dos dígitos, llamados bits, el cero y el uno. Se recomienda repasar en el enlace http://es.wikipedia.org/wiki/Sistema_binario.

La matemática binaria fue trabajada por George Boole quien creó un algebra que lleva su nombre. Ver información en  http://es.wikipedia.org/wiki/George_Boole

El sistema numérico en base 8 se llama octal y utiliza los dígitos 0 a 7. Se recomienda consultar http://es.wikipedia.org/wiki/Sistema_octal

Del hexadecimal, base 16,  ya habíamos hablado en proyectos anteriores, sabemos que utiliza 16 caracteres alfanuméricos, los números del 0 al 9, y las letras de la A hasta la F. Consultar la página http://es.wikipedia.org/wiki/Sistema_hexadecimal

Se recomienda al lector ejercitarse en la conversión entre los diferentes sistemas numéricos:http://www.dav.sceu.frba.utn.edu.ar/homovidens/gomezgomez_paz/PROYECTIN/PAGINA/conversionentresistdenumC.htm

Respecto al código ASCII (American Standard Code for Information Interchange) consultar http://es.wikipedia.org/wiki/ASCII

1 comentario:

  1. Hola Buen Dia .... Amigo si deseo solamente que al ingresarle un numero, imprima si es primo o no ..... gracias por tu atencion .

    ResponderEliminar