Archive for the 'C#' Category

Optimización de Código - Cómo Convertir un Entero en Binario

October 12th, 2008 | Category: C#

El título de este post debería ser realmente: “Cómo formatear una cadena para mostrar un entero en formato binario”… pero creanme que nadie lo buscaría así en un buscador :P.

Bien esto parece ser un problema recurrente alrededor de los foros en internet, existen muchas soluciones diferentes a este problema, incluso hay algunos lenguajes que ya incluyen soporte para hacer esto fácilmente,  el CLR no se caracteriza por tener una funcionalidad fácil de utilizar sin embargo en este artículo exploraremos varias soluciones (desde luego no todas )posibles hasta llegar a la solución ideal que espero les sea de provecho a todos.

Estas son las opciones que exploraremos, son básicamente 2.

La primera opción no requiere de mucho análisis pero tiene sus inconvenientes digamos menores. La segunda opción es la que más nos importa, aprenderemos a deducir el algoritmo a implementarlo y subsecuentemente lo iremos optimizando hasta lograr una versión digna de un excelente programador:

  1. Convertir con BitVector32 (mala solución pero rápida de implementar)
  2. Convertir creando un algoritmo eficiente
    • Utilizando string
    • Utilizando StringBuilder
    • Utilizando punteros con codigo inseguro

 

1.  CONVERTIR CON BITVECTOR32

Esta es la solución más rápida al problema, pero en mi opinión la menos profesional….

BitVector32 es una colección especializada que se encuentra en:

System.Collections.Specialized

Esta clase provee una estructura simple que almacena valores boleanos que representan un entero de 32 bit.  Que que?

Ok esta explicación no necesariamente es clara para todos, hagamoslo muy simple veamos un entero de 32 bits, imaginemos que en memoria se ve así:

0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31

Es decir cada casilla representa un bit, para completar asi 32 bits.

Bien entonces esto a final de cuentas es una coleccion bits, es decir en cada ‘celda’ podemos tener dos valores posibles 1 o 0… true o false.

Bien eso es lo hace BitVector toma un entero y lo permite manejar como un array de booleanos.

El método ToString() permite convertir todo esto a 1s y 0s para poder ver los valores de cada ‘celda’ en su representación numérica, sin embargo esto tiene un inconveniente pues si utilizamos elObjetoBitVector.ToString() recibiremos algo así como esto:

BitVector32{00000000000000000000000001101111}

Nos sobra algo al principio y al final… a nosotros solo nos interesan los datos binarios así que la solución es hacer un substring de la parte de la cadena que nos interesa, finalmente el código quedaría así:

  1. public static string EB_BitVector32(Int32 entero)
  2. {
  3. BitVector32 bv = new BitVector32(entero);
  4. return bv.ToString().Substring(12, 32);
  5. }

Esto nos produciría este resultado similar a:

00000000000000000000000001101111

 

2.  CONVERTIR CREANDO UN ALGORITMO EFICIENTE

Ahora si entremos en materia hagamos el algoritmo.

Una de las primeras ideas que a uno se le viene a la mente es utilizar el conocido algoritmo que nos permite convertir un numero decimal a base 2, para el que no lo recuerde o no lo conozca: es un algoritmo que toma como base divisiones anidadas del valor entre 2 y recupera los residuos. En est link tienen una explicación detallada al respecto: Decimal a Binario

Sin embargo dicho algoritmo puede ser ineficiente para nuetsro gusto.

Retomemos conceptos como puntos clave que nos permitan llegar a un algoritmo óptimo:

  1. Básicamente un entero es un arreglo de 4 bytes (32 bits)
  2. Por cada bit se debe producir su equivalente como cadena (”0″ , “1″) según si el bit esta prendido(1) o apagado(0)
  3. Podemos acceder a todos los bit como un arreglo de boolean a traves de BitVector32, pero en ese caso la solúción sería aún más lenta que utilizando la propuesta inicial que usa BitVector32
  4. Hay que encontrar el mecanismo adecuado para recorrer los bit

De los conceptos clave tenemos que los dos primeros nos son utiles, el tercero no nos aporta nada nuevo y el cuarto es nuestra necesidad más inmediata: Averiguar como recorrer los bit sin necesidad de usar BitVector32.

Lo primero que me salta a la mente es usar operadores binarios especialmente corrimientos de bit (<<) y and (&) para las máscaras.

