Banderitas en la nube

Por Saiyine Enviar correo el 2010-04-29 18:04:07 - Secciones: PRINCIPAL APPENGINE PYTHON PROGRAMACION - Enlace permanente: 918

Hacia tiempo que le tenia ganas a la nube, pero una traba relacionada con la liquidez, en particular, la falta de ella, me impedia pegarme con los grandes pesos pesados. Porque ganas e ideas para webs tengo, pero de fundirme doce o trece euros al mes en un servidor virtual para que luego no entre ni dios, no tanto. Por tanto, solo me quedaba la posibilidad de intentarlo con AppEngine y Heroku, que son, posiblemente, los primos pobres de la computación en la red de redes: nada de maquinas virtuales, tu subes el programa y nosotros te lo ejecutamos.

Me decidí por AppEngine tanto por la sencillez de uso como por el lenguaje elegido, Python. Tambien se puede programar en Java, pero mi consejor super especial del dia es: ¡NO! No toqueis la versión de Java de AppEngine ni con un palito, o tendreis que aguantar toda una retahila de bugs conocidos, APIs incomprensibles, APIs incomprensibles de terceros para intentar normalizar las anteriores, falta de documentacion o directamente erronea y en general una ciudadania de segunda clase con respecto a la versión en Python. Por su parte, Heroku, es bastante especialito, usando Ruby para el codigo y Git para las actualizaciones, rollo muy Rockstar-programo-con-mi-Mac-en-un-Starbucks.

Pues bien, mi experiencia AppEngine ha sido, en general, bastante lamentable, como mínimo, pero sobre todo lo que mas me ha alucinado es los programas en AppEngine no parecen seguir un flujo determinista, es decir, el mismo programa con la misma entrada a veces da resultados diferentes, incluso, durante el desarrollo de mi prueba, un par de veces tuve que parar el servidor porque el programa no funcionaba, y al volver a reiniciar, todo iba perfectamente. O subir una copia del programa que funciona en local y que en la nube deje de ir sin dar error... y que pasado un rato empiece a ir al pelo. Frustrante, y eso que era una prueba con un código bastante pequeño, no quiero ni pensar lo que puede ser intentar sacar adelante un proyectazo en esas condiciones.

Y esa es otra: ¿cual era la prueba? Reemplazar mi servicio de geolocalización en PHP (las banderitas de los comentarios). Resultado: en ocho horas, fundida toda la CPU asignada para el dia (cada 24 horas te dan 6.5 horas de CPU de cuota, y si te pasas, te cierran el grifo)(o a pagar claro, que va a ser que no). Un programa consistente en un acceso a memoria (memcache), a veces una query (ojo, una, y no siempre) y muy raramente, del orden de una de cada cien peticiones, una lectura HTTP de mi propio servidor de geolocalización para alimentar la base de datos.

AppEngine vende la moto de que, en su versión gratuita, da para un blog con cinco millones de visitas al mes. ¡Vamos, ni de coña, si mis banderitas lo funden con lo que serian unas 250 mil sencillisimas peticiones al mes! (Es decir, diez veces menos de lo prometido) Un desastre, y claro, lo tuve que quitar el mismo dia que lo puse, ocho horas de reloj me duraron las banderitas en la nube.

Hay que pensar en positivo, al menos estoy aprendiendo Python con esta historia.

SocializerMenefante Del.icio.us Digg Ver los comentarios

Numeros aleatorios en Python

Por Saiyine Enviar correo el 2010-04-24 18:24:48 - Secciones: PROGRAMACION PYTHON AZAR - Enlace permanente: 917

Para obtener números al azar en Python lo primero que teneis que hacer es importar la libreria random:

import random

Con ella obtendreis varias funciones, de las cuales creo que las dos más interesantes son:

random.randrange(a, b)
random.random()

La primera os dará un número entero entre a y b, a incluido, y el segundo, un número en coma flotante entre 0 y 1, que de nuevo puede pasar que sea igual a 0.

import random

print 'random = %f, randrange = %i' % (random.random() , random.randrange(0, 1000))

SocializerMenefante Del.icio.us Digg Ver los comentarios

