Más sobre mensajes navideños en hexadecimal

Respondiendo a la invitación de mi buen amigo Rodrigo, posteo aquí (perdón por el Spanglish, que a veces me sabe a gloria) mi solución al descifrado del mensaje navideño hexadecimal enviado por Pablito. El hecho de que lo primero que me vino a mí a la mente fue un método extensor a-la-LINQ, mientras que a Rodrigo un algoritmo concurrente, me ha traído a la mente una frase que usaba a menudo mi abuela, “cada loco con su tema” (más sobre eso al final). Aunque obvia decir que lo de “loco” es en sentido figurado: yo a lo mejor lo estoy, pero difícilmente se pueda encontrar tanto en nuestra profesión como fuera de ella a alguien más “cuerdo” que Rodrigo.


Básicamente, lo que hice fue crear un método extensor de string que produce una secuencia con los caracteres que componen el mensaje de entrada. Aquí va todo el código fuente:


using System;
using System.Collections.Generic;
using System.Text;
using System.Threading;
using System.Diagnostics;

namespace ConsoleApplication5
{
  class Program
  {
    const string message =
      “%46%65%6C%69%63%65%73%20%46%69%65%73” +
      “%74%61%73%2C%20%63%61%63%68%6F%20%66” +
      “%72%69%6B%69%21%20%41%68%6F%72%61%2C” +
      “%20%64%65%6A%61%74%65%20%64%65%20%74” +
      “%6F%6E%74%65%72%69%61%73%20%79%20%76” +
      “%65%74%65%20%61%20%65%6D%62%6F%72%72” +
      “%61%63%68%61%72%74%65%20%75%6E%20%70” +
      “%6F%71%75%69%6E%21%21%20%4B%65%65%70” +
      “%20%52%6F%63%6B%69%6E%27%21%21”;

    static void Main(string[] args)
    {
      Solution1();
      Solution2();

      const int TIMES = 100000;

      Console.WriteLine(“Solution 1: ” +
        Helper.Measure(Solution1, TIMES));
      Console.WriteLine(“Solution 2: ” +
        Helper.Measure(Solution2, TIMES));

      Console.ReadLine();
    }

    static void Solution1()
    {
      var sb = new StringBuilder();
      foreach (char c in message.DecryptedChars())
        sb.Append(c);
      Debug.WriteLine(“Translation: ” + sb.ToString());
    }

    static void Solution2()
    {
      var sb = new StringBuilder();
      foreach (char c in message.DecryptedChars2())
        sb.Append(c);
      Debug.WriteLine(“Translation: ” + sb.ToString());
    }
  }

  static class Helper
  {
    public static IEnumerable<char> DecryptedChars(this string source)
    {
      if (source == null)
        throw new ArgumentException(“Source cannot be null”);
      int times = source.Length / 3;
      for (int i = 0; i < times; i++)
      {
        yield return (char)int.Parse(source.Substring(3 * i + 1, 2),
          System.Globalization.NumberStyles.AllowHexSpecifier);
      }
    }

    public static IEnumerable<char> DecryptedChars2(this string source)
    {
      if (source == null)
        throw new ArgumentException(“Source cannot be null”);
      int times = source.Length / 3;
      char[] chars = new char[times];
      Parallel.For(0, times,
        (i) =>
        {
          chars[i] = (char)int.Parse(source.Substring(3 * i + 1, 2),
            System.Globalization.NumberStyles.AllowHexSpecifier);
        });
      for (int i = 0; i < times; i++)
        yield return chars[i];
    }

    public static long EllapsedTicks(Action action)
    {
      var sw = new Stopwatch();
      sw.Start();
      action();
      sw.Stop();
      return sw.ElapsedTicks;
    }

    public static long Measure(Action action, int times)
    {
      Debug.Assert(times > 0);
      long avg = 0;
      for (int i = 0; i < times; i++)
        avg += EllapsedTicks(action);
      return avg / times;
    }
  }
}


Originalmente, mi solución solo incluía el método DecryptedChars. Inspirado en el post de Rodrigo, y pensando en que el tipo de procesamiento se presta idealmente para la paralelización (se accede solo para lectura y a segmentos que no se solapan de la cadena de entrada), he creado una segunda variante del método que utiliza Parallel.For. En mi máquina de doble núcleo, no obstante, la variante secuencial es un 100% más eficiente; lo que indica que en la versión concurrente el coste de la creación del array temporal y los cambios de contexto supera las ventajas de la operación en paralelo. Pero en presencia de 4 ó más procesadores, el panorama podría ser diferente.



El bit nostálgico: Con “cada loco con su tema”, mi abuela aludía frecuentemente a la exhuberancia sonora que se vivía en mi casa: un tipo de música diferente en cada habitación, todas sonando simultáneamente. Mi abuelo era un gran amante de la música tradicional cubana, y mi padre del jazz; mientras que yo me decantaba exclusivamente por el pop/rock de finales de los 60 y los 70. Mirando atrás, creo que mi hermano fue capaz de extraer y combinar lo mejor de todo aquello; yo en ese aspecto fui presa durante mucho tiempo de uno de los mayores males que pueden aquejar al ser humano en cualquier faceta de la vida: el sectarismo, totalitarismo, “pensamiento único” o como quiera que queramos llamar a ese state of mind en el que se considera que una sola cosa vale y todas las demás son 100% pura basura.


 

Octavio Hernandez

Desarrollador y consultor en tecnologías .NET. Microsoft C# MVP entre 2004 y 2010.

6 comentarios en “Más sobre mensajes navideños en hexadecimal

  1. Excelente solución maestro! También puedes asociar la frase “Cada loco con su tema” con la canción del mismo nombre de JM Serrat (grande entre grandes).

    Un abrazo y feilces fiestas para ti y los tuyos.
    Miguel

  2. Migue,

    Efectivamente, Serrat es un gigante… Esta canción no la conocía, y eso que me gusta mucho Serrat; pero evidentemente me quedé en lo más clásico de su obra, la de la década de los 70, y luego (un poco paradójicamente) solo he escuchado cosas muy salteadas, como el reciente DVD con Sabina.

    Fuerte abrazo, felices fiestas,

    Octavio

Deja un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *