{"id":107,"date":"2020-03-17T11:48:51","date_gmt":"2020-03-17T10:48:51","guid":{"rendered":"https:\/\/intsight.com\/?p=107"},"modified":"2021-01-21T18:55:03","modified_gmt":"2021-01-21T17:55:03","slug":"la-transformacion-de-box-muller","status":"publish","type":"post","link":"https:\/\/intsight.com\/index.php\/2020\/03\/17\/la-transformacion-de-box-muller\/","title":{"rendered":"La transformaci\u00f3n de Box-Muller"},"content":{"rendered":"<p><span style=\"font-variant: small-caps;\">En casi todos<\/span> los fen\u00f3menos aleatorios, ya pertenezcan a la f\u00edsica, la gen\u00e9tica o las finanzas, la distribuci\u00f3n normal, o de Gauss-Laplace (la de la famosa curva de la campana) juega un papel importante. Sin embargo, .NET no ofrece de serie una clase, o un m\u00e9todo, que genere valores aleatorios pertenecientes a esta distribuci\u00f3n. Podemos utilizar una librer\u00eda de terceros, por supuesto. Pero no est\u00e1 de m\u00e1s conocer alternativas, sobre todo para aplicaciones peque\u00f1as o pruebas de concepto, en los que no merezca la pena usar algo m\u00e1s completo.<\/p>\n<p>El problema a resolver es: teniendo como punto de partida un generador de n\u00fameros aleatorios que utilice una distribuci\u00f3n uniforme, como la clase <code>Random<\/code>, \u00bfc\u00f3mo podemos transformarlos para obtener la distribuci\u00f3n normal? Lo primero es ponernos de acuerdo sobre los par\u00e1metros de la distribuci\u00f3n normal que generaremos. Hay dos par\u00e1metros: la media y la varianza. Pero podemos ce\u00f1irnos a una distribuci\u00f3n con media igual a cero y varianza igual a uno. Es f\u00e1cil cambiar de par\u00e1metros desplazando y estirando los n\u00fameros que vamos a generar.<\/p>\n<p>\u00bfCu\u00e1l es el algoritmo adecuado para transformar una distribuci\u00f3n uniforme en una normal? La respuesta es el llamado <a href=\"https:\/\/heliosphan.org\/zigguratalgorithm\/zigguratalgorithm.html\" target=\"_blank\" rel=\"noopener noreferrer\">algoritmo del zigurat<\/a>, que realiza un muestreo por regiones. El enlace anterior incluye c\u00f3digo en C#. Pero existe un algoritmo mucho m\u00e1s sencillo, que se conoce como la <a href=\"https:\/\/mathworld.wolfram.com\/Box-MullerTransformation.html\" target=\"_blank\" rel=\"noopener noreferrer\">transformaci\u00f3n de Box-Muller<\/a>. Esta transformaci\u00f3n convierte dos valores aleatorios $u$ y $v$, pertenecientes a una distribuci\u00f3n uniforme sobre el intervalo [0, 1], en otros dos valores aleatorios, a los que llamaremos $x$ e $y$, pertenecientes a una normal con media cero y varianza uno. Las f\u00f3rmulas necesarias son estas:<br \/>\n$$<br \/>\n\\eqalign{x&amp;=\\sqrt{-2 \\ln u} \\cos 2\\pi v\\cr<br \/>\ny&amp;=\\sqrt{-2 \\ln u} \\sin 2\\pi v}<br \/>\n$$Existen m\u00e9todos alternativos, como el de Marsaglia, que evitan las funciones trigonom\u00e9tricas, pero al precio de descartar algunas muestras. Antes de recomendar el m\u00e9todo original de Box-Muller, he hecho la prueba en un Core i7-4770, y no he encontrado diferencias significativas entre ambos m\u00e9todos:<\/p>\n<ol>\n<li>Probablemente, los procesadores m\u00e1s o menos modernos (el m\u00edo es un Intel Core de cuarta generaci\u00f3n, que ya tiene su edad) penalicen m\u00e1s los saltos que las funciones trigonom\u00e9tricas.<\/li>\n<li>Adem\u00e1s, la funci\u00f3n <code>Random<\/code> de .NET utiliza internamente un algoritmo relativamente bueno, pero que tiene su propio coste.<\/li>\n<\/ol>\n<p>La manera m\u00e1s sencilla de implementar un generador de n\u00fameros aleatorios con las caracter\u00edsticas anteriores sea probablemente utilizar un iterador basado en un bucle infinito.<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"csharp\">public static IEnumerable&lt;double&gt; BoxMuller()\n{\n    Random rnd = new Random();\n    while (true)\n    {\n        double u = Math.Log(1 - rnd.NextDouble());\n        double r = Math.Sqrt(-u - u);\n        double v = 2 * Math.PI * rnd.NextDouble();\n        yield return Math.Cos(v) * r;\n        yield return Math.Sin(v) * r;\n    }\n}\n<\/pre>\n<p>Por supuesto, esta es la implementaci\u00f3n m\u00e1s tonta posible: la instancia que contiene las variables de estado de la iteraci\u00f3n pertenece a una clase y ocupa memoria din\u00e1mica. Adem\u00e1s, es bastante probable que el compilador llame a la propiedad <code>Current<\/code> y al m\u00e9todo <code>MoveNext<\/code> a trav\u00e9s del tipo de interfaz <code>IEnumerator<\/code>, con lo que se tratar\u00eda de llamadas virtuales. Pero existen t\u00e9cnicas sencillas para resolver estos dos problemas, aunque las explicar\u00e9 en otro momento. Si tiene prisa, puede mirar como la clase <code>List<\/code> implementa internamente su iterador (se utiliza una estructura). He hecho la prueba y, al menos en .NET Core, la ganancia en velocidad no es significativa.<\/p>\n<p>La imagen de la entrada, por cierto, es una representaci\u00f3n ficticia de la famosa torre de Babel. Quiz\u00e1s habr\u00eda sido m\u00e1s apropiado usar una imagen de un zigurat, pero pens\u00e1ndolo mejor, la forma de la torre se parece un poco a la campana de Gauss.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>En casi todos los fen\u00f3menos aleatorios, ya pertenezcan a la f\u00edsica, la gen\u00e9tica o las finanzas, la distribuci\u00f3n normal, o de Gauss-Laplace (la de la famosa curva de la campana) juega un papel importante. Sin embargo, .NET no ofrece de serie una clase, o un m\u00e9todo, que genere valores aleatorios pertenecientes a esta distribuci\u00f3n. Podemos [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":109,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"jetpack_post_was_ever_published":false,"_jetpack_newsletter_access":"","_jetpack_dont_email_post_to_subs":false,"_jetpack_newsletter_tier_id":0,"_jetpack_memberships_contains_paywalled_content":false,"_jetpack_memberships_contains_paid_content":false,"footnotes":""},"categories":[7],"tags":[15,16,14],"class_list":["post-107","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-fintech","tag-algorithms","tag-iterator","tag-normal-distribution"],"jetpack_featured_media_url":"https:\/\/i0.wp.com\/intsight.com\/wp-content\/uploads\/2020\/03\/babel.png?fit=360%2C360&ssl=1","jetpack_sharing_enabled":true,"_links":{"self":[{"href":"https:\/\/intsight.com\/index.php\/wp-json\/wp\/v2\/posts\/107","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/intsight.com\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/intsight.com\/index.php\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/intsight.com\/index.php\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/intsight.com\/index.php\/wp-json\/wp\/v2\/comments?post=107"}],"version-history":[{"count":25,"href":"https:\/\/intsight.com\/index.php\/wp-json\/wp\/v2\/posts\/107\/revisions"}],"predecessor-version":[{"id":584,"href":"https:\/\/intsight.com\/index.php\/wp-json\/wp\/v2\/posts\/107\/revisions\/584"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/intsight.com\/index.php\/wp-json\/wp\/v2\/media\/109"}],"wp:attachment":[{"href":"https:\/\/intsight.com\/index.php\/wp-json\/wp\/v2\/media?parent=107"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/intsight.com\/index.php\/wp-json\/wp\/v2\/categories?post=107"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/intsight.com\/index.php\/wp-json\/wp\/v2\/tags?post=107"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}