Reading a random row from a MySQL table with Perl DBI

Por Saiyine Enviar correo el 2010-01-25 09:21:17 - Secciones: PERL DBI PROGRAMACION ENGLISH MYSQL - Enlace permanente: 902

This is the code from one of the clients used in the post "Statistics on getting a random row from a table" to check for the quickest way to access a random row.

It does nothing but reading a thousand random rows, I hope it's enough for you to learn how to access MySQL databases using Perl and use it as a quickstart for your own scripts.

#!/usr/bin/perl

use DBI;
use DBD::mysql;

$platform = "mysql";
$database = "database";
$host = "server_ip";
$port = "3306";
$user = "user";
$pw = "12345";

$dsn = "dbi:$platform:$database:$host:$port";

$connect = DBI->connect($dsn, $user, $pw);

for ($i=0; $i<1000; $i++)
{
    $query = "SELECT COUNT(1) AS total FROM bench_myisam";
    $query_handle = $connect->prepare($query);
    $query_handle->execute();

    $query_handle->bind_columns(\$total);

    if ($query_handle->fetch())
    {
        $rand = int(rand($total));

        $query = "SELECT id FROM bench_myisam LIMIT 1 OFFSET $rand";
        $query_handle = $connect->prepare($query);
        $query_handle->execute();

        $query_handle->bind_columns(\$id);
        $query_handle->fetch();
    }  
}

SocializerMenefante Del.icio.us Digg Ver los comentarios

Buscando el pseudo-punto fijo del MD5 en Java

Por Saiyine Enviar correo el 2009-05-10 08:15:57 - Secciones: JAVA MD5 PROGRAMACION - Enlace permanente: 874

Esta es mi entrada para el reto de encontrar el punto fijo de MD5 que mencionaron el otro dia en programming.reddit. No es un código muy limpio, sino más bien una prueba de concepto para ver que tal se portaba Java... y se porta tres ordenes de magnitud más lento que las implementaciones en C tirando de OpenSSL que usa el ganador (amén de una granja de P4).

En mi maquina de casa, este código me saca unas 44.000 sumas por segundo por nucleo, muy lejos de los millones por segundo de las versiones en código nativo.

package com.saiyine.experimentos.md5;

import java.security.MessageDigest;
import java.util.Random;
import java.util.Timer;
import java.util.TimerTask;

public class Sumador
{
    static String hex = "01234567890abcdef";
    static Random r = new Random();
    static MessageDigest md;
    static int contador = 0;
    static long tiempo = 60;
   
    public Sumador()
    {
        try
        {
            md = MessageDigest.getInstance( "MD5" );
        } catch ( Exception e)
        {
            e.printStackTrace();
        }
    }
   
    public String md5(String cadena)
    {
        String aux ="";
            md.update( cadena.getBytes() );
            byte[] digest = md.digest();
            String moco = "";
            for ( byte b : digest )
            {
                moco = moco +","+( b & 0xff );
                String aux2 =(Integer.toHexString( b & 0xff ));
                while (aux2.length()<2)
                {
                    aux2="0"+aux2;
                }
                aux=aux+aux2;
            }
            md.reset();
        return aux;
    }

    public static int parecido(String cadena1, String cadena2, int imprimir)
    {
        int aux = -1;
        for (int i=0;i<32; i++)
        {
            cadena1 = cadena1.substring(1, 32) + cadena1.charAt(0);
            int contador = 0;
            for (int j=0;j<32; j++)
            {
                if (cadena1.charAt(j) == cadena2.charAt(j))
                {
                    contador++;
                } else
                {
                    contador=0;
                }
            }
            if (contador>aux)
                aux=contador;
        }
        if (aux > imprimir)
        System.out.println(cadena1+" - "+cadena2+ " "+aux);
        return aux;
    }
   
    public static char hexa()
    {
       int i = r.nextInt(15);
        return hex.charAt(i);
    }   
   
    public static String azar(String cadena)
    {
        return hexa() + cadena.substring(0,cadena.length()-1);
    }   
   