Veamos un poco de teoría acerca de lo que haremos.

  1. Crear un bucle e 32 ciclos
  2. Leemos siempre el valor del primer bit del entero de 32 bit, esto lo podemos hacer efectuando un and binario (&) del entero con un numero que tenga el primer bit prendido unicamente, es decir con : 0×80000000 (es decir 2147483648 )… porqué este número?  bueno coloquemos este 0×80000000 en nuestro arreglo de 32 bits:
    1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
    8       0       0       0       0       0       0       0      

    Cada 4 bits representan uno de los digitos hexa, de esta forma el 0×8 esta representado por los primeros 4 bits, en efecto 1000 = 0×8, así que si se fijan en nuestro arreglo de bits solo tenemos un bit prendido, de tal forma que cualquier cosa que haga & con 0×80000000 solo podría llegar a tener prendido el bit 1, así podemos hallar que valor tiene el bit 1 de un número dado ya que si ese bit esta en 0 al hacer and quedaria en 0, mientras que si esta en 1 al hacer and quedaria en uno nuevamente. Concretando, cuando hacemos un and entre dos enteros y uno de los enteros es 0×80000000 el resultado siempre sera el valor del primer bit del otro entero. ( espero haber sido claro).

  3. En cada iteración corremos 1 bit posición a la izquierda (<< 1), de tal forma que el primer bit es reemplazado por el bit siguiente
  4. Se regresa al paso 2

IMPLEMENTACION CRUDA Utilizando String

Este es el código resultante de la implementación más simpe del algoritmo:

  1. public static string EB_String(int entero)
  2. {
  3. //La máscara y el # de iteraciones
  4. const uint mascara = 0x80000000;
  5. const int iteraciones = 32;
  6. //el contador y el resultado
  7. int contador = 0;
  8. string resultado = "";
  9.  
  10. //Se recorren los 32 bit
  11. while (contador++ < iteraciones)
  12. {
  13. /*Si el entero and la mascara = 0 quiere decir
  14. *que el bit 1 esta apagado*/
  15. if ((entero & mascara) == 0)
  16. resultado += "0";
  17. else
  18. resultado += "1";
  19.  
  20. /*correr un bit a la izquierda para poner
  21. *el siguiente bit en la posicion del primero*/
  22. entero = entero << 1;
  23. }
  24. return resultado;
  25. }
 

OPTIMIZACIÓN DE CÓDIGO Utilizando StringBuilder

La implementación anterior es suceptible de una sencilla mejora utilzando StringBuilder ya que al ser string un tipo inmutable , las recurrentes concatenaciones generadas al utilizar masivamente el método sobrecargarian el GC, sin contar el overhead producido por las frecuentes reservas de memoria (1 adicional por cada cambio al string). StringBuilder se encuentra en el namespace System.Text.

Código optimizado utilizando StringBuilder:

  1. public static string EB_StringBuilder(int entero)
  2. {
  3. //La máscara y el # de iteraciones
  4. const uint mascara = 0x80000000;
  5. const int iteraciones = 32;
  6. //el contador y el resultado
  7. int contador = 0;
  8. StringBuilder resultado = new StringBuilder(iteraciones);
  9.  
  10. //Se recorren los 32 bit
  11. while (contador++ < iteraciones)
  12. {
  13. /*Si el entero and la mascara = 0 quiere decir
  14. *que el bit 1 esta apagado*/
  15. if ((entero & mascara) == 0)
  16. resultado.Append('0');
  17. else
  18. resultado.Append('1');
  19.  
  20. /*correr un bit a la izquierda para poner
  21. *el siguiente bit en la posicion del primero*/
  22. entero = entero << 1;
  23. }
  24. return resultado.ToString();
  25. }

OPTIMIZACIÓN DE CÓDIGO Utilizando punteros - código unsafe

Aunque StringBuilder ya proporciona una mejora muy importante, dadas las características de nuestro problema(concatenacion uno a uno) resulta mucho más idoneo generar código que utilice punteros. Es importante tener en cuenta que para que este código funcione se requiere modificar las propiedades del proyecto para que admita código unsafe.

Esta es la versión básica con manejo de apuntadores:

  1. public static unsafe string EB_Unsafe(int entero)
  2. {
  3. const uint mascara = 0x80000000;
  4. const int iteraciones = 32;
  5.  
  6. int contador = 0;
  7.  
  8. //Se reservan 32 posiciones y uno adicional para
  9. //terminacion en null
  10. char* resultado = stackalloc char[iteraciones + 1];
  11. //puntero de trabajo
  12. char* aux = resultado;
  13.  
  14. while (contador++ < iteraciones)
  15. {
  16. if ((entero & mascara) == 0)
  17. *aux = '0';
  18. else
  19. *aux = '1';
  20.  
  21. //Mover el puntero una posicion dentro de la cadena
  22. aux++;
  23.  
  24. entero = entero << 1;
  25. }
  26. return new string(resultado);
  27. }

