<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">

	<channel>
		<title>Genbetadev</title>
		<link>http://www.genbetadev.com</link>
		<description>
Información sobre el sector de los desarrolladores, el desarrollo de aplicaciones, para móviles, desarrollo web, bases de datos, frameworks y lenguajes de programación		</description>
		<pubDate>2013-05-26 02:06:10</pubDate>

		<generator>http://www.genbetadev.com</generator>
                    <item>
      <title><![CDATA[¿En que sector te gustaría trabajar?: la pregunta de la semana]]></title>
      <link>http://www.genbetadev.com/trabajar-como-desarrollador/en-que-sector-te-gustaria-trabajar</link>
      <guid>http://www.genbetadev.com/trabajar-como-desarrollador/en-que-sector-te-gustaria-trabajar</guid>
      <pubDate>Mon, 26 Nov 2012 08:33:30 +0000</pubDate>

      <author>josejuan</author>
      <description><![CDATA[
      <p><a href="http://www.genbetadev.com/respuestas/en-que-sector-te-gustaria-trabajar"><img alt="Cyber guerra" src="http://img.genbetadev.com/2012/11/cyberguerra.jpg" class="centro_sinmarco" /></a></p>

	<p>Si pudieras elegir, si todas las empresas se pegaran por contratarte, ¿en que sector te gustaría trabajar?, ¿quizás en la <span class="caps">NASA</span> programando satélites o robots?, ¿quizás programando sistemas de cotización bursátil?, ¿o lo tuyo es la ciberguerra?. Cuéntanos que actividad es en la que te gustaría realizarte como desarrollador: </p>

<blockquote><a href="http://www.genbetadev.com/respuestas/en-que-sector-te-gustaria-trabajar">¿En que sector te gustaría trabajar?</a>
</blockquote>

	<p>Recuerda que para responder, debéis hacerlo desde nuestra sección de <a href="http://www.genbetadev.com/respuestas/en-que-sector-te-gustaria-trabajar">Genbeta Dev Respuestas</a>(sigue el enlace) y en este post los comentarios están cerrados. Gracias.</p>

	<p><!--more--></p>

	<p>En la anterior pregunta de la semana, os preguntábamos <a href="http://www.genbetadev.com/respuestas/que-lenguaje-o-tecnologia-de-programacion-te-has-puesto-a-aprender">¿Qué lenguaje o tecnología de programación te has puesto a aprender últimamente y cómo?</a>. La respuesta ganadora ha estado muy disputada y no ha habido un ganador claro, de hecho, han sido <a href="http://www.genbetadev.com/usuario/69753">Pablo Herrera</a> y <a href="http://www.genbetadev.com/usuario/73045">Daniel Esteve</a> quienes han obtenido la mayor puntuación con las respuestas:</p>

<blockquote>Justo en este momento estoy tratando de configurar un buen entorno de desarrollo para Coffescript, que es una verdadera delicia para programar Javascript. Su desventaja: es tan nuevo que los editores aún no lo traen por defecto, hay que configurar bastante. El que mejor lo integra es WebMatrix pero es demasiado lento y trabaja con su propio servidor. Por el momento recomiendo bastante este lenguaje, en especial si ya se ha trabajado lenguajes similares, como python y ruby.</blockquote>

	<p>y</p>

<blockquote>Yo soy sencillamente un novato que quiere aprender por su cuenta, en estos momentos estoy en un frustrarte <span class="caps">PCPI</span> donde no es que tenga mucho que aprender… por mi propia cuenta estoy aprendiendo <span class="caps">PHP</span> para poder mantener,desarrollar y mejorar unos websites y esta siendo una gran aventura me he comprado uno de esos libros de anaya a nivel <span class="caps">MEDIO</span> de y eso que yo de <span class="caps">PHP</span> se lo justo pero sobre todo lo tengo para consulta de alguna y que otra función,código y consejo. Por suerte tengo unos cuantos amigos programadores y me echan una pequeña mano(no les dejo mas) y por uno de ellos estoy empezando a hacer mis pinitos en csarp sobre mono y como no LinuxMint, también por si te lo estas preguntando tengo 17 Años.
Adiós y gracias por dedicar unos minutos a leer estas palabras</blockquote>

	<p>Muchas gracias a todos por participar. Nos vemos la semana que viene con las mejores respuestas a la pregunta que os hemos propuesto. Y recordad que la sección de <a href="http://www.genbetadev.com/respuestas">Genbeta Dev Respuestas</a> está a vuestra disposición para resolver todas las dudas que tengáis.</p>

	<p>En Genbeta Dev Respuestas | <a href="http://www.genbetadev.com/respuestas/en-que-sector-te-gustaria-trabajar">¿En que sector te gustaría trabajar?</a></p>      ]]></description>
      </item>
                    <item>
      <title><![CDATA[¿Podemos mejorar nuestras condiciones laborales?: la pregunta de la semana]]></title>
      <link>http://www.genbetadev.com/trabajar-como-desarrollador/podemos-mejorar-nuestras-condiciones-laborales-la-pregunta-de-la-semana</link>
      <guid>http://www.genbetadev.com/trabajar-como-desarrollador/podemos-mejorar-nuestras-condiciones-laborales-la-pregunta-de-la-semana</guid>
      <pubDate>Thu, 04 Oct 2012 05:09:54 +0000</pubDate>

      <author>josejuan</author>
      <description><![CDATA[
      <p><img alt="Condiciones" src="http://img.genbetadev.com/2012/10/condiciones.jpg" class="centro" /></p>

<p>Nos guste o no, vivimos en una sociedad en la que la ley de la oferta y la demanda regula lo que valemos en ella. Todos nosotros compramos aquel producto que, cubriendo lo que pedimos, sea el más barato (si el único que cumple es el más caro, entonces, es también el más barato que podemos comprar).</p>

<p>A nuestro alrededor vemos todos los días a personas que tienen jornadas laborales mucho más duras que las nuestras (hablo en general, tengo pocas dudas de que la jornada de los mineros o de las cajeras [por poner dos ejemplos] es más dura que la nuestra) y sin embargo es bien conocido el malestar de nuestra profesión.</p>

<p>Entonces, ¿si o no tenemos lo que merecemos?, ¿se aprovechan de nosotros porque les dejamos o porque pueden?.</p>

<p>Y lo más importante, si como yo, eres de los que no se rinden,</p>

	<p><blockquote><a href="http://www.genbetadev.com/respuestas/podemos-mejorar-nuestras-condiciones-laborales">¿que podemos hacer para cambiar la situación?</a></blockquote><br />
<!--more--></p>

<p>Recuerda que para responder, debéis hacerlo desde nuestra sección de&nbsp;<a href="http://www.genbetadev.com/respuestas/podemos-mejorar-nuestras-condiciones-laborales">Genbeta Dev Respuestas</a> (sigue el enlace) y en este post los comentarios están cerrados. Gracias.</p>

<p>En la anterior pregunta de la semana os preguntábamos <a href="http://www.genbetadev.com/desarrolladores/que-eventos-para-desarrolladores-no-deberiamos-perdernos-la-pregunta-de-la-semana">¿Qué eventos para desarrolladores no deberíamos perdernos?</a> en la que se han anotado un buen número de eventos que pueden interesarte. El evento de desarrollo más valorado fue la respuesta de <a href="http://www.genbetadev.com/usuario/72604">Rubén López García</a>:</p>

<blockquote>Recomiendo el <span class="caps">HTML</span> Tour que se va a celebrar dentro de nada en Bilbao, Barcelona, Madrid, Sevilla y Pamplona&#8230;</blockquote>

<p>Muchas gracias a todos por participar. Nos vemos la semana que viene con las mejores respuestas a la pregunta que os hemos propuesto. Y recordad que la sección de Genbeta Dev Respuestas está a vuestra disposición para resolver todas las dudas que tengáis.</p>

<p>En Genbeta Dev |&nbsp;<a href="http://www.genbetadev.com/respuestas/podemos-mejorar-nuestras-condiciones-laborales">¿Podemos mejorar nuestras condiciones laborales?</a></p>      ]]></description>
      </item>
                    <item>
      <title><![CDATA[El hombre orquesta (complicando el IVA)]]></title>
      <link>http://www.genbetadev.com/trabajar-como-desarrollador/el-hombre-orquesta-complicando-el-iva</link>
      <guid>http://www.genbetadev.com/trabajar-como-desarrollador/el-hombre-orquesta-complicando-el-iva</guid>
      <pubDate>Thu, 06 Sep 2012 12:08:50 +0000</pubDate>

      <author>josejuan</author>
      <description><![CDATA[
      <p><img alt="El hombre orquesta" src="http://img.genbetadev.com/2012/09/hombre_orquesta.jpg" class="centro_sinmarco" /></p>

<p>A todos nos ocurre alguna vez, que nos asombramos jocosamente al ver algunas soluciones que otros han aplicado; sonreímos con vehemencia (en el mejor de los casos) y comentamos mientras hinchamos pecho, que esa barbaridad se resolvía fácilmente de tal o cual forma.</p>

<p>Yo sigo asombrándome y sonriendo con vehemencia, pero en lugar de sacar pecho, procuro ver y hacer ver, que quizás había una razón para ello, que quizás esa persona (anónima o no) supo encontrar una solución en un campo de batalla, en el que quizás nosotros nos hubiéramos perdido.</p>

	<p><p>Leyendo comentarios sobre el nuevo cambio de <span class="caps">IVA</span>, me he vuelto a asombrar y sonreír sobre algunas formas de enfocar el problema y me han venido a la cabeza la gran cantidad de variantes que hay a la hora de tratar con tan peliagudo problema. Un problema, que hace que muchos desarrolladores se conviertan en auténticos <b>&#8220;hombre orquesta&#8221;</b>. ¿O quizás piensas que es un tema sencillo?.</p><br />
<!--more--></p>

<h2>Advertencia</h2>

<p>No soy contable ni nada parecido, lo poco que se del <span class="caps">IVA</span> es a fuerza de tener que hacer o trabajar con software que lo incluye en sus procesos, así, no tomes mis requisitos de <span class="caps">IVA</span> como una referencia, más bien como situaciones que quizás te encuentres en algún momento. Y si detectas un error u otros requisitos del <span class="caps">IVA</span> ¡coméntalo por el bien de todos!.</p>

	<p><h2>El <span class="caps">IVA</span></h3></p>

<p>El <span class="caps">IVA</span> es muchísimo más complejo de lo que puede parecer a simple vista. Si bien es cierto que el caso habitual es conocido por todos (¿seguro?), existen muchísimas situaciones y variables que lo modifican de una u otra forma. Puedes buscar cualquier libro de contabilidad y repasarte los temas del <span class="caps">IVA</span>, verás que de sencillos y concisos no tienen nada (a mi desde luego no me lo parecen).</p>

<p>Como ni puedo, ni debo, ni quiero, ni (probablemente) querrás tú, no entraré en conceptos que difícilmente conozco así, sólo indicaré en general situaciones que se dan o se deben contemplar al trabajar con el <span class="caps">IVA</span>.</p>

<h3>Requisitos</h3>

<p>Sea pues una lista informal (y seguro que incompleta) de requisitos a la hora de trabajar con <span class="caps">IVA</span>:</p>
<ul>
<li>El <span class="caps">IVA</span> de un producto, puede ser diferente según se lo vendas a uno u otro cliente (eg. nacional / extranjero pero hay más casos como ciertas organizaciones que no pagan <span class="caps">IVA</span> o lo tienen reducido).</li>
<li>En caso (por ejemplo) nacional, tu aplicación, quizás deba aplicar el <span class="caps">IVA</span> local y descontarlo después, o no aplicarlo o contemplar ambos casos.</li>
<li>El <span class="caps">IVA</span> de un producto, puede ser diferente (además) del tipo de producto de que se trate (eg. productos de primera necesidad / productos de lujo).</li>
<li>El <span class="caps">IVA</span> de un producto, puede ser diferente (además) del <span class="caps">IVA</span> de otros productos incluidos en la venta (eg. los gastos de envío).</li>
<li>El <span class="caps">IVA</span> de un producto, puede ser diferente (además) si ha sido manipulado y/o agrupado (eg. packs, ofertas, &#8230;).</li>
<li>El <span class="caps">IVA</span> de un producto, puede depender (además) de la fecha de venta, pero resulta, que realmente manda la fecha de facturación ¡pero cuando vendes aún no sabes cuando se realizará la factura!. Por ello, frente a un cambio de <span class="caps">IVA</span>, la elección de la fecha en que cambia el <span class="caps">IVA</span> puede ser arbitraria (eg. unas empresas querrán facturar al finalizar el mes y otras al empezar, etc&#8230; incluso dependiendo del cliente final al que se le factura el día o semana que él decide).</li>
<li>El <span class="caps">IVA</span> de un producto, puede depender (además) de ¡la fecha de disfrute! (eg. una estancia hotelera dentro de un mes), que a su vez depende de la de facturación, etc&#8230; (hay hoteles que prefieren facturar al entrar el cliente, otros al salir, otros al terminar el mes de la fecha de salida, etc&#8230;)</li>
<li>El <span class="caps">IVA</span> de un producto, puede que ¡no tengas que procesarlo! (pero si el importe). Por ejemplo cuando incluyes en la venta productos o servicios que tú no facturarás (lo hará otra empresa ¡pero tú debes contemplar también su casuística!).</li>
</ul>

<p>Si ésto aún no es suficiente para ti (auténtico Chuck Norris del <span class="caps">IVA</span>), espera, porque hasta ahora únicamente hemos <i>&#8220;definido el <span class="caps">IVA</span>&#8221;</i> (incluyendo el concepto de <i>&#8220;desconocido&#8221;</i> no lo olvides).</p>

<p>Peticiones <i>&#8220;especiales&#8221;</i>:</p>

<ul>
<li>Se desean cargar los precios de venta con impuestos incluidos, de tal forma que <b>el precio final</b> sea <b>siempre</b> un número prefijado (eg. 100,00 euros). (Cuidadito con este requisito, en general, es una trampa <b>mortal</b> [no tiene solución para algunos casos]).</li>
<li>Junto con la anterior, se desean tener tarifas netas (cargadas sin impuestos). Es decir, debes procesar simultáneamente importes que llevan o no llevan el <span class="caps">IVA</span> (luego totalizar, agrupar impuestos, excepciones, gastos fuera de <span class="caps">IVA</span>, etc&#8230;).</li>
<li>Si has sido capaz de componer todas las reglas de negocio, ahora resulta que <b>debes exportar</b> los precios para que otro sistema los procese. (Cuidadito con los decimales, seguramente deberás desnormalizar datos, etc&#8230;).</li>
<li>Múltiples divisas y aquí hay mucho juego, puede ser que el cambio de divisa sea informativo o que se permita al cliente final pagar en la moneda elegida; puede ser que los precios se carguen en una divisa y deban convertirse a otra (manteniendo estrategias como <i>&#8220;precios redondos&#8221;</i>).</li>
<li>Pasarela de pago, convirtiendo los importes de una divisa a otra (cuidado decimales).</li>
<li>Aplíquense reglas adicionales <i>&#8220;al gusto&#8221;</i> sobre cálculo de comisiones (que se aplican sobre el neto y/o bruto) y el <span class="caps">IVA</span> de esas mismas comisiones.</li>
</ul>

<p>Bueno, seguro que hay muchas otras situaciones, he puesto las que me han venido a la cabeza y con las que de una u otra forma habré tenido que trabajar en algún momento.</p>

<p>Como anécdota diré, que una vez había que pasar ciertas tarifas de netas (sin <span class="caps">IVA</span>) a brutas (con <span class="caps">IVA</span>); tras comentar que el cambio no era trivial en nuestro sistema, nos comentó alguien de un sistema remoto con quien conectábamos que <i>&#8220;sólo es multiplicar los precios de una tabla&#8221;</i>, aún se pueden oír las risas de nuestro equipo (el extremo no conocía nada de nuestro sistema para poder hacer tal afirmación).</p>

<h2>Problemas fáciles, problemas difíciles</h2>

<p>Igual que con el <span class="caps">IVA</span>, con otros problemas se producen situaciones similares en todo momento. Problemas aparentemente triviales se convierten en auténticos quebraderos de cabeza, y son éstos, precisamente, de los que se oye recurrentemente la frase <i>&#8220;¡pues si es muy fácil!&#8221;</i>.</p>

	<p><p>Así, te animo a ser cauto y/o condescendiente cuando creas ver una barbaridad (chapuza, desastre, etc&#8230;) y esperes a tener más datos del problema para crucificar al pobre tipo que, al fin y al cabo, supo solucionar el problema.</p><br />
<del></del></p>      ]]></description>
      </item>
                    <item>
      <title><![CDATA[Programando con una fotocopiadora ]]></title>
      <link>http://www.genbetadev.com/paradigmas-de-programacion/programando-con-una-fotocopiadora</link>
      <guid>http://www.genbetadev.com/paradigmas-de-programacion/programando-con-una-fotocopiadora</guid>
      <pubDate>Fri, 24 Aug 2012 07:21:03 +0000</pubDate>

      <author>josejuan</author>
      <description><![CDATA[
      <p><img alt="Fotocopiadora" src="http://img.genbetadev.com/2012/08/fotocopiadora.png" class="centro_sinmarco" /><br />
<p>Me habrás oído decir en alguna ocasión, que el problema real que debemos solucionar y su solución, no reside en ningún lenguaje de programación concreto, en todo caso, el tener que usar un lenguaje concreto es una más de las restricciones del problema.</p></p>

<p>Esta forma de pensar y mi afición al juego de <a href="http://www.genbetadev.com/formacion/la-fascinante-busqueda-de-algoritmos">la búsqueda de algoritmos</a> me llevó hace unos tres años a encontrar una curiosa solución al esquivo problema <b>NP-completo</b>, al punto de poder resolverlo en tiempo lineal (sí, coste temporal en <b>O(n)</b>); un gran logro sin duda, si no fuera porque el tamaño de la memoria tiene coste exponencial (¡ouch!, <b>O(e<sup>n</sup>)</b>).</p>

	<p><p>Aún así, fue emocionante analizar esa posibilidad, porque la solución se basa en utilizar las propiedades de las transparencias y una fotocopiadora para codificar la solución. Si sientes curiosidad por ver cómo tan vetusto aparato puede solucionar tamaño problema te animo a leer <b>Programando con una fotocopiadora</b>.</p><br />
<!--more--></p>

<h2>El problema de la suma de subconjuntos</h2>

<p>Un problema muy sencillo de plantear y entender pero muy difícil de resolver, es el problema de la suma de subconjuntos. En él, se trata tan sólo de decir si <b>sí</b> o <b>no</b> es posible sumar una cantidad <b>K</b> con los números <b>C = {z1, z2, &#8230;zN}</b> que nos dan.</p>

<p>Por ejemplo, si <b>C = {1, 7, 3}</b> y <b>K = 4</b> es fácil ver que la respuesta es <b>sí</b>.</p>

<p>Para no salirnos del tema, puedes leer sobre <a href="http://en.wikipedia.org/wiki/NP-complete">NP-complete</a>. Para el caso que nos ocupa, será suficiente con que sepas que si alguien es capaz de resolver el problema anterior de forma eficiente, será capaz de resolver una gran cantidad de problemas muy importantes de forma eficiente, donde eficiente es resolverlos en tiempo polinomial.</p>

<p>Para que te hagas una idea, hoy en día no es posible encontrar la mejor organización de los componentes electrónicos en una placa (si no sabes nada de electrónica, te sonará que esas placas están llenas de pistas de cobre que van y vienen por todos lados), por ello, se utilizan algoritmos que intentan obtener una buena organización, pero no son capaces de asegurar <b>la mejor organización</b>. Otros muchos e importantes problemas prácticos deben ser aproximados (a veces es suficiente pero otras no) o no pueden ser resueltos y nadie sabe todavía si existe un algoritmo eficiente para resolverlos.</p>

<p>Al menos, se sabe que si alguien demuestra que uno de estos algoritmos no puede resolverse eficientemente, entonces se sabrá que no se puede ninguno (y no habrá que seguir buscando) y más interesante, si alguien demuestra que puede resolver eficientemente uno sólo de estos algoritmos, podrá resolver eficientemente <b>todos</b> estos algoritmos.</p>

<p>Por ahora, no existe mejor solución que (básicamente) ir revisando todas las posibilidades del problema e ir verificando una a una si se cumple o no. Bueno, en realidad (no seamos demasiado laxos), cuando se concreta un problema, siempre se puede hacer alguna mejora. Por ejemplo en el caso del conjunto suma, la mejor solución tiene coste <b>O(2<sup>n/2</sup>)</b> que es exponencialmente más eficiente que la trivial, pero sigue siendo exponencialmente más costosa que una polinomial.</p>

<h2>Algoritmos y ordenadores</h2>

<p>Obviamente cuando diseñamos un algoritmo una de las principales restricciones que tenemos es que debe correr en nuestro ordenador; aunque esta restricción sea obvia, no por ello deja de ser una restricción que podemos <b>considerar eliminar del problema</b>.</p>

<p>Los físicos y en particular ingenieros resuelven diariamente multitud de problemas que podrían definirse como <i>&#8220;algoritmos&#8221;</i>, un ascensor, unas escaleras mecánicas, los procesos para la elaboración de la lejía, etc&#8230; son soluciones que, puestas sobre el papel, se asemejan mucho a las soluciones que definimos los programadores. Una receta de cocina, no deja de ser un algoritmo que es ejecutado por un cocinero.</p>

<p>Puedes estar seguro, que si resuelves el problema <b>NP-complete</b> con una patata y un mondadientes, nadie se va a quejar de que no compila con <b><span class="caps">GCC</span></b>.</p>

<p>La solución eficiente, de ser encontrada, actuaría directamente en nuestros programas (como una <span class="caps">API</span>) o indirectamente como un coprocesador matemático. Muchas personas analizan este tipo de alternativas, en las referencias puedes encontrar una forma usando <span class="caps">DNA</span> y es un debate caliente en física la posibilidad de encontrar propiedades cuánticas que puedan ser explotadas para resolver problemas NP-complete.</p>

<h2>Las transparencias</h2>

<p>Una transparencia es como una hoja de papel pero transparente. Antiguamente (eg. cuando yo estaba en la Universidad) se usaba como sustituto de PowerPoint y similares. Al ser transparentes, se podía fácilmente dibujar por capas las partes de un coche, el cuerpo humano, etc&#8230; la forma de verlas, era poner una o varias en un proyector de transparencias.</p>

<h2>Resolviendo NP-complete con transparencias y una fotocopiadora</h2>

<p>Se trata de ver si dado un conjunto de números, algún subconjunto suma una constante dada. El algoritmo ingenuo es ir revisando todas las formas de agrupar los números y ver si alguna agrupación suma dicha constante. Pero claro, combinaciones de N elementos tomados de 0 en 0, tomados de 1 en 1, &#8230;, tomados de N en N hace un total de 2<sup>N</sup> agrupaciones, por tanto, en el peor caso, tendremos que revisar un número exponencial de agrupaciones.</p>

<p>Por ejemplo, sean los n&uacute;meros { 1, 2, 5, 9, 13 } y el valor que deben sumar sea 15, entonces har&iacute;amos:</p>
<table border="1" cellspacing="0" cellpadding="5" style="margin-left: 50px">
<tr><th>Iteraci&oacute;n</th><th>N&uacute;meros que quedan</th><th>Libreta</th></tr>
<tr><td align="right">1</td><td>{ 1, 2, 5, 9, 13 }</td><td>{ 0 }</td></tr>
<tr><td align="right">2</td><td>{ 2, 5, 9, 13 }</td><td>{ 0, 1 }</td></tr>
<tr><td align="right">3</td><td>{ 5, 9, 13 }</td><td>{ 0, 1, 2, 3 }</td></tr>
<tr><td align="right">4</td><td>{ 9, 13 }</td><td>{ 0, 1, 2, 3, 5, 6, 7, 8 }</td></tr>
<tr><td align="right">5</td><td>{ 13 }</td><td>{ 0, 1, 2, 3, 5, 6, 7, 8, 9, 10, 11, 12, 14, <b>15</b><span style="color: gray">, 16, 17</span> }</td></tr>
</table>

<p>Es decir, a cada paso, obtenemos el doble de números que la vez anterior porque seguimos dejando los números que teníamos y añadimos esos mismos números pero sumando el elemento que procesamos. Si algunos de los <b>nuevos números</b> es el que buscamos, detenemos el proceso.</p>

<p>¿Y si pudiéramos en cada paso sumar ese nuevo elemento a todos los grupos existentes <b>en un sólo paso</b> (simultáneamente)?, en ese caso, el algoritmo tendría coste lineal, pero resulta que con una transparencia podemos hacer precisamente eso, vemos como:</p>

<h3>Algoritmo para la fotocopiadora</h3>

<p>Como soporte de almacenamiento, necesitamos un máximo de tantas transparencias como números haya en la lista de entrada. Si nos dan 10 números, necesitaremos 10 transparencias como máximo, si nos dan 100 números tan sólo necesitaremos 100 transparencias como máximo. Fácil ¿no?, incluso conseguir 1000 transparencias no parece nada complicado.</p>

<p>El algoritmo para resolver &#8220;SubSet sum&#8221; en nuestra fotocopiadora es sencillo:</p>
<ul>
<li>Todas nuestras transparencias deben tener en la base una línea negra horizontal.</li>
<li>Tomamos la primera transparencia y la ponemos en la fotocopiadora desplazada una longitud linealmente proporcional al primero número.</li>
<li>Hacemos una fotocopia en una transparencia nueva.</li>
<li>Sin quitar la primera transparencia, ponemos la copia realizada desplazándola una longitud linealmente proporcional al segundo número de la lista.</li>
<li>Hacemos una fotocopia en una transparencia nueva.</li>
<li>Sin quitar ni la primera ni segunda transparencias, ponemos la copia realizada desplazándola una longitud linealmente proporcional al tercer número de la lista.</li>
<li>&#8230;, y así sucesivamente, repetimos el proceso haciendo tantas copias como números nos hayan dado o bien cuando exista una línea horizontal en la posición linealmente proporcional al valor constante buscado.</li>
</ul>

<p>El método es tan simple que apenas requiere de explicación. Cada fotocopia que realizamos contiene todos los números anteriores y al desplazarla, estamos sumando todos esos números al del desplazamiento ¡a la vez!.</p>

<h3>La solución no es práctica</h3>

<p>Probablemente pienses que no es operativo usar unas transparencias y una fotocopiadora, pero ese no es el problema de que el método no sirva en la práctica, los problemas técnicos asociados a la máquina no son tales. Uno puede decir que no es posible hacer una fotocopia cuando en lugar de una hoja tenemos un bloque de (digamos) 30 transparencias, pero este sencillo problema se resuelve haciendo una fotocopia adicional cada pocas (e incluso a cada una, seguiría siendo lineal) fotocopias &#8220;desplazadas&#8221;. También se puede quejar uno de que no es posible calibrar el desplazamiento que debe hacerse cuando los números son muy grandes (y por tanto el desplazamiento muy pequeño) pero existen elementos para calibrar a nivel atómico. Otros problemas como la dispersión de la luz al atravesar la transparencia o el poder reutilizar el soporte son &#8220;sólo&#8221; problemas técnicos que de una u otra forma podrían resolverse.</p>

<p>Es obvio que la construcción de la <i>&#8220;SubSet Sum Machine&#8221;</i> (como yo llamo a la hipotética máquina) distaría de ser la tan sencilla fotocopiadora, sin embargo es notable que su limitación difiere completamente de las limitaciones de los algoritmos actuales (exponenciales en tiempo) pues <b>siempre</b> podrá resolver linealmente cualquier problema en la que la constante <b>K</b> quepa en el dispositivo de almacenamiento (la transparencia).</p>

<p>La limitación real de la máquina consiste en que muchos algoritmos se transforman al problema <i>&#8220;subset sum&#8221;</i> usando una representación exponencial de <b>K</b> (algo como K = 2<sup>z</sup>) y por tanto el tamaño que debe tener la transparencia pasa a ser exponencial. Aunque pudiéramos calibrar nuestra fotocopiadora a nivel atómico y nuestras transparencias fueran una sucesión de átomos de hidrógeno, &#8220;sólo&#8221; podríamos disponer en cada hoja de aproximadamente 2.970.000.000 átomos, lo que significa que en una hoja sólo podríamos resolver problemas cuya constante K no supere ese valor. Esta limitación hace inútil (o al menos de momento) la solución propuesta.</p>

<h2>Conclusión</h2>

<p>Además de disfrutar con esta partida en la fascinante búsqueda de algoritmos, han quedado claras las propiedades exponenciales del problema NP-completo analizado. Si estás interesado en profundizar en las propiedades de tamaño problema y el estado del arte del mismo, te sugiero a un gigante en la materia, <b>David Pisinger</b>, con su artículo <a href="http://www.dcs.gla.ac.uk/~pat/cpM/JChoco/knapsack/papers/hardInstances.pdf">Where are the hard knapsack problems?</a> (son sólo 14 páginas).</p>

<h2><i>¡Eh! ¿y que pasa si yo lo resuelvo?</i></h2>

<p>Bueno, en otras disciplinas como las matemáticas (en las que tendrías que convencer a gente sesuda que lea tu trabajo) o en física (tendrías que conseguir inversores que te financien el desarrollo) sería complicado hacer valer tu trabajo <b>o tendrías que revelar tu descubrimiento</b>. Afortunadamente, si eres capaz de resolver eficientemente un problema NP-completo, podrás fácilmente colgar una página web en la que cualquiera pueda postear un problema <b>muy difícil</b> para tu algoritmo (elige alguno de entre 3SAT, <span class="caps">TSP</span>, Knapsack, etc&#8230;) y dile en cuanto tiempo lo tendrá resuelto (antes habrás analizado empíricamente el coste real de tu algoritmo para hacer predicciones precisas). <b>Sin revelar tu poderoso algoritmo</b>, convencerás al mundo de su valía y el dinero empezará a caer sobre tu cabeza.</p>

<p>Como diversión, está bien pensar que quizás lo resuelvas, pero advertido quedas de que se cree que tal solución <b>no existe</b> y por tanto, estarías buscando la piedra filosofal&#8230;</p>

<p>Más información | <a href="http://www.dcs.gla.ac.uk/~pat/cpM/JChoco/knapsack/papers/hardInstances.pdf">Where are the hard knapsack problems?</a>.<br />

Más información | <a href="http://es.wikipedia.org/wiki/NP-completo">NP-completo</a>.<br />

Más información | <a href="http://es.wikipedia.org/wiki/Problema_de_la_suma_de_subconjuntos">Problema de la suma de subconjuntos</a>.<br />

Más información | <a href="http://www.csd.uwo.ca/~jamie/.Refs/Courses/CS881/charlotte.html">Using <span class="caps">DNA</span> algorithms to solve NO-complete problems</a>.<br />
</p>      ]]></description>
      </item>
                    <item>
      <title><![CDATA[La fascinante búsqueda de algoritmos]]></title>
      <link>http://www.genbetadev.com/formacion/la-fascinante-busqueda-de-algoritmos</link>
      <guid>http://www.genbetadev.com/formacion/la-fascinante-busqueda-de-algoritmos</guid>
      <pubDate>Thu, 23 Aug 2012 09:12:27 +0000</pubDate>

      <author>josejuan</author>
      <description><![CDATA[
      <p><img alt="La fascinante búsqueda de algoritmos" src="http://img.genbetadev.com/2012/08/busqueda_de_algoritmos.jpg" class="centro_sinmarco" /><br />
<p>Hay cierto tipo de pasatiempos que, siendo pasatiempos, producen en quien los practican gratificantes emociones. Cuando se consigue un equilibrio entre dificultad y éxito es cuando más fuertes son las emociones. Preguntarle la hora a alguien enfrascado en resolver un Sudoku, es como preguntársela a una farola. ¿Cuantas veces os han tenido que repetir vuestras madres la frase <i>&#8220;¡a cenaaaaaar!&#8221;</i>, mientras repetís (nivel tras nivel) <i>&#8220;¡que ya voy!&#8221;</i>?. No es casualidad que juegos como el Tetris con suaves curvas de dificultad sean tan populares, mantienen al sujeto en ese preciso equilibrio de dificultad (reto) y éxito (progreso). En la mayoría de los casos da igual si <i>&#8220;ganas&#8221;</i> o no, de hecho, no <i>&#8220;ganar&#8221;</i> es un aliciente para intentarlo de nuevo (mientras se mantenga el equilibrio de éxito).</p></p>

	<p><p>Una de las principales cosas que hacemos los programadores es buscar algoritmos, soluciones eficientes a los problemas del día a día, esa búsqueda de mejorar nuestros procesos es normalmente un aliciente para muchos (y me incluyo). Pero igual que en el Counter-Strike, si el desafío se produce ante otros, toma mayor relevancia (así es el ser humano, que le vamos a hacer).</p><br />
<!--more--></p>

<h2>La fascinante búsqueda de algoritmos</h2>

<p><b>La fascinante búsqueda de algoritmos</b> tiene diferentes y variados modos de juego; podemos conocer la dificultad del problema o no, saber si hay solución, tener una definición estricta o que admite ajustes, en un lenguaje concreto, etc&#8230; pero dos de los que a mí más me gustan son:</p>
<ul>
<li>Problemas con una definición muy muy sencilla pero que son muy muy difíciles.</li>
<li>Problemas para los que hay una solución eficiente y encontrar una <b>variante</b> más eficiente todavía.</li>
</ul>

<p>Del primer tipo, es muy muy (pero que muy) improbable que encontremos una solución (para mí, al menos), pero su sencilla definición permite que le demos muchas vueltas al problema y encontremos montones de estrategias interesantes que luego podemos aplicar en otros algoritmos. Un ejemplo típico sería (nada más y nada menos) que buscar un algoritmo eficiente para un problema <b>NP-completo</b>, si no recuerdas cuales son, aquí tienes dos:</p>
<ul>
<li>Dado un conjunto de números <b>C = {n1, n2, &#8230;, nM}</b> responder <b>SÍ</b> o <b>NO</b> (nada más) a la pregunta <i>&#8220;¿Existe un subconjunto de <b>C</b> que sume <b>K</b>?&#8221;</i>. <span style="color: gray">Por ejemplo, si es C = {1, 7, 3} y K = 4 la respuesta es SI.</span></li>
<li>Dado un mapa de carreteras y una lista de ciudades, responder <b>SÍ</b> o <b>NO</b> (nada más) a la pregunta <i>&#8220;¿Existe una ruta que pase por cada ciudad <b>una sola vez</b>?&#8221;</i>. <span style="color: gray">Por ejemplo, en la imagen:</span><br />
<img alt="Hamiltoniano" src="http://img.genbetadev.com/2012/08/hamiltoniano.png" class="centro_sinmarco" /></li>
</ul>

<p>Del segundo tipo la dificultad depende de quien haya indicado la solución, es común en foros o en sitios como <a href="http://www.solveet.com/">Solveet</a> que los <i>&#8220;participantes&#8221;</i> vayan refinando las soluciones (incluso sus propias soluciones) en tal caso será frecuente que podamos aportar soluciones mejores refinando unos las soluciones de otros (en una especie de brainstorm informal). Otras veces sin embargo, las soluciones son <i>&#8220;oficialmente las mejores&#8221;</i> en cuyo caso será muy difícil que las podamos mejorar, pero siempre queda la posibilidad de mejorar casos concretos.</p>

<p>Quizás un ejemplo representativo de esto último podría ser el problema de triangular un polígono simple (como los que se estudian en el cole), es un problema abierto el obtener un algoritmo más sencillo que el de Bernard Chazelle (de coste lineal). Obtenerlo está (casi seguro) muy lejos de nuestro alcance, pero tomado como un juego, es emocionante darle vueltas y descubrir propiedades y estrategias que no se te habían ocurrido antes.</p>

<p>Así, existen infinidad de problemas de algorítmica de diversos temas y dificultades que podemos resolver (o intentar) con el fin de matar el tiempo, practicar un determinado lenguaje, aprender y pensar en nuevas estrategias, retar a los demás para saciar nuestra innata sed de competitividad, etc&#8230;</p>

<h2>Lugares donde encontrar desafíos</h2>

<p>En mi opinión los mejores sitios para obtener desafíos son la documentación universitaria (y biblioteca técnica y científica asociada), en ella se encuentran desafíos de todo tipo, mostrados con rigor, por personas que saben mucho sobre lo que escriben y cuyas soluciones, hayamos dado o no con ella, seguro que nos aportarán una información valiosa.</p>

<p>No tienen porqué ser libros cargados de teoremas y análisis extensos; muchos se centran en aspectos prácticos como al estilo de <i>&#8220;Gráficas por computadora en Basic&#8221;</i> que contienen montones de ejemplos. Hay bastantes y muy buenos cuyos títulos suelen ser <i>&#8220;Algoritmos de &#8230;&#8221;</i> que contienen un buen puñado de retos por resolver (con sus soluciones, claro).</p>

<p>En <a href="http://www.etnassoft.com/biblioteca/">OpenLibra</a> (comentado frecuentemente en GenBeta Dev) hay multitud de libros similares a los que puedes acceder gratuitamente.</p>

<p>Pero si no queremos seguir un método ni horario, también podemos recurrir a Internet por supuesto, aquí una pequeña lista de sitios que se me ocurren (seguro que hay muchísimos más):</p>
<ul>
<li><a href="http://en.wikipedia.org/wiki/List_of_algorithm_general_topics">List of algorithm general topics</a> en Wikipedia es un buen punto de partida, podemos navegar por las materias que más nos interesen (IA, gráficas, cálculo numérico, etc&#8230;).</li>
<li><a href="http://www.solveet.com/">Solveet</a>, que <a href="http://www.genbetadev.com/autor/juan-quijano">Juan</a> ya nos presentó <a href="http://www.genbetadev.com/desarrolladores/soolvet-mejora-como-desarrollador-made-in-spain">En su día</a> y que contiene problemas y soluciones en múltiples lenguajes.</li>
<li><a href="http://rosettacode.org/wiki/Rosetta_Code">Rosetta Code</a>, que contiene multitud de algoritmos resueltos en multitud de lenguajes.</li>
<li><a href="http://programmingpraxis.com/">Programming praxis</a>, sitio que lleva tiempo presentando periódicamente problemas y que la gente resuelve de manera informal (sin rankings ni nada parecido).</li>
<li><a href="http://projecteuler.net/">Project Euler</a>, que se centra en problemas de tipo numérico aunque no de forma estricta (eg. también de combinatoria).</li>
<li><a href="http://4clojure.com">4 Clojure</a>, específico para Clojure, guardando las versiones con tus progresos y validación automática sobre los test definidos en cada problema.</li>
<li><a href="http://www.codechef.com/">Code Chef</a>, que promueve la cooperación y competitividad.</li>
<li><a href="http://www.topcoder.com/">Top Coder</a>, sitio muy famoso y con muy elaboradas herramientas que a mí personalmente <b>no me gusta</b> porque tiene demasiado control sobre los programadores (y se trata de pasar un buen rato, no de sentirte observado).</li>
</ul>

<p>Pero recuerda, se trata de pasar un buen rato, encuentra aquellos problemas que están dentro de tu <i>&#8220;zona de juego&#8221;</i> (difíciles pero divertidos y asequibles a tu skill) y un consejo, nunca te tomes demasiado en serio los retos con que te encuentres.</p>

<p>Más información | <a href="http://en.wikipedia.org/wiki/Brainstorming">Brainstorming</a>.<br />

Más información | <a href="http://www.etnassoft.com/biblioteca/">OpenLibra</a> libros técnicos en donde seguro encontrarás retos interesantes.<br />

Más información | <a href="http://en.wikipedia.org/wiki/Polygon_triangulation">Polygon triangulation</a>.<br />
</p>      ]]></description>
      </item>
                    <item>
      <title><![CDATA[Diseño emergente en la base de datos]]></title>
      <link>http://www.genbetadev.com/bases-de-datos/diseno-emergente-en-la-base-de-datos</link>
      <guid>http://www.genbetadev.com/bases-de-datos/diseno-emergente-en-la-base-de-datos</guid>
      <pubDate>Mon, 20 Aug 2012 10:39:34 +0000</pubDate>

      <author>josejuan</author>
      <description><![CDATA[
      <p><img alt="SQL emergente" src="http://img.genbetadev.com/2012/08/sql_emergente.jpg" class="centro_sinmarco" /></p>

<p>Es muy frecuente al desarrollar aplicaciones que los requisitos no estén completamente definidos desde el principio y/o que éstos sufran cambios a corto y medio plazo, bien porque son descartados, modificados o porque aparecen otros nuevos. Es por ésto que las metodologías ágiles establecen estrategias que se adaptan a dichos precarios escenarios.</p>

<p>Carlos Ble en su post <a href="http://www.carlosble.com/2012/08/diseno-emergente-tambien-para-la-base-de-datos/">Diseño emergente, también para la base de datos</a> describe una forma de trabajar a nivel de base de datos mediante un enfoque emergente. Si bien muchos programadores llevan utilizando estrategias ágiles (o emergentes) desde hace mucho tiempo, y que lo que Carlos describe también se realiza desde hace mucho tiempo; la clarividencia con la que técnicas concretas (Scrum, Kanban, &#8230;) y Carlos en su artículo ponen de manifiesto un determinado aspecto es, a mi juicio, muy interesante y revelador, porque te hace pensar de forma consciente sobre problemas que llevas resolviendo, <i>&#8220;inconscientemente&#8221;</i>, desde hace mucho tiempo.</p>

<p>Así, con independencia de lo correcto o no de cada solución, del tipo de contexto que cada uno tengamos que resolver (y que serán diferentes), el compartir nuestras estrategias hace que cada cual piense en sus propios problemas y tome de cada uno aquello que le resulta útil.</p>

	<p><p>Por tanto, te propongo un ejemplo práctico de cómo normalmente yo aplico el enfoque de <b>Diseño emergente en la base de datos</b>.</p><br />
<!--more--></p>

<h2>Breve reseña al diseño emergente</h2>

<p>Cuando alguien diseña algo (un avión, una casa, un coche, &#8230;) es habitual planificar todos y cada uno de los aspectos que finalmente formarán parte del producto. Así, todo es diseñado y planificado de antemano. A modo de ejemplo llevado al extremo, podemos imaginar un sencillo y humilde tornillo ubicado en la parte interna de uno de los motores de los ya obsoletos transbordadores espaciales de la <span class="caps">NASA</span>. Es muy probable que dicho tornillo esté presente explícitamente en multitud de planos (no en todos, claro, en algunos estará presente de forma implícita) y que su tamaño, dimensiones y composición química fueran diseñados o elegidos realizando un cálculo concreto. Quien montara el motor, puso ese tornillo y no otro, porque así <b>estaba decidido de antemano</b>.</p>

<p>Un carpintero, puede construir una silla a partir de planos similares a los usados en la <span class="caps">NASA</span>, pero será probable que no se le indique el tipo concreto de tachuela que debe usar ni cuantas debe poner. Así, cuando el carpintero tenga que poner las tachuelas a la silla, <b>decidirá en ese mismo momento</b> que tipo de tachuela pondrá y con que separación. Es decir, el diseño final de la silla <b>ha emergido</b> del propio proceso de construcción, en oposición a una cuidadosa planificación previa.</p>

<p>Llevado al extremo, podríamos imaginar a un artista frente a un tronco de madera; el artista no sabe (no ha decidido aún) que figura va a esculpir, empieza observando el material, va esquilmando los trozos de madera agrietados o dañados, golpeando aquí y allá. Al poco vislumbra la imagen de un caballo rampante y comienza a dar grosera forma al material, pero aún no sabe cual será la posición de las patas, la cola, la crin, &#8230; todos y cada uno de los golpes del artista <b>son espontáneos</b> no hay absolutamente ningún tipo de diseño previo, cuando termina su obra, podemos decir que <b>el caballo rampante ha emergido del proceso</b> realizado por el artista.</p>

<p>En el diseño de software y concretamente en el agilismo, el diseño emergente se produce al aplazar decisiones, análisis, trabajos y/o requisitos sin que ello impida comenzar a desarrollar <b>y utilizar</b> las aplicaciones y sistemas involucrados. En cierto momento, <b>surge la necesidad</b> de implementar un requisito que ni siquiera se había comentados con anterioridad, este nuevo requisito <b>a emergido</b> del uso y/o desarrollo de la aplicación.</p>

<h2>¿Es necesario el diseño emergente en la base de datos?</h2>

<p>El artículo de Carlos me gustó porque puso de manifiesto un aspecto que ha estado presente desde siempre, pero que él ha sabido exponer de forma explícita y no como <i>&#8220;un problema menor&#8221;</i> con el que nos encontramos a diario. En los siguientes párrafos, intentaré transmitir el tipo de problema que se trata.</p>

<p>Como en cualquier elemento de software, un cambio en una parte afecta a otras y las bases de datos (concretamente relacionales) no son una excepción. Por ejemplo el añadir, quitar o cambiar las propiedades de un campo de una tabla, requiere normalmente ajustar otras partes del sistema. En casos concretos se dispone de herramientas que automatizan ciertos procesos (eg. <span class="caps">CRUD</span>), <b>pero en general</b>, cualquier cambio aparentemente nimio, exige un esfuerzo apreciable en otras partes del sistema.</p>

<p>El uso de <b><span class="caps">ORM</span></b> (Object relational mapping) es hoy muy común y permite automatizar buena parte del trabajo que hace un <b><span class="caps">DBA</span></b> (DataBase Administrator) tiene bastantes (y ágiles) ventajas. Algunos de los problemas que voy a describir se mitigan o eliminan completamente usando un <span class="caps">ORM</span> pero otros se mantienen igualmente. A mi en particular sólo me gusta usar <span class="caps">ORM</span> en aplicaciones compactas que hacen uso intensivo de un framework (eg. un portal web), en general, yo me siento muy cómodo teniendo en todo momento control sobre la base de datos, por lo que asumiré en lo siguiente que no se usa un <span class="caps">ORM</span>.</p>

<p>Respecto de las bases de datos, algunos ejemplos habituales que me vienen a la cabeza son los <i>&#8220;campos zombies&#8221;</i>, que serían aquellos que se introducen en el diseño pero que luego nunca se usan o se usan realmente poco, están en la base de datos, pero como si no estuvieran, de forma similar tendríamos las <i>&#8220;tablas zombies&#8221;</i>; otro ejemplo podrían ser las <i>&#8220;tablas tocapelotas&#8221;</i> que en diseño se han definido como relaciones <i>&#8220;uno a cero o uno&#8221;</i> pero cuya partición termina siendo un infierno porque realmente debería ser una única tabla y en todas las consultas se arrastra un horripilante <i>&#8220;<span class="caps">LEFT</span> <span class="caps">OUTER</span> <span class="caps">JOIN</span>&#8221;</i> que nunca o casi nunca actúa (siempre se hace el <span class="caps">JOIN</span>); otros muchos ejemplos provienen de un exceso o falta de normalización, a veces el diseño está muy normalizado penalizando en demasía el rendimiento obligando o bien a desnormalizar el diseño o a crear estructuras adicionales de cacheado; el caso contrario es una falta de normalización (cuando en diseño se piensa que el producto cartesiano en la práctica será pequeño, pero no es así) que produce tablas con ingentes cantidades de registros (innecesarios).</p>

<p>En definitiva, existen muchas situaciones en las que el mejor diseño no está claro (los motivos de que no estén claros son muy variados y pocas veces debidas a cuestiones técnicas) y es sólo cuando la aplicación se pone en marcha que <b>emergen</b> esos fallos dejando patente un mal diseño.</p>

<h2>Diseño emergente en la base de datos</h2>

<p>Como solución a los problemas mencionados, Carlos propone incluir en las tablas que lo requieran un campo en el que serializar todos aquellos datos cuyo diseño no esté claro aún. Por ejemplo, podemos llamar al campo <b>xdata</b> y serializar a <b>json</b>, <b>xml</b>, &#8230; después utiliza esa información en la capa de aplicación.</p>

<p>Yo utilizo ese esquema cuando los datos dentro de <b>xdata</b> no tengo que procesarlos internamente en la base de datos, habitualmente porque hay aplicaciones externas y personalizables que hacen uso de ella. Aun así, rara vez directamente en un campo <b>xdata</b> como tal, yo prefiero normalizar ese campo, veamos como.</p>

<h2>La tabla de registro</h2>

<p>Muchos conoceréis el <i>&#8220;Windows Registry&#8221;</i>, realmente no es mas que una gran tabla que asigna un valor a una clave. La estructura arbórea que surge, proviene únicamente de fijar delimitadores en las claves (como una ruta de archivo en <b>/home/josejuan/datos/listado.txt</b>).</p>

<p>Un resumen muy general de mi estrategia preferida sería precisamente esa, tener una tabla que almacena pares de <b>&#8220;clave + valor&#8221;</b>.</p>

	<p><p>Por poner un ejemplo, los diferentes estados en los que puede estar un semáforo, se podrían codificar como:</p><br />
<script src="https://gist.github.com/3403233.js"> </script></p>

	<p><p>De esta forma, no es preciso crear una tabla semáforo para almacenar los posibles estados en que puede estar. Si por ejemplo cuestionamos la necesidad de tener un campo <b>&#8220;telephone 3&#8221;</b> en la tabla <b>&#8220;<span class="caps">USER</span>&#8221;</b> se podría codificar en la misma tabla anterior como:</p><br />
<script src="https://gist.github.com/3403242.js"> </script></p>

<p>Por claridad y rendimiento, suelo tener tablas de registro especiales para conjuntos de datos especiales. Por ejemplo, si nuestro sistema cuenta con entidades <i>&#8220;<span class="caps">EMPRESA</span>&#8221;</i> será habitual tener una tabla de registro especial para esta entidad.</p>

<p>Me consta que este tipo de estrategias de una u otra forma es usada por muchos programadores.</p>

<h2>Ejemplo práctico</h2>

	<p><p>Por escribir algo de código y clarificar la estrategia anterior, supongamos que estamos creando una aplicación (usando <b>Extreme programming</b>) y que por el momento únicamente necesitamos registrar cuentas de acceso para los usuarios, en tal caso, con la siguiente tabla sería suficiente:</p><br />
<script src="https://gist.github.com/3403297.js"> </script></p>

	<p><p>El <b>Product Owner</b> rápidamente empieza a pedir requisitos que si bien están claros, son coherentes y son validables, tenemos serias dudas de su utilidad práctica final (algo subjetivo mientras no se demuestre lo contrario) ¿serán necesarios los cuatro campos de teléfono, dos de fax, el correo principal, el secundario y otros tantos campos <i>&#8220;de imperiosa necesidad&#8221;</i>?. Tras ver las orejas al lobo, decidimos cumplir con los requisitos usando una tabla de registro, que podría ser ésta:</p><br />
<script src="https://gist.github.com/3403320.js"> </script></p>

	<p><p>Por tema de eficiencia, hemos dejado dos campos valor: uno pequeño y con datos en la página de registro y otro blob de acceso lento. Como verás, el campo pequeño es de tipo <b>varbinary</b> porque será capaz de almacenar cualquier tipo de dato que sea necesario (nosotros almacenamos un número como un número, no como una cadena, una fecha como una fecha, etc&#8230;).</b></p>

	<p><p>Para probar el rendimiento de las consultas, vamos a generar 10.000 registros de usuario con aproximadamente unos 100.000 registros de valores en la tabla de registro. Esto se puede hacer con el siguiente script:</p><br />
<script src="https://gist.github.com/3403328.js"> </script></p>

<p>Los datos han generado para cada usuario la siguiente información:</p>
<ul>
<li><b>last_access</b>, un campo fecha con la fecha de último acceso del usuario.</li>
<li><b>error_count</b>, un campo numérico con el número de errores asociados al usuario.</li>
<li><b>message_status</b>, un campo de texto con un mensaje de estado asociado al usuario.</li>
<li><b>message list</b>, que es <b>una tabla</b> asociada al usuario con un histórico de mensajes (mensaje 1, mensaje 2, &#8230;).</li>
</ul>

<p>Para acceder a los datos, se puede utilizar una vista que simule una tabla, de forma que se minimizan los cambios a realizar en las consultas (básicamente añadir o quitar campos) en caso de cambios en la estructura. Por ejemplo la siguiente vista permite acceder (indizada adecuadamente, es decir, es eficiente) a los campos de registro de cada usuario:</p><br />
<script src="https://gist.github.com/3403517.js"> </script><br />
<p>(Esta consulta es estándar pero puede implementarse mediante <b><span class="caps">PIVOT</span> <span class="caps">TABLE</span></b> que en ciertos escenarios puede tener un mejor rendimiento).</p>

	<p><p>Para acceder al histórico de mensajes, también podemos simular una tabla a partir de la de registro, por ejemplo:</p><br />
<script src="https://gist.github.com/3403522.js"> </script></p>

<p>En ambos casos, a partir de una tabla de registro a la que pueden añadirse estructuras adicionales, hemos creado dos <i>&#8220;tablas virtuales&#8221;</i> sin tener que modificar la estructura de la base de datos.</p>

	<p><p>Por ejemplo, para listar los usuarios con más de 9 errores en los últimos 3 días bastaría escribir:</p><br />
<script src="https://gist.github.com/3403714.js"> </script></p>

	<p><p>O bien para saber cual es el número medio de mensajes y longitud media por mensaje de todos los usuarios bastaría con hacer:</p><br />
<script src="https://gist.github.com/3403724.js"> </script></p>

<p>Como vemos, podríamos incluso realizar vistas y consultas sobre datos (claves) que ni siquiera existen.</p>

<h2>Utilidad de la tabla de registro</h2>

<p>La utilidad de la tabla de registro es bastante limitada como medio de evolucionar <i>&#8220;emergentemente&#8221;</i> una aplicación. En mi opinión, otras técnicas ágiles, usar <span class="caps">ORM</span> o refactorizar serán probablemente mejores alternativas que trabajar en el diseño y estructura de la aplicación apoyándose en la tabla de registro.</p>

<p>Además, aunque depende del tipo de aplicación, cambios en la lógica de negocio e interface de usuario son mucho más costosos que en la base de datos, un <b><span class="caps">DBA</span></b> experimentado no tiene problemas en añadir, eliminar o modificar objetos de la base de datos, quizás son algo molestos, sobre todo si lo usas en combinación con una herramienta <span class="caps">CASE</span> y datos en vivo, pero nada que no se pueda hacer en unos minutos.</p>

<p>Sin embargo, la tabla de registro es insustituible como medio para añadir de forma rápida y versátil estructuras que no suponen un coste añadido o a posteriori. Para almacenar valores de informe, de histórico, datos constantes, de configuración, &#8230; y en general, datos que no tendrán un coste o cobertura muy alta en la aplicación (si un dato es altamente actualizado, accedido, mapeado, &#8230; desde muchos puntos, será preferible que tenga <i>&#8220;entidad propia&#8221;</i>).</p>

<p>Así, yo la uso indiscriminadamente como forma de disponer de un gestor <b>No-<span class="caps">SQL</span></b> dentro de mi gestor <b>Sí-<span class="caps">SQL</span></b>.</p>

<p>Vía | <a href="http://www.carlosble.com/2012/08/diseno-emergente-tambien-para-la-base-de-datos/">Diseño emergente, también para la base de datos</a>.<br />

Más información | <a href="http://en.wikipedia.org/wiki/Emergent_Design">Emergent Design</a>.<br />

Más información | <a href="http://en.wikipedia.org/wiki/Windows_Registry">Windows Registry</a>.<br />

Más información | <a href="http://en.wikipedia.org/wiki/Extreme_programming">Extreme programming</a>.<br />

Más información | <a href="http://en.wikipedia.org/wiki/Object-relational_mapping">Object relational mapping</a>. <a href="http://en.wikipedia.org/wiki/Database_administrator">Database Administrator</a>.<br />

</p>      ]]></description>
      </item>
                    <item>
      <title><![CDATA[¿Dónde está la complejidad?]]></title>
      <link>http://www.genbetadev.com/metodologias-de-programacion/donde-esta-la-complejidad</link>
      <guid>http://www.genbetadev.com/metodologias-de-programacion/donde-esta-la-complejidad</guid>
      <pubDate>Wed, 08 Aug 2012 05:01:00 +0000</pubDate>

      <author>josejuan</author>
      <description><![CDATA[
      <p><img alt="Dónde está la complejidad" src="http://img.genbetadev.com/2012/08/dondeesta.jpg" class="centro_sinmarco" /></p>

<p>En ocasiones anteriores hemos visto que como programadores, podemos escribir el código <i>ingenuo</i> (evidente, fácil, sin pensar, &#8230;) o podemos entrenarnos (como los deportistas) para obtener un mejor rendimiento a la hora de escribir nuestro código. Mejor rendimiento es, por supuesto, obtener el resultado más próximo al ideal dentro de las restricciones fijadas por el contexto; por ejemplo, si tenemos un minuto para escribir el algoritmo, alguna optimización podremos hacer, pero no podremos darle demasiadas vueltas (o si prima la legibilidad, etc&#8230;).</p>

<p>Una de las cosas que suelo observar, es que muchos programadores se centran en el lenguaje, en el hecho de que están programando una máquina. Sí, eso es evidente, pero si queremos resolver un problema, deberemos comprender en dónde reside su dificultad intrínseca (la que no depende nada más que de ese problema). Y en un algoritmo, su dificultad reside en su complejidad computacional intrínseca (la que no depende de la implementación).</p>

	<p><p>Así pues, <b>¿dónde está la complejidad de los algoritmos?</b>.</p><br />

<!--more--></p>

<h2>Breve introducción a la complejidad computacional</h2>

<p>Cuando en algún sitio lees algo como <i>&#8220;tal algoritmo tiene un coste de <b>O(n log n)</b>&#8220;</i>, o algo como <i>&#8220;la complejidad de ese proceso es <b>O(n<sup>2</sup>)</b>&#8220;</i>, o más rebuscado como que <i>&#8220;puede mejorarse a <b>O(|xs| * n + n + n log n)</b>&#8220;</i>; lo que te están indicando es algo realmente útil y que no es más, que <b>el número máximo de operaciones necesarias para resolver el problema</b>.</p>

<p>Pongamos algún ejemplo. El siguiente algoritmo para calcular el valor máximo y el valor mínimo de una lista de números tiene por coste <b>O(2n)</b> donde <b>n</b> es la longitud de la lista, veámoslo:</p>

	<p><script src="https://gist.github.com/3246141.js"> </script></p>

<p>Obviamente puede hacerse en <b>O(n)</b> de esta forma:</p>

	<p><script src="https://gist.github.com/3246143.js"> </script></p>

<p>(Nota: el algoritmo es sencillo pero, ¿habrías puesto el <b>else</b>?)</p>

<p>Sin embargo, si sabemos que la lista está ordenada, resulta que el coste es <b>O(2)</b>. Veamos:</p>

	<p><script src="https://gist.github.com/3246145.js"> </script></p>

<p><i>&#8220;Si, bueno&#8221;</i> te dirás, <i>&#8220;¿Pero a que nos referimos con <b>número de operaciones</b>?&#8221;</i>. Cierto, en el último algoritmo (con coste <b>O(2)</b>) no se realizan solo dos operaciones, si mirarámos el código que al final tiene que ejecutar la máquina veríamos que realiza muchas operaciones, no solo dos.</p>

<p>En realidad, para no añadir de golpe muchas ideas, arriba he dicho que <b>O(n)</b> (y otros) representa el número de operaciones, pero no es cierto. La <b>complejidad computacional</b> analiza el coste de un algoritmo en todos los sentidos posibles, incluida la cantidad de memoria necesaria para resolverlo, accesos a disco y, en general, cualquier <b>coste</b> que sea digno de tener en cuenta.</p>

<p>Por tanto, si comparamos el primer y segundo algoritmos que hemos escrito, lo que <b>realmente</b> va a marcar una diferencia significativa es la longitud de la lista de entrada y por eso se indica <b>el coste</b> del algoritmo en base a dicha longitud. Por ejemplo, si el número de valores a agregar fueran significativos (máximo, mínimo, promedio, cuadrado, etc&#8230;) entonces probablemente, indicaríamos el coste del algoritmo no sólo con la longitud <b>n</b> de la lista, sino también con el número (por ejemplo <b>m</b>) de valores a agregar.</p>

<p>Así, una buena especificación del coste de un algoritmo, <b>tiene que</b> tener en cuenta todas aquellas variables (<b>n</b>, <b>m</b>, &#8230;) que de variar, afectarán drásticamente al rendimiento del algoritmo.</p>

<p>Sólo por poner un ejemplo de especificación de rendimiento de algoritmos en base a otra cosa que no sea la velocidad de ejecución (número de operaciones), mostraré la comparativa en cantidad de memoria utilizada. Supongamos que queremos saber, dada una lista de números, que elemento ocupa la posición central tras aplicar una operación a todos los números y ordenar los resultados.</p>

<p>Una forma de hacerlo es usando un array temporal:</p>

	<p><script src="https://gist.github.com/3246147.js"> </script></p>

<p>Que tiene un coste de memoria <b>O(n)</b> adicionales pues debemos crear un array temporal; sin embargo, en cuanto a velocidad, supuesto que el cómputo con un coste relevante sea <b>funcOperator</b>, sólo requiere <b>O(n)</b> operaciones (estamos suponiendo que el coste de la ordenación puede despreciarse). Sin embargo, es posible hacerlo sin memoria adicional (¿sabrías hacerlo?), veamos como:</p>

	<p><script src="https://gist.github.com/3246150.js"> </script></p>

<p>En este caso no hace falta memoria adicional, así, el coste de memoria (adicional) es <b>O(0)</b> ¡gratis!; sin embargo, en cuanto a velocidad, en las mismas suposiciones que el de antes, supone un máximo de <b>O(n<sup>2</sup>)</b> operaciones.</p>

<p><i>&#8220;¿Y saber este tipo de cosas es importante?&#8221;</i>, importante no, ¡importantísimo!. Nuestro trabajo consiste en programar y/o utilizar algoritmos para resolver los problemas ¿cómo vamos a elegir los adecuados si no conocemos <b>sus propiedades</b>?. Supón que eres un transportista y que tienes una furgoneta, ¿aceptarías el compromiso de llevar un paquete de Madrid a Barcelona sin conocer previamente el peso y dimensiones del paquete y el peso y dimensiones máximas que soporta tu furgoneta?, si fueras un buen transportista, no lo aceptarías sin conocer esa información y contrastarla.</p>

<p>Muchos programadores <i>&#8220;funcionan&#8221;</i> escribiendo el código en algún lenguaje y <i>&#8220;viendo&#8221;</i> por donde <i>&#8220;cojea&#8221;</i>. Usar un depurador, un profiler, una herramienta de cobertura de código, etc&#8230; <b>es muy mala práctica</b> si se usan como el medio a través del cual escribimos nuestro código (son buenas prácticas para prevenir, detectar y corregir los inevitables errores que todos los humanos cometemos). Digamos que todos pondríamos un extintor en la cocina por si se prende fuego ¡pero nadie prende fuego a la cocina para asar un pollo porque tenga un extintor!.</p>

<h2>¿Dónde está la complejidad?</h2>

<p>Ahora que hemos definido <b>complejidad</b> y hemos visto que es algo a lo que siempre debemos estar atentos para escribir un buen código, ¿cómo la encontramos?, ¿en qué nos tenemos que fijar para saber qué hace que un algoritmo sea más o menos complejo que otros, y encontrar por tanto, las mejores alternativas a nuestros problemas?.</p>

<p>Inevitablemente las herramientas con que contemos para resolver el problema (eg. el lenguaje de programación que debamos usar) serán determinantes (y por tanto debemos conocer las propiedades del lenguaje), sin embargo, habitualmente hablamos de lenguajes de propósito general precisamente, porque en teoría, el lenguaje no impone ninguna limitación a la hora de implementar una estrategia (un algoritmo) determinado (en la práctica si, obviamente).</p>

<p>En los ejemplos anteriores, se han presentado diferentes estrategias (algoritmos) escritos en javascript (implementación), pero cualquier programador puede entender la siguiente estrategia (¡algoritmo!): <i>&#8220;ordénese una lista de números; en la primera posición encontraremos el mínimo y en la última posición el máximo&#8221;</i>. ¿Hace falta implementar esta estrategia (algoritmo) en algún lenguaje concreto para saber que tiene un coste <b>O(n log n)</b>?, no, claro que no, porque de alguna forma, hemos sintetizado (abstraído) los elementos del problema.</p>

<p>Así, al enfrentarnos a un problema, debemos hacerlo en una primera etapa, pensando únicamente en las propiedades esenciales (abstractas) de los elementos que componen el problema. Luego, en la medida que conocemos esas propiedades, podemos pasar a nuestro <i>&#8220;contexto de implementación&#8221;</i> y ver cuales de esas propiedades son las más adecuadas para explotar en dicho contexto.</p>

<p>Pero es que además, si tenemos libertad en fijar el contexto de implementación, <b>seremos capaces de elegir el mejor lenguaje en cada situación</b> (o herramientas en general). Por ejemplo, en <a href="http://www.solveet.com/exercises/Problema-de-las-Olimpiadas-rusas/38">Solveet</a> propusieron el siguiente problema: <i>&#8220;si escribimos consecutivamente todos los números naturales (sin dejar espacios), tendremos algo como 12345678910111213&#8230; ¿qué dígito encontraremos en la posición <b>n</b>?&#8221;</i>. Sólo tras pensar en el problema, en las propiedades que posee, podemos llegar a soluciones como esta en shell:</p>

	<p><script src="https://gist.github.com/3246325.js"> </script></p>

<p>(Que no es nada eficiente, pero sí muy concisa). Otros ejemplos son aquellos en los que es posible usar programación lineal, ¡ni siquiera hace falta escribir código para resolver el problema!.</p>

<h2>Conclusión</h2>

<p>La complejidad de los algoritmos que como programadores nos vemos obligados a escribir y utilizar todos los días, reside únicamente en el problema <b>real</b> que debemos solucionar. Muchas veces <b>creemos</b> que debemos usar tal o cual herramienta, tal o cual lenguaje, tal o cual algoritmo, etc&#8230; y no es cierto (si usar un lenguaje es un requisito, entonces forma parte del problema <b>real</b>).</p>

<p>Así, céntrate primero en las propiedades del <i>&#8220;enunciado&#8221;</i> y juega con ellas mentalmente, la mejor estrategia (algoritmo) te vendrá a la cabeza mientras miras ensimismado por la ventana.</p>

<p>Más información | <a href="http://es.wikipedia.org/wiki/Teor%C3%ADa_de_la_complejidad_computacional">Teoría de la complejidad computacional</a>.</p>      ]]></description>
      </item>
                    <item>
      <title><![CDATA[Anonymous Grid Computing]]></title>
      <link>http://www.genbetadev.com/desarrollo-web/anonymous-grid-computing</link>
      <guid>http://www.genbetadev.com/desarrollo-web/anonymous-grid-computing</guid>
      <pubDate>Mon, 06 Aug 2012 03:30:00 +0000</pubDate>

      <author>josejuan</author>
      <description><![CDATA[
      <p><img alt="Grid computing" src="http://img.genbetadev.com/2012/07/grid_computing.jpg" class="centro_sinmarco" /></p>

<p>El <i>Grid computing</i> aprovecha un conjunto heterogéneos de recursos de forma conjunta. Así, una empresa podría utilizar, en segundo plano, todas las <span class="caps">CPU</span> de sus empleados para realizar cálculos en lugar de tener un centro de cálculo específico o podría usar el espacio libre en los discos para tener un servidor de datos distribuido y altamente tolerante a fallos. Proyectos como <span class="caps">BOINC</span> son ejemplos de computación distribuida heterogénea.</p>

<p>Sin embargo, para utilizar todos estos recursos, normalmente hace falta una planificación e infraestructura. En casos como <span class="caps">BOINC</span> es preciso darte de alta, instalar un programa, etc&#8230;</p>

	<p><p>Hoy, os propongo un juego (un katta si queréis), ¡aprovecharnos del poder de cómputo de todos los ordenadores de todos nuestros internautas!. El juego se llama <b>Anonymous Grid Computing</b>.</p><br />
<!--more--></p>

<h2>Introducción</h2>

<p>Como veremos, en unas pocas líneas de código podemos acceder a una ingente cantidad de potencia de cálculo. El esquema es válido en un entorno real pues el único requisito es tener una conexión a internet. Todo los ordenadores que una empresa internacional posee en todo el mundo, podrían ser usados fácilmente con el mecanismo que propongo. El enfoque <i>&#8220;anonymous&#8221;</i> que le he dado, es porque a los informáticos nos suele gustar este tipo de juegos (nosotros ser el gato que juega con el ratón), pero en la práctica, un uso <i>&#8220;no consentido&#8221;</i> tendría implicaciones morales, legales y técnicas que no pretendo analizar. Por eso lo he llamado <i>&#8220;juego&#8221;</i>.</p>

<h2>Computación distribuida</h2>

<p>No todos los problemas son adecuados para una computación distribuida, no es mi intención comentar el tema, puedes leer las referencias si quieres profundizar, pero básicamente, aquellos problemas que pueden ser divididos en pequeños problemas, resolverlos de forma independiente y luego juntarlos, son susceptibles de ser abordados mediante computación distribuida. Algunos ejemplos:</p>
<ul>
<li>Renderizar las imágenes de una película de animación 3D.</li>
<li>Simular un experimento muchas veces para contrastar posibilidades (eg. formación de galaxias).</li>
<li>Búsqueda exhaustiva de soluciones (eg. romper por fuerza bruta claves de datos crifrados).</li>
</ul>

<p>En nuestro caso, nos da igual lo que calculemos, lo interesante es que podremos lanzar en segundo plano (hipotéticamente sin que se de cuenta el internauta) los cálculos en todas las <span class="caps">CPU</span> de nuestros internautas. Sí, si un navegante tiene un ordenador con 8 cores ¡usaremos sus 8 cores!.</p>

<h2>Ejemplo de cálculo</h2>

<p>Como digo no es importante si no te queda muy claro este punto, solo voy a describir el problema de ejemplo de cálculo que vamos a solucionar. Podría ser cualquier otro, pero aquí, vamos a calcular <b>Pi</b>, además, de una manera muy <b>ineficiente</b> para hacer de forma sencilla, un uso masivo del procesador.</p>

<p>El método de Montecarlo es famoso por permitir, de forma fácil, encontrar soluciones aproximadas a problemas muy complejos. Solo explicaré nuestro problema, en las referencias encontrarás datos concretos sobre el método.</p>

<p>Si tenemos un cuadrado de 1&#215;1 metros e inscrito un círculo (de radio medio metro) al que lanzamos dardos de forma completamente aleatoria, los dardos cubrirán todo nuestro cuadrado, algunos habrán caído dentro del círculo y otro fuera. Una simple regla de tres, nos dirá el valor de Pi. De forma similar, si en lugar de todo un círculo, unicamente inscribimos en nuestro cuadrado un cuarto de círculo, obtendremos la cuarta parte de Pi.</p>

<p>Resumiendo, que si <b>T</b> es el número total de dardos lanzados y <b>H</b> (de hit) es el número de dardos que caen dentro del cuarto de circunferencia, entonces <b>Pi = 4 H / T</b>.</p>

<p>Si <b>X</b> e <b>Y</b> son las posiciones aleatorias dentro de nuestro cuadrado de 1&#215;1 metros, estará dentro del cuarto de circunferencia si es <b>X<sup>2</sup> + Y<sup>2</sup> &lt; 1</b>.</p>

<h2>Solución</h2>

<p>La solución consistirá en la parte del servidor que acumulará los datos generados por los ingenuos internautas (que están pensando en comprarse un ordenador más potente porque las páginas web van un poco lentas; risas). Luego mostraremos la página web que realiza el trabajo en paralelo.</p>

<h3>Servidor</h3>

<p>En un escenario real, la carga de datos debería ser transaccional, por simplificar usaré un sencillo archivo de texto que almacenará e irá sumando los valores <b>T</b> y <b>H</b> que nos envían los clientes. Además, devolverá al cliente el valor actual de Pi aproximado, ésto no hace falta, pero así nosotros vemos como evoluciona el cálculo.</p>

<p>Bueno, el código es un sencillo script en Perl que no requiere de explicación (para cualquier duda usa los comentarios):</p>

	<p><script src="https://gist.github.com/3142332.js"> </script></p>

<h3>Cliente</h3>

<p>Para conseguir la paralelización en el navegador, usaremos la <span class="caps">API</span> Web Workers que ya está disponible en Firefox, Safari, Chrome y como no en IE <b>no</b>.</p>

<p>Obviamente, si queremos que nuestro proceso sea transparente, ni pediremos al usuario que nos entregue hilos de ejecución ni le mostraremos la evolución del proceso, pero así nosotros podemos verlo. La página siguiente tampoco tiene mucha miga y tampoco requiere explicación (para cualquier duda usa los comentarios):</p>

	<p><script src="https://gist.github.com/3142522.js"> </script></p>

<p>El script que realiza el trabajo y del que se crean tantos hilos como sea preciso, también es sumamente sencillo, aquí está:</p>

	<p><script src="https://gist.github.com/3142648.js"> </script></p>

<h3>Ejemplo de uso</h3>

<p>Para verlo en acción puedes ir a la página <a href="http://computer-mind.com/agc/anonymousGridComputing.html">anonymousGridComputing.html</a> que he dejado funcionando. Allí, le puedes indicar cuantos hilos quieres dejar funcionando. En la imagen, puedes ver un ejemplo:</p>

	<p><img alt="Anonymous Grid Computing in action" src="http://img.genbetadev.com/2012/07/agc_running.png" class="centro_sinmarco" /></p>

<h2>Estimación de la capacidad</h2>

<p>La mejor forma de estimar la carga es conociendo el número de usuarios únicos que visitan nuestros sitios y el tiempo medio que permanecen en ellos. Por ejemplo, la página de <i>El Corte Inglés</i> tiene unas 270.000 visitas al día con una permanencia media de 5 minutos, eso hacen 1.350.000 minutos de computación por día. Es decir, el equivalente a 1.350.000 / 1440 = 937 equipos. Como centro de cálculo, no está nada mal. Lo más difícil sería estimar la capacidad media de cada equipo, pero podemos tener un factor aproximado incluso con las pocas líneas que hemos escrito, porque cada 5 minutos nos envían el número de eventos generados (en el valor <b>T</b>) así, el mismo valor <b>T</b> es un indicador de la potencia de cálculo de cada máquina en el momento que nos envía cada resultado. De ahí, a guardar una cookie para identificar cada equipo hay un paso.</p>

<h2>Conclusión</h2>

<p>De una forma muy sencilla, podemos utilizar toda una heterogénea infraestructura para realizar cálculos de algún tipo sin necesidad de realizar ninguna configuración ni preparación previa (nada más que un navegador funcionando). La naturaleza de JavaScript además permite realizar prototipos de forma rápida y cómoda. Las mejoras en los intérpretes también hacen que usar JavaScript no conlleve un rendimiento desastroso aunque en casos reales, la utilización de un applet con código Java optimizado puede ser recomendable (ver comparativa en las referencias).</p>

<p>En todo caso, ¡un ejercicio muy divertido!.</p>

<p>
Más información | <a href="http://en.wikipedia.org/wiki/Grid_computing">Grid computing</a>, <a href="http://boinc.berkeley.edu/"><span class="caps">BOINC</span></a>, <a href="http://es.wikipedia.org/wiki/Computaci%C3%B3n_distribuida">Computación distribuida</a>.<br />

<a href="http://es.wikipedia.org/wiki/M%C3%A9todo_de_Montecarlo">Método de Montecarlo</a>.<br />

Más información | <a href="http://www.w3.org/TR/workers/">W3C Web Workers</a>.<br />

Más información | <a href="http://shootout.alioth.debian.org/u32/benchmark.php?test=all&lang=v8&lang2=java">Comparativa rendimiento JavaScript frente a Java</a>.<br />

</p>      ]]></description>
      </item>
                    <item>
      <title><![CDATA[Impossible Programming ]]></title>
      <link>http://www.genbetadev.com/metodologias-de-programacion/impossible-programming</link>
      <guid>http://www.genbetadev.com/metodologias-de-programacion/impossible-programming</guid>
      <pubDate>Wed, 01 Aug 2012 05:44:45 +0000</pubDate>

      <author>josejuan</author>
      <description><![CDATA[
      <p><img alt="Requisitos de usuario imposibles" src="http://img.genbetadev.com/2012/07/complex_factory.gif" class="centro_sinmarco" /></p>

<p>El término <b>Impossible Programming</b> no existe (aunque la vivamos a diario), pero es el nombre que se me ha ocurrido para presentar un sencillo ejercicio sobre <b>Programación dirigida por eventos</b>.</p>

<p>Hay algunos problemas, que si bien en si mismos no son complicados, la gran cantidad de elementos involucrados hacen que resolverlos con éxito, sea una empresa arriesgada. Por ejemplo, todos los aquí presentes sabemos sumar pero&#8230; ¿estaríamos seguros del resultado tras sumar dos números con 230 dígitos?.</p>

<p>Al menos con la suma sabemos como tenemos que proceder pero, ¿y si además de haber muchos elementos involucrados, no estamos seguros de tener una estrategia óptima?, ¿y si resulta que tras deshacer el nudo gordiano y dedicar mucho tiempo a poner orden (por ejemplo con cuidadosas pruebas unitarias y <span class="caps">TDD</span>) la estrategia no es correcta?, ¿y si nos cambian los requisitos, la información es errónea o las condiciones varían con el tiempo?, ¿y si (muy a nuestro pesar) efectivamente el problema supera nuestros conocimientos?, &#8230;</p>

	<p><p>Bueno, bueno, una vez encomendados a San Isidoro de Sevilla (patrono oficial de los programadores), ¿hay algo más que podamos hacer?. Claro, existen un buen número de herramientas útiles que los programadores podemos usar cuando (seamos sinceros) no tenemos ni idea de por donde tirar. Hoy, te propongo jugar con la <b>Programación dirigida por eventos</b>, ¿estás preparado?.</p><br />
<!--more--></p>

<h2>Brevísima introducción a la Programación dirigida por eventos</h2>

<p>Cuando escribimos un programa, habitualmente esperamos unos datos de entrada pactados de antemano (eg. una lista de nombres, los datos de registro de la centralita telefónica, etc&#8230;) y por eso podemos escribir un procedimiento que procese los datos de forma prefijada. Insisto, establecemos un procedimiento concreto de antemano, que describe como deben procesarse esos datos.</p>

<p>Sin embargo, si los datos nos llegan en cualquier orden y forma (ahora un nombre, luego un teléfono, después algo que hay que procesar, etc&#8230;), fijar un único procedimiento concreto será, normalmente, más complicado que si nos centramos en procesar individualmente cada bloque de datos por separado. El siguiente ejemplo puede aclarar la idea, en lugar de tener todo mezclado:</p>

	<p><script src="https://gist.github.com/3209166.js"> </script></p>

<p>Puede quedar más claro si separamos las diferentes formas en las que nos llegan los datos:</p>

	<p><script src="https://gist.github.com/3209183.js"> </script></p>

<p>En general, la programación dirigida por eventos está orientada a situaciones en las que la naturaleza del problema es, precisamente, un conjunto de eventos pero, <b>podemos aprovechar la forma de organizar los eventos para simplificar un problema</b>, veamos como.</p>

<h2>El problema: la situación</h2>

<p>Hoy tenemos un problema <i>&#8220;empresarial&#8221;</i> (no <i>&#8220;matemático&#8221;</i>), por eso deberás hacer un pequeño esfuerzo para no dormirte entre requisito y requisito.</p>

<p>Supongamos que una empresa dispone de diversas factorías para fabricar productos de repostería (toman las materias primas y producen ya embalados diversos tipos de dulces). Como son factorías muy viejas y que llevan funcionando mucho tiempo, se han convertido en auténticas máquinas de Rube Goldberg, como la de la imagen que encabeza el post.</p>

<p>La cuestión es que si bien su mecánico es bastante bueno y sabe hacer funcionar todos los sistemas, no hay nadie capaz de entender cómo y para qué sirven todos los mecanismos que componen las factorías, ni siquiera el mecánico tiene una idea general de cómo funciona todo, él simplemente va arreglando las cosas que dejan de funcionar.</p>

<p>Cada día se producen averías con mayor frecuencia y el Caos empieza a ser lo habitual en el funcionamiento de las factorías. Además, debido a que no es evidente el flujo de procesos, una máquina puede dejar de funcionar debido a que a mucha distancia, otra máquina ha dejado de tener materia prima y a la empresa le resulta muy difícil proveer un suministro constante de materia prima para maximizar la producción. De pronto se quedan sin harina, y deben salir corriendo a buscar más para que todo vuelva a funcionar.</p>

<p>En definitiva, he descrito el tipo de empresa en el que todos querríamos trabajar.</p>

<p>Así pues, en la empresa piensan que como los ordenadores ordenan cosas, a ellos igual les viene bien tener uno para que ponga orden en sus factorías (si este argumento te parece descabellado, debes saber que es <b>&#8220;El argumento&#8221;</b> del que es imposible sacar a los clientes; piensan que <b>no son</b> ellos los que deben poner orden ¡para eso están los ordenadores y los informáticos!) y te llaman a ti, que de fabricar pasteles sabes lo mismo que un caracol de tocar el Ukelele.</p>

<h2>El problema: lo que hay que hacer</h2>

<p>Obviamente todos sabemos que la mejor solución es construir nuevas y flamantes factorías que dispongan de la última tecnología pero claro, la empresa dice que <i>&#8220;ya lo habíamos pensado pero como nuestro mecánico es muy bueno (que lo arregla todo), a ver si se podía poner orden en los recursos que debemos tener disponibles en cada momento&#8221;</i>.</p>

<p>Así, los requisitos serían más o menos los siguientes:</p>

<ul>
<li>Se pide diseñar un sistema que indique la cantidad de recursos necesarios para maximizar la producción de pasteles. Obviamente debe poder decirlo con la antelación suficiente (días o semanas).</li>
<li>El sistema debe servir para diferentes factorías, cada cual con sus particularidades.</li>
<li>El sistema debe ser capaz de adaptarse a situaciones inesperadas como caída, eliminación, adición y/o cambios en el comportamiento de los subsistemas de cada factoría.</li>
</ul>

<h2>Soluciones &#8220;formales&#8221;</h2>

<p>Todo el mundo sabe que en un escenario como este las redes de Petri son una herramienta que nos permite analizar, comprender y optimizar el flujo de procesos, es probable que exista algún software que nos permita modelar el Caos de estas factorías pero, ¿y si no sabemos qué son esas redes?, ¿y si existen procesos estocásticos que dificultan el modelo?, ¿y si no sabemos usar ese software? (deberíamos aprender).</p>

<p>Todo el mundo sabe también, que la complejidad de los procesos puede ser rodeada usando la estadística (para obtener datos) y el cálculo de probabilidades (para predecir comportamientos). Podríamos realizar un cuidadoso estudio estadístico de, únicamente, el flujo de materias prima de entrada y la producción de salida, <b>ignorando toda la complejidad interna de las factorías</b>. Obtendríamos así (probablemente) un modelo ajustado sobre la probabilidad de que nos quedemos sin materia prima que nos permitiría minimizar la probabilidad de parada. Sin embargo, aplicar esta solución requiere tomar muchos datos y, además, con el tiempo habría que ir ajustando el modelo.</p>

<h2>Solución &#8220;a la torera&#8221;</h2>

<p>Como Joselito con su capote, vamos nosotros capear el problema, sino con inteligencia, esperemos que con gracia. Porque en realidad lo que vamos a hacer es una mezcla &#8220;informal&#8221; de las dos anteriores. Pues en lugar de modelar la red de Petri, únicamente vamos a simular nuestras factorías, y en lugar de minimizar la probabilidad de parada, sólo vamos a obtener los datos del funcionamiento de las partes (estadística).</p>

<p>Es decir, vamos a construir un simulador de factorías para poder predecir que recursos se van a agotar primero y en tan sólo 99 líneas de código. ¿Empezamos?.</p>

<h3>Lo que vamos a hacer</h3>

<p>La solución que proponemos a la empresa es, ya que no va a cambiar las factorías y que el funcionamiento va a seguir cambiando de un día para otro, la siguiente:</p>

<ul>
<li>Nuestro sistema se podrá reconfigurar instantáneamente, bastará con cambiar el archivo de entrada. Así, si un nuevo subsistema cambia su comportamiento, es añadido o eliminado, ajustamos el archivo de configuración y listo.</li>
<li>Nuestro sistema podrá funcionar con independencia entre las diferentes factorías, un cambio en unas, no afectan en nada a las otras.</li>
<li>Nuestro sistema responderá instantáneamente a las entradas de material, así, podremos pedir un report que instantáneamente nos indique el estado que tendrán todos los subsistemas (por ejemplo, para poder ver que la máquina de amasado se bloqueará porque necesitará más harina).</li>
</ul>

<h3>El generador de simuladores</h3>

<p>La primera estrategia consiste en abstraer el problema. En lugar de intentar modelar directamente las factorías, vamos a crear un generador de simuladores. Con ello conseguimos por un lado no tener que pensar en el tremendo lío que hay en cada factoría y por otro, que la configuración y reconfiguración pueda hacerse muy fácilmente (desde el archivo de configuración).</p>

<h3>El simulador</h3>

<p>La segunda estrategia consiste en identificar las partes de la factoría que sean posibles, si una parte es completamente incomprensible no pasa nada, la trataremos como un todo, pero el identificar las más partes posibles nos permitirá actuar fácilmente allí donde se produzca el problema.</p>

<p>La forma más fácil de ver como funciona el simulador es ver su archivo de configuración:</p>

	<p><script src="https://gist.github.com/3215424.js"> </script></p>

<p>Vemos que hemos identificado las diferentes materias primas con el número de elementos que nos llegan en cada envío (eg. 30 manzanas por envío) y los procesos internos (eg. para hornear hacen falta cinco montajes ya listos y una unidad de energía). Si mañana se añade un proceso, sólo es añadir una línea, si desaparece, eliminarla, si la máquina de recortes empieza a fallar y desperdicia una de cada seis manzanas sólo es ajustar el número de manzanas necesarias (eg. en lugar de 15 poner 19), etc&#8230;</p>

<h3>El control</h3>

<p>Para controlar el simulador, además del archivo de configuración, querremos disponer de algunos comandos como levantar todo el sistema, detener el sistema, simular la caída de un subsistema, añadir recursos de algún tipo, de otro tipo, pedir un estatus a todos los sistemas, etc&#8230;</p>

<h3>La implementación</h3>

<p>Para implementar nuestro sistema vamos a utilizar <b>Inter-process communication</b> que aunque suena muy técnico, es tan sencillo como que unos procesos se envían <i>&#8220;correos electrónicos&#8221;</i> (es un símil) unos a otros. Como en mi ejemplo los procesos son sencillos scripts (en <b>bash</b>) utilizaré un sencillo comando <b>msgtool</b> que puedes encontrar en las referencias.</p>

<p>Bien, el código que nos permite generar los simuladores a partir del archivo de configuración es:</p>

	<p><script src="https://gist.github.com/3215499.js"> </script></p>

<p>Que no hace otra cosa, más que generar automáticamente una serie de scripts que mandan y reciben mensajes simulando las relaciones existentes en las factorías. Después veremos algún script autogenerado por este código en <b>Perl</b>.</p>

<h3>El despliegue</h3>

<p>Para poner en marcha los simuladores, crearemos una carpeta para cada factoría con su archivo de configuración, por comodidad podemos poner nuestro script generador de simuladores y la utilidad <b>msgtool</b>. El proceso de generación es trivial, pongo un ejemplo completo con el listado de archivos (que es lo que ocupa espacio):</p>

	<p><script src="https://gist.github.com/3215540.js"> </script></p>

<p>Para levantar todos los subsistemas de nuestro simulador, basta hacer:</p>

	<p><script src="https://gist.github.com/3215601.js"> </script></p>

<p>Obviamente si solicitamos a los subsistemas que emitan su estado, estarán todos a cero:</p>

	<p><script src="https://gist.github.com/3215615.js"> </script></p>

<p>Por comodidad, podemos lanzar el siguiente comando en otro terminal para tener actualizado en tiempo real el estado del simulador:</p>

	<p><script src="https://gist.github.com/3215633.js"> </script></p>

<p>El simulador también nos ha generado automáticamente scripts para cada uno de los recursos que tenemos disponibles, así, podríamos lanzar por ejemplo la siguiente carga de recursos (eg. según llegan a los almacenes):</p>

	<p><script src="https://gist.github.com/3215736.js"> </script></p>

<p>Vemos que con las manzanas y operarios que han entrado se han producido seis recortes y hay disponibles las guindas, pero no puede continuar el proceso, además, operarios tenemos tres parados y no hay más manzanas.</p>

<p>Si continuamos añadiendo recursos, vemos la evolución:</p>

	<p><script src="https://gist.github.com/3215776.js"> </script></p>

<p>Así, podemos predecir fácilmente las necesidades de recursos que vamos a tener a corto plazo y pedirlas antes de que se agoten, sin necesidad de tener almacenadas grandes cantidades.</p>

<h3>Los scripts autogenerados</h3>

<p>Cuando hemos generado el simulador a partir del archivo de configuración, se nos ha listado los identificadores asociados a cada variable (manzana, horneado, &#8230;) algunos no se usarán para nada (eg. manzana) pero otros serán los identificadores de las colas de mensajes.</p>

<p>El script que levanta todo el sistema contiene lo siguiente:</p>

	<p><script src="https://gist.github.com/3224425.js"> </script></p>

<p>Que como ves, únicamente pone en segundo plano cada uno de los subsistemas que hemos definido.</p>

<p>Cada uno de los subsistemas intermedios (masa, montaje, &#8230;) son muy parecidos, contabilizan los recursos que tienen disponibles y cuando son suficientes para producir el resultado, lo notifican al subsistema siguiente. Por ejemplo, el subsistema del montaje es:</p>

	<p><script src="https://gist.github.com/3224445.js"> </script></p>

<p>Que como ves, únicamente consiste en esperar a que llegue un mensaje a su número de cola asociada y en caso de que sea alguna materia prima, incrementa la cantidad disponible. Si es un comando de sistema (poweroff o report) efectúa lo propio y en cualquier caso, mira a ver si puede producir el resultado con el material disponible, en cuyo caso, lo notifica a la cola de destino.</p>

<p>El subsistema final es un caso especial, pues cuando se termina una tarta, no hay nada que hacer, así, únicamente se notifica, pero es esencialmente igual que los demás.</p>

<p>El último script que genera el generador de simuladores es el <b>sendToAllSystems</b>, que sirve para notificar a todos los subsistemas alguna cosa como que se apaguen o que reporten su estado:</p>

	<p><script src="https://gist.github.com/3224462.js"> </script></p>

<h2>Conclusión</h2>

<p>Por un lado, vemos que existen estrategias que nos permiten abstraer la dificultad intrínseca de los problemas. Obviamente no las podemos aplicar siempre, sólo en aquellos problemas que las admitan (como en este caso). Conocerlas y practicar con ellas nos permite enfrentarnos a situaciones que a priori pueden parecer intratables o que superan nuestros conocimientos.</p>

<p>Por otro lado, vemos que no hace falta dedicar mucho tiempo ni recursos para modelar adecuadamente un problema a priori complejo. Subestimar o ignorar herramientas como el shell hará que perdamos la oportunidad de utilizarlas en innumerables ocasiones.</p>

<h2>Nota sobre la implementación</h2>

<p>La <b>Programación dirigida por eventos</b> no impone mayor restricción que el que se pueda <i>&#8220;disparar&#8221;</i> un cómputo cuando se producen ciertas condiciones, es por ello que existen otros paradigmas en los que puede hacerse una traslación casi directa cuando la usamos en la forma presentada en el post. Por ejemplo, es trivial definir en <b><span class="caps">POO</span></b> una clase denominada <b>Subsistema</b> que disponga de algún método <b>recibirRecurso</b> y la referencia al subsistema receptor de su producto. También es trivial crear un sencillo programa que únicamente mantenga una lista completa de todas las variables, si introducen una materia prima, incrementa la variable y revisa hasta que no se pueda todas las transformaciones en subproductos.</p>

<p>Que la implementación utilizada sea distribuida (cada subsistema puede correr en máquinas distantes) no es relevante (aunque interesante) pero enfatiza el sentido de <i>&#8220;disparo de evento&#8221;</i> que se produce entre los subsistemas y muestra que es muy sencillo utilizar colas de mensajes (semáforos, mutex, &#8230;) en un shell.</p>

<p>Más información | <a href="http://es.wikipedia.org/wiki/Programaci%C3%B3n_dirigida_por_eventos">Programación dirigida por eventos</a>.<br />

Más información | <a href="http://en.wikipedia.org/wiki/Inter-process_communication">Inter-process communication</a>, <a href="http://tldp.org/LDP/lpg/node37.html#SECTION00742600000000000000">msgtool</a>.<br />

Más información | <a href="http://en.wikipedia.org/wiki/Isidore_of_Seville">Isidore of Seville</a>, <a href="http://en.wikipedia.org/wiki/Rube_Goldberg_device">Rube Goldberg device (o incredible machine)</a>, <a href="http://es.wikipedia.org/wiki/Redes_de_Petri">Redes de Petri</a>.<br />

</p>      ]]></description>
      </item>
                    <item>
      <title><![CDATA[Optimizas u optimizas]]></title>
      <link>http://www.genbetadev.com/metodologias-de-programacion/optimizas-u-optimizas</link>
      <guid>http://www.genbetadev.com/metodologias-de-programacion/optimizas-u-optimizas</guid>
      <pubDate>Fri, 27 Jul 2012 04:17:13 +0000</pubDate>

      <author>josejuan</author>
      <description><![CDATA[
      <p><img alt="Fast or slow" src="http://img.genbetadev.com/2012/07/fastVSslow.jpg" class="centro_sinmarco" /></p>

<p>Lo deseable cuando escribimos cualquier algoritmo, es que este sea tan rápido como sea posible. Desafortunadamente, no siempre es lo más indicado, bien porque no se disponga de tiempo para codificarlo o bien porque supondría una complejidad adicional en el despliegue (ej. precálculos, caché, librerías) y/o en el propio código por cuestiones de legibilidad y mantenimiento.</p>

<p>Sin embargo, no cabe duda de que no sólo en aplicaciones con marcado carácter computacional (financieras, simulación física, graficación, juegos, etc&#8230;) es deseable la máxima optimización. Páginas web dinámicas, un fluido interface de usuario (Tablet, PC o navegador), procesos en segundo plano en nuestro servidor y en otras tantas situaciones podemos obtener múltiples beneficios (mejor experiencia de usuario, menor consumo de cloud contratado, no necesitar hardware adicional, etc&#8230;) si procuramos escribir un código eficiente.</p>

	<p><p>Así, te invito a practicar conmigo en la optimización del código con un sencillo ejemplo. ¿Te animas?.</p><br />

<!--more--></p>

<h2>Breve introducción</h2>

<p>Existen muchos tipos de optimización en los algoritmos. Éstos pueden ser de índoles muy diferentes. La optimización puede estar orientada al hardware como por ejemplo, el instalar un segundo disco para minimizar los bloqueos entre procesos que escriben y leen. Otras optimizaciones se centran en el lenguaje concreto sobre el que trabajamos, cambiando una <i>&#8220;palabra&#8221;</i> por otra, podemos obtener un mejor desempeño (eg. en Haskell, cambiando <b>concat $ map</b> por <b>concatMap</b> o en otros haciendo el cambio <b>String</b> por <b>StringBuilder</b>). Otras consisten en un cambio en la estrategia basada en una combinación de las anteriores (una típica es aquella que consiste en cambiar un tipo genérico <b>Integer</b> por uno concreto y más eficiente a nivel de compilador y hardware como <b>Int32</b>).</p>

<p>Pero sin duda, la mejor forma de optimizar un algoritmo es mejorándolo, reemplazándolo por otro en el que el número de operaciones requeridas para hacer el trabajo, sea menor que en el anterior.</p>

<p>Sin embargo, ésto último no siempre es cierto, <i>&#8220;¿cómo?&#8221;</i> te dirás, <i>&#8220;¿usar un algoritmo más eficiente es peor que usar otro menos eficiente?&#8221;</i>. Pues sí, la cuestión es que un algoritmo no siempre se comporta igual. Hay algoritmos que (es solo un ejemplo) funcionan igual de bien (o mal) con independencia de que deban procesar uno o miles de datos, otros sin embargo, funcionan bien para pocos datos pero con muchos se vuelven penosos, etc&#8230;</p>

<p>La eficiencia de los algoritmos es la materia de estudio de la <b>Complejidad computacional</b>, sin embargo, como es verano y hace calor, comentaré implícitamente algunos aspectos de dicha teoría revisando cuatro soluciones diferentes a un mismo problema.</p>

<h2>El problema</h2>

<p>Como es habitual, lo más fácil es plantear un problema <i>&#8220;matemático&#8221;</i>, esto es así porque de forma sencilla y concisa, se puede poner un problema que condense toda la dificultad que queremos mostrar sin tener que poner un ejemplo <i>&#8220;empresarial&#8221;</i> que sería largo y aburrido (recuerda el ejemplo de mi post de <a href="http://www.genbetadev.com/metodologias-de-programacion/microcodigo-en-mi-codigo">Microcódigo en mi código</a>).</p>

<p>De todos modos, si se te ocurren problemas <i>&#8220;cotidianos&#8221;</i> que no tengan un enunciado demasiado largo, puedes postearlo en los comentarios o en <a href="http://www.solveet.com/">Solveet</a>.</p>

<p>El problema propuesto consiste en calcular todos los <b>factoriones</b> que existen, por simplificar un poco, vamos a suponer que nos indican que no existen factoriones mayores que un <b>N</b> dado. Bien, ¿qué es un <b>factorión</b>?:</p>

	<p><p>Un factorión es un número natural (1, 2, 3, &#8230;) tal que, si tomamos todos sus dígitos decimales y sumamos sus factoriales, volvemos a obtener ese mismo número.</b></p>

<p>Por ejemplo, el número 1024 tiene por dígitos decimales a {1, 0, 2, 4}, los factoriales (recuerda que el factorial de <b>n</b> se denota <b>n!</b> y es igual al producto de los <b>n</b> primeros números naturales, es decir, <b>n! = 1 &#215; 2 x &#8230; x n</b>) de esos dígitos son {1, 1, 2, 24} cuya suma total hace 28, y como no nos ha dado 1024, resulta que 1024 <b>no es</b> un factorión.</p>

<p>Por otro lado, el número 145 tiene por dígitos {1, 4, 5} cuyos factoriales son {1, 24, 120} y su suma es precisamente 145, luego 145 <b>sí es</b> un factorión.</p>

<h3>Solución ingenua</h3>

<p>Se suele llamar <i>&#8220;solución ingenua&#8221;</i> a la solución evidente, la que se escribe sin pensar. Es un término común en ciencias de la computación, no pienses que lo he puesto yo. Dicha solución, consiste en usar la potencia del ordenador para que revise, uno a uno, todos los números, aplicando directamente la definición <b>sin pensar</b>, ¡a veces es la mejor solución posible!.</p>

<p>Si usamos Python, una forma de escribir directamente una solución sería así:</p>

	<p><script src="https://gist.github.com/3184595.js"> </script></p>

<h3>¿Cómo se optimiza?</h3>

<p>La forma de optimizar cualquier algoritmo, consiste en <b>conocer y entender sus propiedades y las del problema a resolver</b>, <i>&#8220;¿propiedades?&#8221;</i>, sí, sólo vamos a poder actuar sobre aquellas propiedades que conozcamos, por ejemplo, viendo el algoritmo en Python, se puede ver que para cada <b>i</b> estamos calculando una y otra vez los factoriales de los mismos dígitos (sólo hay 10 dígitos decimales, del 0 al 9), aunque sea una propiedad evidente, es una propiedad bien conocida que el factorial de 9 es siempre 362.880, si siempre es 362.880, nos podemos ahorrar las 8 multiplicaciones que requiere obtenerlo si lo dejamos precalculado en una matriz. Es decir, dejar en una matriz los 10 factoriales de los 10 dígitos posibles.</p>

<p>La propiedad de que el factorial de un número <b>n</b> no cambia es conocida e interesante. Una propiedad de la función <b>rand()</b> es justo la contraria, el valor devuelto puede tomar (o no) un nuevo valor diferente cada vez. <b>Si no conocemos las propiedades de las cosas, no podremos controlarlas eficientemente.</b></p>

<h3>Optimizar el código</h3>

<p>Como decía en la introducción, una forma de optimizar la ejecución, es mejorando el código, <b>aunque el algoritmo sea el mismo</b> (técnicamente el algoritmo será diferente, pero el proceso teórico, el algoritmo teórico seguirá siendo el mismo). En nuestro ejemplo, una forma evidente es precalcular los factoriales de cada dígito, pero hay otra cosa que se puede hacer que creo te va a gustar.</p>

<p>Una <b>propiedad</b> que tiene la definición de factorión es que, si tengo calculada la suma de los factoriales de los dígitos del número <b>n</b>, para calcular la suma de los factoriales de los dígitos del número <b>n + 1</b> sólo tengo que sumar y restar aquellos dígitos que sean diferentes ¡a que es genial! (esta propiedad ya no es tan evidente ¿eh?). Probablemente lo veas mejor con un ejemplo (es sencillo, ya verás):</p>

<p>Para n = 10321041, tenemos que hacer 1 + 1 + 1 * 2 * 3 + 1 * 2 + 1 + 1 + 1 * 2 * 3 * 4 + 1 = 37.</p><br />

<p>Para n = 10321042, tenemos que hacer 1 + 1 + 1 * 2 * 3 + 1 * 2 + 1 + 1 + 1 * 2 * 3 * 4 + 2 = 38.</p><br />

<p>¿En que se diferencian?, ¡únicamente en el dígito que cambia!, es decir, si tenemos calculada la definición para n = 10321041, para saber cuanto vale la de n = 10321042, únicamente tenemos que restar el factorial del dígito que sale (1) y sumar el factorial del dígito que entra (1 * 2 = 2).</p>

<p><i>&#8220;¿Y esta propiedad es interesante?&#8221;</i>, mucho, porque significa que en 9 de cada 10 números de los millones que tengamos que revisar sólo tendremos que hacer una resta y una suma ¿no es genial un ahorro del 90%?. Nota: cuando el primer dígito pase del 0 al 1, del 1 al 2, &#8230;, del 8 al 9, podemos aplicar la propiedad anterior, cuando del 9 pase al 0 no, porque pasará a las centenas, pero en éstas, sólo serán 2 restas y 2 sumas y no será ¡hasta los miles! que deberemos calcular 3 restas y 3 sumas, etc&#8230; desde luego, un ahorro impresionante.</p>

<p>En la práctica, el código siguiente lo único que hace es contar de 0 a N, pero en lugar de usar el operador suma (n = n + 1), lo hace como en el cole, mantiene un array con dígitos que inicialmente valen 0 y va sumando <i>&#8220;llevando una&#8221;</i>. No detallaré el algoritmo por no aburrir demasiado, pero puedes usar los comentarios si quieres que aclare algo.</p>

	<p><script src="https://gist.github.com/3184792.js"> </script></p>

<p>Aún se puede optimizar más el código, por ejemplo, una <b>propiedad</b> interesante del procedimiento que hemos establecido, es que siempre se resta y suman los mismos factoriales, por ejemplo, del dígito 0 siempre se pasa al 1, del dígito 1 siempre se pasa al 2, etc&#8230; en lugar de restar y sumar ¿porque no precalculamos esa resta y suma y así sólo hacemos una suma? ¡acabamos de reducir a la mitad el número de sumas (y restas)!.</p>

<p>No obstante, no merece la pena ahondar más aquí, ahora veremos una forma mejor.</p>

<h3>Optimizar reescribiendo el algoritmo</h3>

<p>Entre nuestro primer algoritmo ingenuo y el segundo, realmente no hay ninguna diferencia, son el mismo algoritmo, lo único que el segundo corre muchísimo más rápido. Digamos que tenemos que seguir yendo de Madrid a Barcelona, pero en lugar de a 50 Km/h lo hacemos a 1540 Km/h. Lo que vamos a hacer ahora es ¡un agujero de gusano para atravesar el espacio tiempo y teletransportarnos de Madrid a Barcelona! <i>&#8220;¿cómo?&#8221;</i>, <b>conociendo las propiedades</b> de los factoriones y aprovechándonos de ellas por supuesto.</p>

<p>Si volvemos a mirar como se calcula la definición que deben cumplir los factoriones:</p>

<p>Para n = 10321041, tenemos que hacer 1 + 1 + 1 * 2 * 3 + 1 * 2 + 1 + 1 + 1 * 2 * 3 * 4 + 1 = 37.</p>

<p>Nos fijamos que la suma tiene la <b>propiedad conmutativa</b>, ¿porqué es importante saber eso?, porque en cualquier objeto que cumpla dicha propiedad, podemos cambiar el orden de los elementos sin que afecte al resultado final, es decir, el número anterior, lo podemos reescribir como: 00111234. Es decir, <b>todos</b> los números que pueden escribirse con los mismos dígitos podemos calcularlos una única vez.</p>

<p>Si por ejemplo nos dicen que busquemos factoriones de 1 a 10.000.000, con los algoritmos anteriores tendríamos que revisar todos y cada uno de los 10 millones de números, sin embargo, si usamos la nueva propiedad únicamente tendremos que revisar 11.440 ¡hemos reducido el problema un 99,8856%! (ojo, antes reducíamos las operaciones en un 90% pero sólo de cada uno de los millones de números a revisar, es decir, teníamos que seguir revisando millones de números, ¡pero es que ahora hemos reducido <b>el problema</b> a sólo revisar unos pocos números!).</p>

<p><i>&#8220;¿Y como se usa esta propiedad?&#8221;</i>, bueno, sólo tenemos que generar las combinaciones con repetición de los dígitos involucrados. Tampoco explicaré el código siguiente, cualquier aclaración por favor, siéntete libre de indicarla en los comentarios:</p>

	<p><script src="https://gist.github.com/3184961.js"> </script></p>

<p>Éste código sin embargo, no es más rápido que el anterior en C para valores de N inferiores a unos tres millones, a partir de ahí, este último código en Haskell deja en pañales al de C.</p>

<h3>Optimizando a lo Agente Smith</h3>

<p><i>&#8220;¿Aún podemos optimizar más?&#8221;</i>. ¡Claro!, igual que el Agente Smith en la pelicula Matrix, no sólo vamos a teletransportarnos instantáneamente de Madrid a Barcelona, además, ¡nos vamos a clonar para ser muchos simultáneamente!.</p>

<p>En general, no siempre podemos paralelizar nuestros algoritmos pero, una propiedad muy interesante de nuestro último algoritmo, es que el cálculo de las combinaciones con repetición se hace recursivamente y de forma <b>independiente</b> (en los algoritmos de Python y C, las variables y matrices eran compartidas para cada número a revisar), es decir, lo podemos paralelizar muy fácilmente añadiendo un simple <b>parMap</b>:</p>

	<p><script src="https://gist.github.com/3184973.js"> </script></p>

<p>De todos modos, para números tan pequeños (decenas de millones) no merece la pena, porque hemos optimizado tanto y tan bien, que incluso en mi humilde Atom D510 sólo le lleva unos pocos milisegundos.</p>

<h3>¿Se puede mejorar al Agente Smith</h3>

<p>Pues sí, existen <b>propiedades</b> adicionales que no hemos explotado como por ejemplo, que la suma parcial de la definición debe ser menor o igual que el número que revisamos (algo evidente, claro). Es decir, si estamos verificando el número 999.999, vemos que cuando llevamos sumados los factoriales de tres dígitos ya nos da 1.088.640, por lo que no hace falta que sigamos sumando para saber que <b>no es</b> un factorión.</p>

<p>¿Sabías que el estudio de las propiedades (y relaciones) de las cosas es el objetivo de las matemáticas?.</p>

<h2>Curiosidades sobre los algoritmos escritos</h2>

<ul>
<li>El código Python tiene un coste O(n) y es lento y aburrido.</li>
<li>El código C tiene un coste O(n) y es muy rápido ¡sólo unos 10 ciclos de reloj para cada número!.</li>
<li>El código Haskell sin paralelizar tiene un coste O(log n) y aunque no es rápido en ejecución, efectúa muchísimas menos operaciones que los anteriores.</li>
<li>El código Haskell paralelizado, tiene un coste O((log n) / c) donde &#8216;c&#8217; es el número de hilos que le dejamos para ejecutar; si tenemos un <span class="caps"><span class="caps">AMD</span></span> Phenom II X6 con seis cores, tardará seis veces menos que el Haskell anterior.</li>
</ul>

<h2>Conclusión</h2>

<p>Como programadores debemos ser capaces de escribir un <b>código eficiente</b>, utilizar el algoritmo ingenuo es cómodo y tentador, pero hay muchas situaciones en las que un programador bien entrenado (no acomodado en que simplemente, funcione) puede fácilmente optimizar un código y obtener un producto mejor. Ni las mejoras son mínimas si las hemos ido realizando desde el principio del desarrollo, ni el código se complica innecesariamente si las hacemos de forma inteligente. A veces únicamente será cambiar la forma de aplicar una instrucción, otras declarar una pequeña matriz para precalcular algún resultado e incluso otras, complicaremos conscientemente el algoritmo asumiendo el coste de mantenimiento.</p>

<p>Si no hay una razón para no escribir un buen código, ¿porqué no hacerlo?.</p>

	<p><p>En Genbeta Dev | <a href="http://www.genbetadev.com/desarrolladores/soolvet-mejora-como-desarrollador-made-in-spain">Solveet</a>.<br />
</p>

	<p>Más información | <a href="http://es.wikipedia.org/wiki/Combinaciones_con_repetici%C3%B3n">Combinaciones con repetición</a>.<br />
</p>

	<p>Más información | <a href="http://www.solveet.com/exercises/Factorion/106">Problema factorión</a>.<br />
</p>

	<p>Más información | <a href="http://es.wikipedia.org/wiki/Complejidad_computacional">Complejidad computacional</a>.<br />
</p>

	<p></p></p>      ]]></description>
      </item>
        	  <atom:link href="http://www.genbetadev.com/autor/josejuan/rss2.xml" rel="self" type="application/rss+xml" />
	</channel>

</rss>