    public static void main(String[] args)
    {
        Sumador s = new Sumador();
       
        Timer t = new Timer();
        TimerTask task = new TimerTask()
        {
            public void run()
            {
                System.out.println((contador/tiempo)+" comprobaciones/segundo");
                contador=0;
            }
        };
        t.schedule(task, tiempo * 1000, tiempo * 1000);                 
       
        String cadena1 = s.md5(""+(r.nextLong()*r.nextLong()*r.nextLong()));
        String cadena2 = null;
           
        while (true)
        {
            cadena1 = azar(cadena1);
            cadena2 = s.md5(cadena1);
            parecido(cadena1,cadena2,8);
            contador++;
        }
    }
}

SocializerMenefante Del.icio.us Digg Ver los comentarios

Pasar un numero variable de parametros en Java

Por Saiyine Enviar correo el 2009-04-06 08:13:20 - Secciones: JAVA PROGRAMACION - Enlace permanente: 871

Dreamhost

Via Mundogeek descubro que desde hace unos añitos, parece ser que desde la especificación Java 1.5, se puede pasar un número variable de parámetros a cualquier método.

En realidad se convierte en un array en tiempo de compilación, pero que se le va a hacer, algo es algo. Además, puntos extra por utilizar un foreach para la demostración.

public class Conjunto 
{
    public Conjunto(String... cadenas)
    {
        for (String cadena : cadenas)  
              System.out.println(cadena);  
    }
    
    public static void main(String... args) 
    {
        Conjunto saludos = new Conjunto("Hola", "Ciao", "Hello");
        Conjunto mascotas = new Conjunto("Pulpo");
    }
}

SocializerMenefante Del.icio.us Digg Ver los comentarios

Convertir imagen en circulitos en Processing

Por Saiyine Enviar correo el 2008-12-30 02:52:02 - Secciones: PROCESSING JAVA APPLET GRAFICOS PROGRAMACION - Enlace permanente: 856

Dreamhost

Como lo prometido es algo que debe cumplirse, aqui llevais mi primera obra maestra en Processing.

Aunque la primera impresión es que el entorno de programación es una porqueria, y a los 10 minutos ya estaba revisando si se le puede echar mano al código fuente y hacer que el cortar y pegar funcione como dios manda, tengo que admitir que la idea en si es bastante buena: una abstracción basada en la maquina virtual de Java para trabajar con gráficos.

Todo funciona al pelete, aunque igual la ayuda es un poco petarda y el entorno es una mierda. Es sopar unos cuantos ejemplos y ya lo tienes todo para concentrarte en trabajar con imagenes directamente con los valores RGB de cada punto. A ver si encuentro mis viejos programas en Pascal de cuando programar en VGA con 256 colores era tirar un puntero y a jugar, y los vuelvo a la vida con este programazo.

¡Además punto extra porque uno de los dos tipos que han montado esto se apellida Fry! ¡Jugón!

This browser does not have a Java Plug-in.
Get the latest Java Plug-in here.


Necesita Java. Pincha en la foto con el ratón.

/**
 * CircleandoVisible
 *
 * - Un sencillo para ir aprendiendo a manejar Processing
 * Lee una imagen, y convierte pixeles al azar en circulos de 10 pixeles de diametro al pulsar el ratón
 */

// Variable a la que queremos que pueda acceder todo el programa
PImage img;

void setup()
{
  // Carga la imagen de ejemplo
  img = loadImage("f16.jpg");
  
  // El tamaño de la pizarra será igual que el de la imagen
  size(img.width, img.height);

  // La usamos de fondo
  image(img, 0, 0);

  // Queremos un diseño sin bordes  
  smooth();

  // No ejecutamos el bucle (solo redibujamos con el ratón pulsado)  
  noLoop();
}

void draw()
{
  for(int i = 0; i < 100; i++)
  {
    int x1 = int(random(0, width));
    int y1 = int(random(0, height));
    strokeWeight(10);
    stroke(color(img.pixels[y1*img.width + x1]), 128);
    point(x1,y1);
  }
}

void mousePressed()
{
  loop();
}

void mouseReleased()
{
  noLoop();
}