Esta es la versión final de “bonustotalmente optimizada para velocidad de procesamiento, es levemente diferente de la anterior para realizar la menor cantidad de validaciones posibles y para que ignore los 0s a la izquierda, lo cual la hace excepcionalmente rápida en la mayoría de los casos. Revísenla…:

  1. public static unsafe string EB_Unsafe_Opt(int entero)
  2. {
  3. const uint mascara = 0x1;
  4. const int iteraciones = 32;
  5.  
  6. char* resultado = stackalloc char[iteraciones + 1];
  7. char* aux = resultado + iteraciones - 1;
  8.  
  9. do
  10. {
  11. if ((entero & mascara) == 0)
  12. *(aux--) = '0';
  13. else
  14. *(aux--) = '1';
  15.  
  16. entero = entero >> 1;
  17. } while (entero != 0);
  18.  
  19. return new string(++aux);
  20. }

Si alguién tiene una solución mejor por favor no dude en compartirla!!

En unos días publicare un programa especialmente diseñado para medir la velocidad de cada una de estas altenativas… les adelanto que las diferencias de tiempo van casi hasta de 20 o 30 veces más velocidad.

 

Happy Learning!!!

OOOHH OLvidaba decirles Si quieren pueden usar:

  1. System.Convert.ToString(elValor, 2);

jajajajaja, bueno el ejercicio que hicimos produce un resultado 30% más rapido. :P

5 comments

Caracter de Salto de linea

October 10th, 2008 | Category: C#

Un salto de linea en windows no es es realmente un caracter sino que realmente son dos caracteres el 13 y luego el caracter 10.  En detalle:

\r = 13 = CR = Carriage return = Retorno de carro
\n = 10 = LF = Line Feed       = Avance de linea

Mientras que en unix podria ser igual o diferente dependiendo de la configuracion.

La más usual es  que en unix sea solo CR, razon por la cual cuando abres en windows un archivo de texto creado en unix , ves que te sale todo en una sola linea ( falta el LF  - el salto de linea).

Como ves esto depende de la plataforma asi que lo ideal es que en nuestros programas que usen el CLR  es hacer uso de:

System.Environment.NewLine 

En las base de datos utiliza necesariamente CHR(13)+CHR(10)  ya que esto te dara compatibilidad con varias plataformas.

Happy learning!

1 comment

Cómo obtener el SID de un usuario local?

October 08th, 2008 | Category: C#

Obtener el SID del usuario que se encuentra ejecutando la aplicación es bastante sencillo:

  1. Adicionar using a System.Security.Principal
  2. Instanciar un objeto WindowsIdentity
  3. Construirlo a partir de WindowsIdentity.GetCurret()
  4. Usar la propiedad Value
  1. using System;
  2. using System.Security.Principal;
  3.  
  4. class Program
  5. {
  6. static void Main(string[] args)
  7. {
  8. WindowsIdentity currentUser = WindowsIdentity.GetCurrent();
  9. Console.WriteLine(currentUser.User.Value);
  10. Console.ReadLine();
  11. }
  12. }

Sin embargo tratar de acceder a la información de los usuarios diferentes del logueado actualmente ( y sin usar impersonación ) puede ser un poco más complejo. Una alternativa viable es hacer uso de WMI.

  1. Adicionar la referencia a System.Management y el respectivo using
  2. A traves de WMI se debe hacer un query al objeto Win32_UserAccount
  3. Especificar el nombre del dominio, si es local es el nombre de la máquina el cual se peude obtener a traves de System.Environment
  4. Hacer una búsqueda sobre el query
  5. Usar el indizador accediento con la cadena “SID”
  1. using System;
  2. using System.Management;
  3.  
  4. class Program
  5. {
  6. static void Main(string[] args)
  7. {
  8. SelectQuery sQuery = new SelectQuery("Win32_UserAccount", "Domain='"
  9. + System.Environment.MachineName + "'");
  10. ManagementObjectSearcher mSearcher = new ManagementObjectSearcher(sQuery);
  11. Console.WriteLine("User Accounts");
  12. Console.WriteLine("");
  13. foreach (ManagementObject mObject in mSearcher.Get())
  14. Console.WriteLine(mObject["SID"]);
  15.  
  16. Console.ReadLine();
  17. }
  18. }

Happy Learning!

1 comment

Next Page »