SocializerMenefante Del.icio.us Digg Ver los comentarios

Pings XMLRPC con PHP super sencillos

Por Saiyine Enviar correo el 2007-04-29 05:13:35 - Secciones:  PRINCIPAL PHP PROGRAMACION FOTOS  - Enlace permanente: 759

Dreamhost

¡Que ven mis ojos, si Mayo ya está aquí! Y además en menudas fechas caen el 1 y el 2, martes y miercoles, fiesta nacional el primero y madrileña el segundo, lo que unidas a una más que generosa política de puentes de mis empleadores, hacen que me vaya a pegar cinco dias sin pegar un palo al agua, y por el módico precio de aguantar los tremendos problemas de tráfico de la autovia que me separa de mi tierra.

Bueno, en realidad eso de estar sin dar ni golpe tampoco es que vaya mucho conmigo, así que voy a aprovechar para desfacer todos los entuertos que pueda en la página, empezando por una antiquisima petición de Yhandros que ha resultado la mar de simple: el enlace de comentar ya va directo a los comentarios. En realidad a mi me da igual, o incluso prefiero tener delante el texto que voy a mejorar con mis comentarios de calidad, pero si el vulgo lo demanda, que así sea.

Otra mejora que tenia en mente desde tiempos antediluvianos era añadir la capacidad de hacer pings XMLRPC...

Ya, ya sé que todos sabeis de sobra de que hablo, pero por si acaso alguien ha llegado tarde o no tiene en la mesilla de noche mi último bestseller "En la cama con Saiyine" debo contar que, resumiendo infinitamente, hacer ping es hacerse notar.

No sé de donde viene la equivalencia, aunque apostaria a un origen basado en los sonares activos: los típicos sonidos de las pelis de submarinos ¡PING! ¡PONG!, que no son más que una versión burra de la ecolocalización de los morciguillos. De verdad que no me quiero liar, aunque ya me conoceis y podria llenar hojas y hojas con los problemas que los sonares activos les causan a los cetaceos, los jueguecitos americanos y sovieticos en la guerra fria con los sonares, las otras aplicaciones informaticas que utilizan el concepto, la equivalencia física con otros sistemas similares como el radar o el lidar, o las ventajas de los sonares pasivos arrastrados, entiendo que todo eso os da igual y prefiero no espesar mucho la entrada.

Me limitaré a decir que hacer ping en el contexto de las páginas web es mandar una señal a otras webs diciendo, hey, que tengo un rollo nuevo, y esperar recibir el lógico pong de esas webs confirmando que han actualizado sus enlaces con la nueva información.

Los que useis Wordpress o basuras similares supongo que solo tendreis que bajaros el plujin adecuado y tan contentos, pero los que, como yo, prefieren que su página siga siendo suya al ciento por ciento, no estar a la merced de errores ajenos, o simplemente que les guste programar, estabamos abonados a usar librerias extrañas para hacer algo que en realidad se me antojaba sencillísimo: enviar una petición web con una pizca de XML.

¿Solo para esa chorrada tengo que liarme con obtusas librerias experimentales??? Eso no va conmigo.

Así que en cuanto he tenido un rato, me he puesto al tema y lo he conseguido en unas poquisimas lineas. Ni siquiera he tenido que montarme historias de sockets como pensaba en un principio, los simples manejadores del PHP, que permiten acceder a una url como si fuera un fichero local me lo han dado todo practicamente hecho:

<?php

function ping($url)
{
  $params['http']['method'] = 'POST';
  $params['http']['content'] = '<?xml version="1.0"?>
<methodCall>
  <methodName>weblogUpdates.ping</methodName>
  <params>
    <param>
      <value>YOUR SITE TITLE</value>
    </param>
    <param>
      <value>http://YOUR BLOG URL</value>
    </param>
  </params>
</methodCall>';
  $params['http']['header'] = 'Content-Type: text/xml';
  $ctx = stream_context_create($params);
  $file = @fopen($url, 'rb', false, $ctx);
  if ($file)
  {
    $respuesta = @stream_get_contents($file);
  }
  return $respuesta;
}

echo ping('http://rpc.technorati.com/rpc/ping');

?>

Sencillisimo, ¿verdad?

Este PHP avisa a la conocida página technorati de que deberia echarle un vistazo a nuestra página. Lo suyo seria ejecutarlo cada vez que escribais un rollo en vuestras páginas, una vez actualizada la base de datos, y dejar que las visitas lleguen a trillones en busca de novedades.

Naturalmente, hay montones de páginas que se dedican simplemente a ser listas de las actualizaciones de otras como las nuestras, lamentablemente, tengo los enlaces en mi maquina portable, en cuanto la monte mando otro rollo con listas de direcicones a las que hacer ping para que vuestros blogs sean ultrafamosos y estén supervitaminados e hipermineralizados.

SocializerMenefante Del.icio.us Digg Ver los comentarios

Algoritmo de Huffman adaptativo

Por Saiyine Enviar correo el 2005-03-10 03:19:00 - Secciones:  ARTICULO ALGORITMOS PROGRAMACION  - Enlace permanente: 365

Dreamhost

Los códigos de Huffman son posiblemente la variante más conocida de los compresores estadísticos, que aprovechan las características estadísticas de los datos de entrada asignando códigos más cortos a los datos que mas se repiten, logrando así un efecto compresor.

Para lograrlo, el compresor debe recorrer una vez el vector con la información entrante para crear una tabla con la frecuencia de cada carácter, y luego otra vez para traducir la fuente en los códigos adecuados. Esta doble pasada no siempre es posible, y aquí es donde entran las variantes adaptativas, que son capaces de generar la compresión en una sola pasada, al ir modificando los códigos de salida conforme van llegando nuevos códigos a la entrada.

El algoritmo

Como ya mencioné anteriormente, la compresión Huffman adaptativa se diferencia de la normal en que los datos estadísticos se generan en la misma pasada que se envían los códigos de salida, y esto tiene un problema evidente: ¿cómo se envía un dato que aún no ha sido registrado en la tabla de símbolos? ¡Reconstruir la tabla con cada carácter nuevo requeriría muchísimo tiempo de CPU!

Por ello, lo que se hace es modificar el árbol binario del algoritmo Huffman añadiendo un valor especial llamado NYT, not yet transmitted, que servirá como marcador de nuevo carácter, es decir, si tenemos la entrada “arañar”, la salida seria “NYT a NYT r CODIGO(a) NYT ñ CODIGO(a) CODIGO(r)”.

Otro problema que surge es el de la optimalidad de los códigos, ya que si simplemente vamos añadiendo caracteres al árbol estadístico conforme aparecen en la entrada, se perderían las ventajas de la compresión Huffman. Por ello, cada vez que se lee un carácter hay que actualizar el árbol, que pasa a ser bastante mas complejo que el algoritmo no adaptativo, de una manera determinada, dependiendo de si es un nuevo carácter o si ya estaba en el árbol.


Fig. 1. Árbol de ejemplo, conteniendo la palabra “abra”

Los nuevos elementos que podemos advertir en el árbol binario del algoritmo adaptativo frente al clásico son la presencia del código NYT, la numeración de los nodos y un contador de cada carácter. Con este contador llevamos la cuenta de cuantas veces ha aparecido el valor, y es lo que definirá en que posición del árbol pondremos al carácter en caso de reorganización, mientras que la numeración nos servirá para decidir que nodos deben intercambiar su posición.

Un ejemplo paso a paso

Veamos con un ejemplo como funciona el algoritmo, usando la palabra “abracadabra”.


Fig. 2. Árbol vacío

Comenzamos con un árbol vacío, al que le llega el primer carácter, “a”.


Fig. 3. Árbol para “a”

Con la llegada del primer dato, el algoritmo envía el dato tal cual, “a” y después genera los primeros nodos del árbol, es decir, el nodo para el NYT, con el contador a cero, y el del propio dato, con el contador a uno, y el nodo padre, el raíz en este caso, con la suma de los contadores de sus hijos.


Fig. 4. Árbol para “ab”

Tenemos un nuevo dato, así que enviamos el NYT para comunicárselo al destino, seguido del propio dato, por lo que el mensaje enviado completo por el momento es “a 0b”. Este mensaje realmente se compone de los ocho bits del símbolo “a”, seguido del bit 0 y de los ocho de “b”, por lo que es importante que tanto compresor como receptor estén de acuerdo en el tamaño del dato base, que en el programa que nos ocupa es de ocho bits.

Una vez enviados los nuevos códigos, se reestructura el árbol añadiendo el nuevo carácter “b” como hijo derecho del NYT, y creando un nuevo NYT como hijo izquierdo, y actualizando los contadores en cascada hacia la raíz, efectivamente creando un árbol balanceado.



Fig. 5a. Árbol para “abr” no actualizado

Con el nuevo carácter “r” llegamos a un punto crucial del algoritmo: la actualización del árbol. Primero, se manda el NYT y el dato r, con lo que el mensaje comprimido es ahora “a 0b 00r” (recordemos que siempre se mandan los NYT de antes de meter el nuevo carácter en el árbol). Lo introducimos en el lugar del NYT y llegamos a una posición en la que no se cumple la posición de balanceo del árbol, ya que el nodo 49 tiene el valor 2, suma de los valores de sus dos hijos, mientras que su hermano 50 tiene el valor 1, ya que el carácter “a” solo ha aparecido una vez hasta ahora. Por lo tanto, los intercambiamos, en un ajuste de balanceo que en este algoritmo es llamado simplemente proceso de ajuste del árbol.


Fig. 5b. Árbol para “abr” actualizado

Otro carácter “a”, que como ya está en el árbol es enviado comprimido, con lo que tenemos “a 0b 00r 0”.


Fig. 6. Árbol para “abra”

Ahora es el turno del dato “c”, que como no está en el árbol, mandamos el NYT y el dato en claro: “a 0b 00r 0 100c”. Después lo añadimos al árbol.



Fig. 7a. Árbol para “abrac” no actualizado

De nuevo el árbol necesita ser actualizado, ya que los nodos 47 y 48 han dejado de cumplir la condición de balanceo.


Fig. 7b. Árbol para “abrac” actualizado

Otra “a”, con lo que el mensaje completo es ahora “a 0b 00r 0 100c 0”.


Fig. 8. Árbol para “abraca”

El dato “d” es nuevo, así que lo enviamos (“ a 0b 00r 0 100c 0 1100d ”)y expandimos el nodo de NYT para acomodarlo. Además, va a desequilibrar el árbol, que necesitará un ajuste.


Fig. 9a. Árbol para “abracad” antes de actualizar


Fig. 9b. Árbol para “abracad” balanceado

La tercera “a”, con lo que el mensaje comprimido es ahora “a 0b 00r 0 100c 0 1100d 0”.


Fig. 10. Árbol para “abracada”

El siguiente carácter “b”, aunque ya está en el árbol y podría parecer que simplemente bastaría con aumentar su contador y enviar su código, en realidad va a generar un proceso interesante que podría cambiar el árbol casi por completo: una actualización en cascada.


Fig. 11a. Árbol para “abracadab” sin actualizar

Primero, los nodos 45 y 46 deben ser intercambiados, ya al aumentar el contador de b, su nodo tiene mas “peso” que su hermano derecho.


Fig. 11b. Árbol para “abracadab” actualizándose

Intercambiados los nodos, aumentamos el valor de “b” y de los padres hasta la raíz, verificando en cada nodo si es necesaria una nueva actualización, aunque en este ejemplo no se da el caso.



Fig. 11c. Árbol para “abracadab” actualizado

Los dos caracteres que faltan, “r” y “a”, no tienen mucha historia, simplemente enviamos el código y actualizamos los contadores correspondientes, con lo que el código final es “a 0b 00r 0 100c 0 1100d 0 110 110 0”, es decir, 60 bits, frente a los 88 que harían falta para mandar el mensaje sin comprimir.


Fig. 12. Árbol para “abracadabr”


Fig. 13. Árbol para “abracadabra”

La descompresión

Ahora veamos un ejemplo de descompresión. Tenemos la cadena “a 0b 00r 0 100c 0 1100d 0 110 110 0” (de nuevo, imaginemos que en vez de los caracteres tenemos grupos de 8 bits con la representación ascii de dichos caracteres). Leemos los primeros ocho bits, ya que forzosamente el primer carácter debe estar sin codificar, con lo que ya tenemos la “a”, y la introducimos en el árbol. El siguiente dato, o bien es un uno si es otra vez la “a”, o es un cero para indicar el valor NYT. En nuestro caso, es un cero, así que lo leemos y los siguientes ocho bits, con lo que ya tenemos “ab”.

Ahora el descompresor tendría en su memoria un árbol análogo al de la figura 4, por lo que al leer un cero sabemos que debemos leer al menos un bit más. Efectivamente, nos llega otro cero, con lo que tenemos un NYT. Leemos otros ocho bits y ya tenemos “abr”. Actualizamos el árbol, igual que hacíamos en la compresión, y leemos un cero, lo que nos indica que tenemos otra “a”.

Leemos un bit y tenemos un uno, que no es ningún código conocido, leemos otro y tenemos uno-cero, que sigue siendo desconocido. Volvemos a leer y tenemos uno-cero-cero, que en la disposición actual del árbol ( fig 6 ) es un NYT. Leemos ocho bits más, tenemos “abrac”. Sacamos un cero de la cadena, y como sabemos por el árbol que es una “a”, tenemos “abraca”.

Se lee bit a bit hasta que tenemos una combinación con significado, y en este caso, se trata de nuevo de un NYT, acompañado de los ocho bits de la “d”, con lo que el código hasta ahora es “abracad”. Hasta el final, tenemos caracteres sueltos que completan el mensaje, que es, claro esta, “abracadabra”.

SocializerMenefante Del.icio.us Digg Ver los comentarios

Fondos de pantalla

. . .

Recomendamos


. . .

Descargas

  • ApagaPC
    apagapc241.exe  (3540)
  • LimpiaDocus
    LimpiaDocus001.exe  (2828)
  • RCM
    rcm001.zip  (2385)
  • Popmail
    popmail-0.4-psmn.tar.gz  (2463)
  • Manual de Delphi en PDF
    delphi_pdf.zip  (3052)
  • Evangelio del Perl
    Evangelio_del_perl.pdf  (2880)
  • Excel Simpsons
    Excel Simpsons  (3046)
  • . . .

    Proyectos Online

  • Saiyine Store
  • Kunowalls!!!
  • Fondos de pantalla
  • Picaday: imagenes sexys o chocantes.
  • Scarlett: fotos de Scarlett Johansson
  • WhatsmyIP: obtener tu IP pública
  • Uma Thurman: galeria de Uma Thurman
  • FunPics: imagenes graciosas
  • . . .

    Blogs

  • Por lo que más querais, no entreis a estos: Tapanez, Yhandros, Onez.

  • Otros en español: El Mundo Today, La libreta de Van Gaal, Chavalina, Kirai, Mundo Geek, Microsiervos.

  • Mis lecturas en guiri: Michael Yon, Coding Horror, YCombinator news, MySQL Performance, Slashdot.

    . . .
  • Utilidades

  •  Coral  (1235), cacheando webs
  •  Spam.la  (1467), correo de usar y tirar
  •  Mailinator  (1385), correo de usar y tirar
  •  Bug me not  (1246), absurdos registros di NO
  •  Trashmail  (1285), correo de usar y tirar

    . . .

  • Saipuntos

    Jomer: 2
    Yhandros: 1
    Cheimy: 1

    . . .

    Mis wikis

  • Gravastar, VirtualDub, Samuel Eilenberg

    Se suspenden las traducciones de la wikipedia hasta que no se aclaren las condiciones de su adquisicin prctica por parte de google.

    . . .

  • Busquedas


    Varios ejemplos de lo que buscaban visitantes recientes:

    . . .

    Página web ©2001-2010 Saiyine generada en s, con 4777287 visitas en total, hoy (de un total de 0 previstas).

    . . .