<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	>

<channel>
	<title>Apuntes</title>
	<atom:link href="http://www.metonymie.com/apuntes/feed/" rel="self" type="application/rss+xml" />
	<link>http://www.metonymie.com/apuntes</link>
	<description>I blinded her with science!</description>
	<pubDate>Mon, 05 Mar 2012 16:40:01 +0000</pubDate>
	<generator>http://wordpress.org/?v=2.5.1</generator>
	<language>en</language>
			<item>
		<title>Algunos resultados interesantes</title>
		<link>http://www.metonymie.com/apuntes/2012/03/03/algunos-resultados-interesantes.html</link>
		<comments>http://www.metonymie.com/apuntes/2012/03/03/algunos-resultados-interesantes.html#comments</comments>
		<pubDate>Sat, 03 Mar 2012 19:17:37 +0000</pubDate>
		<dc:creator>admin</dc:creator>
		
		<category><![CDATA[Combinatoria]]></category>

		<category><![CDATA[Python]]></category>

		<category><![CDATA[Uncategorized]]></category>

		<category><![CDATA[programación dinamica]]></category>

		<category><![CDATA[Stirling numbers]]></category>

		<guid isPermaLink="false">http://www.metonymie.com/apuntes/?p=56</guid>
		<description><![CDATA[Algunos resultados interesantes de aplicar la función del post anterior.]]></description>
			<content:encoded><![CDATA[<p>Como conte en este <a href="http://www.metonymie.com/apuntes/2012/03/01/algunas-notas-sobre-explosion-combinatoria-los-lenguajes-de-scripting-y-el-uso-de-memoria.html">post</a>, para resolver el problema del <a href="http://www.metonymie.com/apuntes/2012/03/03/cuantas-cadenas-de-tamano-n-pueden-ser-creadas-con-exactamente-m-simbolos-de-un-alfabeto-de-k-simbolos.html">post anterior</a>, hice una serie de pruebas cuyos resultados se pueden ver <a href="http://www.metonymie.com/projects/2011/combinatoria/resultados_problema_combinatoria.html">aca</a>. Me quedo un cubo triangularmente cortado de resultados de variaciones de los parámetros de la función descripta. Para acelerar un poco los tests, una vez que conseguí una solución (cuya demostración se puede encontrar en una hermosa versión typesetada en <a href="http://en.wikipedia.org/wiki/LaTeX">LaTeX</a> acá) la programe en python usando técnicas de <a href="http://en.wikipedia.org/wiki/Dynamic_programming">programacion dinámica</a>. Esta versión se puede encontrar <a href="https://github.com/emluque/Post_Stuff/blob/master/Problema_Combinatoria/Resultados/problema_combinatoria.py">aqui</a>.</p>
<p>Lo interesante fue que me quedaron varias secuencias de números y decidí ir a <a href="http://oeis.org/">OEIS</a> para testearlo. Me encontré entonces con una serie de resultados interesantes que decidí publicar acá para revisarlos mas adelante.</p>
<h3>Resultados simples</h3>
<ul>
	<li>Si m=n=k , la función devuelve una permutación. m!</li>
	<li>si m=n, m&lt;k => f(n, m, k) = k!/(k-m)!</li>
</ul>
<h3>Resultados interesantes</h3>
<ul>
<li><p>n libre, m=3, k=4</p>
<p>Devuelve permutación(4) * Stirling numbers of second kind, S(n,3) ( <a href="http://oeis.org/A000392">http://oeis.org/A000392</a> )</p>
</li>
<li><p>n libre, m=4, k=4</p>
<p>Devuelve permutación(4) * Stirling numbers of second kind, S(n,4) ( <a href="http://oeis.org/A000453">http://oeis.org/A000453</a> )
</p>
</li>
<li><p>n libre, m=3, k=5</p>
<p>Devuelve 5*4*3 * Stirling numbers of second kind, S(n,3) ( <a href="http://oeis.org/A000392">http://oeis.org/A000392</a> )
</p>
</li>
<li><p>n libre, m=4, k=5</p>
<p>Devuelve 5*4*3*2 * Stirling numbers of second kind, S(n,4) ( <a href="http://oeis.org/A000453">http://oeis.org/A000453</a> )
</p>
</li>
<li><p>n libre, m=5, k=5</p>
<p>Devuelve 5*4*3*2 * Stirling numbers of the second kind, S(n,5) (<a href="http://oeis.org/A000481">http://oeis.org/A000481</a> )
</p>
</li>
<li><p>n libre, m=3, k=6</p>
<p>Devuelve 5*4*3*2 * Stirling numbers of second kind, S(n,3) ( <a href="http://oeis.org/A000392">http://oeis.org/A000392</a> )
</p>
</li>
<li><p>n libre, m=4, k=6</p>
<p>Devuelve 6*5*4*3 * Stirling numbers of second kind, S(n,4) ( <a href="http://oeis.org/A000453">http://oeis.org/A000453</a> )
</p>
</li>
<li><p>n libre, m=5, k=6</p>
<p>Devuelve 720 * Stirling numbers of the second kind, S(n,5) ( <a href="http://oeis.org/A000481">http://oeis.org/A000481</a> )
</p>
</li>
</ul>
<p>Parece existir algún tipo de patrón, lamentablemente no tengo el tiempo en este momento y sobre todas las cosas el nivel de matemática suficiente como para investigarlo un poco más. Escribo este post más que nada para revisarlo nuevamente después de haber pasado por <a href="http://www.amazon.com/Concrete-Mathematics-Foundation-Computer-Science/dp/0201558025/ref=sr_1_1?ie=UTF8&#038;qid=1330804570&#038;sr=8-1">Concrete Mathematics</a>, que todavía no pude estudiar.</p>
<h3>Algún otro resultado</h3>
<ul>
<li><p>n=2, m=2, k libre</p>
<p>Oblong (or promic, pronic, or heteromecic) numbers: n(n+1) ( <a href="http://oeis.org/A002378">http://oeis.org/A002378</a> )</p>
</li>
<li><p>n=3, m=2, k libre</p>
<p>6 times triangular numbers: a(n) = 3*n*(n+1) ( <a href="http://oeis.org/A028896">http://oeis.org/A028896</a> )</p>
</li>
</ul>
<p>Da la sensación de que jugando con las variables del cubo se encontrarían un montón de resultados interesantes.</p>

]]></content:encoded>
			<wfw:commentRss>http://www.metonymie.com/apuntes/2012/03/03/algunos-resultados-interesantes.html/feed</wfw:commentRss>
		</item>
		<item>
		<title>Cuantas cadenas de tamaño n pueden ser creadas con exactamente m símbolos de un alfabeto de k símbolos?</title>
		<link>http://www.metonymie.com/apuntes/2012/03/03/cuantas-cadenas-de-tamano-n-pueden-ser-creadas-con-exactamente-m-simbolos-de-un-alfabeto-de-k-simbolos.html</link>
		<comments>http://www.metonymie.com/apuntes/2012/03/03/cuantas-cadenas-de-tamano-n-pueden-ser-creadas-con-exactamente-m-simbolos-de-un-alfabeto-de-k-simbolos.html#comments</comments>
		<pubDate>Sat, 03 Mar 2012 17:57:28 +0000</pubDate>
		<dc:creator>admin</dc:creator>
		
		<category><![CDATA[Combinatoria]]></category>

		<category><![CDATA[Lenguajes Formales]]></category>

		<category><![CDATA[Lógica]]></category>

		<category><![CDATA[Alfabetos]]></category>

		<category><![CDATA[Combinación]]></category>

		<category><![CDATA[Inducción Fuerte]]></category>

		<category><![CDATA[Recurrencia]]></category>

		<guid isPermaLink="false">http://www.metonymie.com/apuntes/?p=55</guid>
		<description><![CDATA[Formula y demostración del problema planteado en el titulo. ]]></description>
			<content:encoded><![CDATA[<p>Lo que estamos buscando es, dado <em>A</em> un alfabeto de tamaño <em>k</em>, la cantidad de cadenas de tamaño <em>n</em> que contienen exactamente <em>m</em> símbolos de <em>A</em>.</p>

<p>Para resolver este problema primero vamos a resolver lo siguiente:</p>

<h2> Cuantas cadenas de tamaño <em>n</em> pueden ser creadas con todos los símbolos de un alfabeto de <em>k</em> símbolos? </h2>

<p>Se propone la siguiente relación de recurrencia de <em>2</em> variables, donde:</p>

<p><em>n</em>: representa el tamaño de la cadena</p>
<p><em>k</em>: representa la cantidad de símbolos en el alfabeto</p>

<p>
f(0, k) = 0
</p>
<p>Para todo <em>k</em>, ya que la cantidad de cadenas de tamaño 0 que pueden formarse con todos los símbolos de <em>A</em> es 0.</p>

<p>
f(1, 1) = 1  
</p>
<p>Ya que existe solo una cadena de tamaño 1 con 1 símbolo.</p>

<p>
f(n, k) = k( f(n-1,k) + f(n-1, k-1) )  
</p>

<h2>Demostración </h2>

<p>Llamemos <em>A<sub>n,k</sub></em> al conjunto de todas las cadenas de tamaño <em>n</em> que contienen a todos los símbolos de <em>A</em> e intentemos construir todas las cadenas que pertenecen a este conjunto.</p>

<p>Para cada símbolo <em>a</em> de <em>A<sub>n,k</sub></em> vamos a realizar el siguiente procedimiento:</p>

<p>1. Llamemos <em>A<sub>n-1,k</sub></em> al conjunto de todas las cadenas de tamaño <em>(n-1)</em> con exactamente <em>k</em> símbolos de <em>A</em> (o sea con todos los símbolos de <em>A</em>).</p>
<p>Para cada cadena <em>α</em> de <em>A<sub>n-1,k</sub></em> construimos una nueva cadena concatenandole <em>a</em>, nos queda así una nueva cadena: <em>aα</em>. Como <em>α</em> tiene tamaño <em>(n-1)</em>, la nueva cadena <em>aα</em> tiene tamaño <em>n</em>.</p>
<p>Ademas por definición de <em>A<sub>n-1,k</sub></em>, <em>α</em> contiene a todos los símbolos de <em>A</em>, y en particular contiene a <em>a</em>. Entonces de esta manera hemos formado todas las cadenas de <em>A<sub>n,k</sub></em> que comienzan por <em>a</em> y tienen al menos 2 ocurrencias de <em>a</em>.</p>
<p>Nos queda todavía construir todas las cadenas de <em>A<sub>n,k</sub></em> que comienzan por <em>a</em> pero tienen solo una ocurrencia de <em>a</em>.</p>

<p>2. Llamemos <em>A<sub>n-1,k-1</sub></em> al conjunto de todas las cadenas de tamaño <em>(n-1)</em> que están formadas por todos los símbolos de <em>A</em> menos <em>a</em>.</p>
<p>Para cada cadena <em>β</em> de <em>A<sub>n-1,k-1</sub></em> construimos una nueva cadena <em>aβ</em>. Se cumple entonces que <em>aβ</em> tiene tamaño <em>n</em> y ademas dado que <em>β</em> contiene a todos los símbolos de <em>A</em> menos a <em>a</em> y le hemos concatenado <em>a</em>, <em>aβ</em> contiene los <em>k</em> símbolos de <em>A</em>.</p>

<p>A partir de estos dos pasos hemos construido todas las cadenas de <em>A<sub>n,k</sub></em> que comienzan con <em>a</em>. Como por definición de <em>A<sub>n,k</sub></em>, cualquier cadena de <em>A<sub>n,k</sub></em> necesariamente tiene que comenzar con un símbolo de <em>A</em> y <em>a</em> es un elemento arbitrario podemos aplicar el procedimiento para construir todas las cadenas del conjunto <em>A<sub>n,k</sub></em>.</p>

<p>Como por hipótesis, <em>|A<sub>n-1,k-1</sub>| = f(n-1,k-1)</em>, <em>|A<sub>n-1,k</sub>| = f(n-1,k)</em> y como existen exactamente <em>k</em> símbolos de <em>A</em> se cumple que <em>f(n, k) = k( f(n-1,k) + f(n-1, k-1) )</em>.</p>


<h2> Cuantas cadenas de tamaño <em>n</em> pueden ser creadas con exactamente <em>m</em> símbolos de un alfabeto de <em>k</em> símbolos?</h2>

<p>Asumiendo que la función anterior es correcta, podemos proponer la siguiente función:</p> 

<math xmlns="http://www.w3.org/1998/Math/MathML">
<mrow>
	<mi>g</mi>
	<mrow>
		<mo maxsize="1.00em">(</mo>
		<mi>n</mi>
		<mo>,</mo>
		<mi>m</mi>
		<mo>,</mo>
		<mi>k</mi>
		<mo maxsize="1.00em">)</mo>
	</mrow>
	<mo>=</mo>
	<mrow>
		<mo>(</mo>
		<mfrac linethickness="0">
			<mi>m</mi>
			<mi>k</mi>
		</mfrac>
		<mo>)</mo>
	</mrow>
	<mo>&times;</mo>
	<mi>f</mi>
	<mrow>
		<mo maxsize="1.00em">(</mo>
		<mi>n</mi>
		<mo>,</mo>
		<mi>m</mi>
		<mo maxsize="1.00em">)</mo>
	</mrow>
</mrow>
</math>



<p>Llamemos <em>A<sub>m</sub></em> al conjunto de todos los subconjuntos de <em>A</em> con exactamente <em>m</em> elementos. Es conocido que la cantidad de subconjuntos de tamaño <em>m</em> de un conjunto se puede calcular como:</p>


<math xmlns="http://www.w3.org/1998/Math/MathML">
<mrow>
	<mo>|</mo>
	<msub>
		<mi>A</mi>
		<mi>m</mi>
	</msub>
	<mo>|</mo>
	<mo>=</mo>
	<mrow>
		<mo>(</mo>
		<mfrac linethickness="0">
			<mi>m</mi>
			<mi>k</mi>
		</mfrac>
		<mo>)</mo>
	</mrow>
</mrow>
</math>

<p>(Ver <a href="http://en.wikipedia.org/wiki/Combination">Combination</a>) . Sabemos ademas por el resultado anterior que la cantidad de cadenas de tamaño <em>n</em> que pueden hacerse de un conjunto de tamaño <em>m</em> es igual a <em>f(n,m)</em>, a partir de lo cual derivamos la formula.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.metonymie.com/apuntes/2012/03/03/cuantas-cadenas-de-tamano-n-pueden-ser-creadas-con-exactamente-m-simbolos-de-un-alfabeto-de-k-simbolos.html/feed</wfw:commentRss>
		</item>
		<item>
		<title>Algunas notas sobre Explosión Combinatoria, los Lenguajes de Scripting y el Uso de Memoria</title>
		<link>http://www.metonymie.com/apuntes/2012/03/01/algunas-notas-sobre-explosion-combinatoria-los-lenguajes-de-scripting-y-el-uso-de-memoria.html</link>
		<comments>http://www.metonymie.com/apuntes/2012/03/01/algunas-notas-sobre-explosion-combinatoria-los-lenguajes-de-scripting-y-el-uso-de-memoria.html#comments</comments>
		<pubDate>Thu, 01 Mar 2012 18:02:01 +0000</pubDate>
		<dc:creator>admin</dc:creator>
		
		<category><![CDATA[Javascript]]></category>

		<category><![CDATA[c]]></category>

		<category><![CDATA[Pointers]]></category>

		<category><![CDATA[punteros]]></category>

		<guid isPermaLink="false">http://www.metonymie.com/apuntes/?p=53</guid>
		<description><![CDATA[Hace un par de meses estuve trabajando en un problema de combinatoria que consistía en los siguiente: Dado un alfabeto A de tamaño k, cuantas cadenas de tamaño n pueden construirse con exactamente z símbolos de A. Busque en bastantes libros y no pude encontrar una solución, por lo que decidí investigarlo por mi cuenta, [...]]]></description>
			<content:encoded><![CDATA[<p>Hace un par de meses estuve trabajando en un problema de combinatoria que consistía en los siguiente: Dado un alfabeto A de tamaño <em>k</em>, cuantas cadenas de tamaño <em>n</em> pueden construirse con exactamente <em>z</em> símbolos de A. Busque en bastantes libros y no pude encontrar una solución, por lo que decidí investigarlo por mi cuenta, para esto, decidí hacer una serie de experimentos con diferentes valores de <em>k</em>, <em>n</em> y <em>z</em> para intentar llegar a una formula general (que sera el tema de un próximo post).</p>
<p>Lo interesante fue que en este proceso fui probando los limites de mi PC y tuve que buscar diferentes soluciones para lograr los resultados que buscaba, ese es el tema de este post, cuyo código se puede encontrar aquí.</p>
<h2>Primer Intento en Javascript</h2>
<p>Mi primer intento fue simplemente hacer una variedad de diferentes loops en Javascript generando las diferentes combinaciones. Use Javascript porque ingenuamente pensé que era un problema simple y lo que quería resolver rápido.</p>
<p>No hay nada muy complejo en esta solución y se pueden ver una versión para <a href="https://github.com/emluque/Post_Stuff/blob/master/Problema_Combinatoria/Javascript/combinaciones-4.html">4</a> y <a href="https://github.com/emluque/Post_Stuff/blob/master/Problema_Combinatoria/Javascript/combinaciones-5.html">5</a> elementos en la carpeta de Github.</p>
<p>El problema es obviamente que hay que hacer un nested loop para cada tamaño de cadena y alfabeto distinto, era evidente que necesitaba una versión mejor.</p>
<h2>Segundo Intento en Javascript</h2>
<p>El <a href="https://github.com/emluque/Post_Stuff/blob/master/Problema_Combinatoria/Javascript/combinaciones-gen.html">segundo intento</a> es una versión general que usando recursividad busca generar las diferentes combinaciones y además cuenta la cantidad de símbolos que tiene cada cadena y genera un reporte. De nuevo, fue una versión rápida codificada sin mucho razonamiento previo, pero que era lo suficientemente general como para poder resolver el problema.</p>
<p>El problema fue que cuando intente correrla con valores mayores a 6 o 7, mi navegador se tildaba y tenía que reiniciar la maquina. Bueno, es bien sabido que javascript sobre un browser no es un entorno muy confiable cuando se le demanda demasiado, por otro lado todas las cadenas de tamaño 7 con un alfabeto de 7 son 7<sup>7</sup>=823543 variaciones. Además por como funciona javascript en la función donde se generan estas cadenas, se copia un array, pero cuando se agrega un elemento a un array o se concatenan dos array en javascript (a diferencia de por ejemplo Python, donde las listas se concatenan por referencia) se genera un array nuevo con el consiguiente uso de memoria que esto implica.</p>
<pre><code>
function generarCombinaciones(depth, size) {
	if(depth==1) {
		var arr = [];
		for(var x=0; x&lt;size; x++) {
			arr[x] = x;
		} 
		return arr;
	} else {
		var arr = new Array();
		var pre = generarCombinaciones(depth-1, size);
		for(var x=0; x&lt;size; x++) {
			for(var y=0; y&lt;pre.length; y++) {
				var arr2 = new Array();
				arr2.push(x);
				arr2 = arr2.concat(pre[y]);
				arr.push(arr2);
			}
		}
		return arr;
	}
}
</code></pre> 
<p>Decidí para mejorar la performance pasar a C.</p>
<h2>Primer intento en C</h2>
<p>El primer intento en C tenía en cuenta este problema y lo que hacia para resolverlo era a través de punteros ir generando recursivamente las nuevos elementos como referenciando a los elementos anteriores. Por ejemplo: primero se generaba la lista [1,2] y luego para generar [[1,1], [1,2], [2,1], [2,2]] se utilizaba el mismo [1] para [1,1] y para [2,1] o sea el puntero de estas dos listas apuntan al mismo nodo en las dos listas. De esta manera se va generando recursivamente un árbol en el que cada sublista es reutilizada por la lista que las listas que la contienen.</p>
<p>Esta versión se puede encontra <a href="https://github.com/emluque/Post_Stuff/tree/master/Problema_Combinatoria/C/Version-1">aquí</a> y se puede correr, haciendo <code class="inline">gcc contar_combinaciones.c -lm -o contar_combinaciones</code>. Además esta versión genera una página HTML con las tablas de resultados y también una tabla con los resultados factorizados. La porción de código a la que se hace referencia es:</p>
<pre><code>

Result_list generar_combinaciones(int depth, int size) {
	Result_list results = NULL;
	Result_list cursor;

	int x;	

	if(depth == 0) {
		for(x=0; x&lt;size; x++) {
			List temp;
			temp = (List) malloc(sizeof(struct Node));
			temp->value = x;
			temp->next = NULL;

			Result_list temp_result;
			temp_result = (Result_list) malloc(sizeof(struct Result));
			temp_result->list = temp;
			temp_result->next = NULL;

			if(results == NULL) {
				results = temp_result;
				cursor = temp_result;
			} else {
				cursor->next = temp_result;
				cursor = cursor->next;
			}
		}
		return results;
	} else {
		Result_list previous_results;
		Result_list previous_temp_cursor;

		previous_results = generar_combinaciones(depth-1, size);
		
		while(previous_results != NULL) {
			for(x=0; x&lt;size; x++) {
				List temp;
				temp = (List) malloc(sizeof(struct Node));
				temp->value = x;
				temp->next = previous_results->list;

				Result_list temp_result;
				temp_result = (Result_list) malloc(sizeof(struct Result));
				temp_result->list = temp;
				temp_result->next = NULL;

				if(results == NULL) {
					results = temp_result;
					cursor = temp_result;
				} else {
					cursor->next = temp_result;
					cursor = cursor->next;
				}
			}
			previous_temp_cursor = previous_results->next;
			free(previous_results);
			previous_results = previous_temp_cursor;			
		}
		return results;
	}
} 


</code></pre>
<p>Me resulto muy interesante de hacer el tener que generar una lista de resultados en cada iteración recursiva que va hilvanando las listas finales.</p>
<p>Sin embargo había otro problema, cuando se van generando cada una de las combinaciones se mantienen en memoria antes de ser computados la cantidad de símbolos que contienen. Bueno con parámetros lo suficientemente grandes, me quedaba sin memoria! y empezaba a tocar el swap (a pesar de que mi maquina de desarrollo tiene 16 gigas de ram). Por lo que tuve que buscar otra solución que fue la solución final.</p>
<h2>Solución final en C</h2>
<p>En la <a href="https://github.com/emluque/Post_Stuff/tree/master/Problema_Combinatoria/C/Version-2">versión final</a> se me ocurrió simplemente contar la cantidad de símbolos luego de generar la lista. Como las listas se van generando recursivamente me pareció una solución bastante elegante.</p>
<pre><code>

void generar_contar_combinaciones(int depth, int size, List lista, int arr[MAX_ALPHABET_SIZE]) {
	int x, c;	
	List cursor_ultimo = lista;

	if(cursor_ultimo != NULL) {
		while(cursor_ultimo->next != NULL) cursor_ultimo = cursor_ultimo->next;
	}

	if(depth == 0) {
		for(x=0; x&lt;size; x++) {
			List temp;
			temp = (List) malloc(sizeof(struct Node));
			temp->value = x;
			temp->next = NULL;

			if(cursor_ultimo == NULL) {
				c = contar_elementos_distintos(temp, size);
				arr[ c ]++;
				free(temp);
			} else {
				cursor_ultimo->next = temp;
				c = contar_elementos_distintos(lista, size);
				arr[ c ]++;
				free(temp);
				cursor_ultimo->next = NULL;
			}
		}
	} else {
		for(x=0; x&lt;size; x++) {
			List temp;
			temp = (List) malloc(sizeof(struct Node));
			temp->value = x;
			temp->next = NULL;

			if(cursor_ultimo == NULL) {
				generar_contar_combinaciones(depth-1, size, temp, arr);
				free(temp);
			} else {
				cursor_ultimo->next = temp;
				generar_contar_combinaciones(depth-1, size, lista, arr);
				free(temp);
				cursor_ultimo->next = NULL;
			}
		}
	}
}

</code></pre>
<h2>Resultados</h2>
<p>Los resultados de esta exploración pueden encontrarse <a href="http://www.metonymie.com/projects/2011/combinatoria/resultados_problema_combinatoria.html">aquí</a> y proximamente voy a hacer un post con la respuesta al problema.</p>]]></content:encoded>
			<wfw:commentRss>http://www.metonymie.com/apuntes/2012/03/01/algunas-notas-sobre-explosion-combinatoria-los-lenguajes-de-scripting-y-el-uso-de-memoria.html/feed</wfw:commentRss>
		</item>
		<item>
		<title>Problema de Algoritmia Básico: Simulación del Caso Promedio</title>
		<link>http://www.metonymie.com/apuntes/2012/02/29/problema-de-algoritmia-basico-simulacion-del-caso-promedio.html</link>
		<comments>http://www.metonymie.com/apuntes/2012/02/29/problema-de-algoritmia-basico-simulacion-del-caso-promedio.html#comments</comments>
		<pubDate>Wed, 29 Feb 2012 17:12:37 +0000</pubDate>
		<dc:creator>admin</dc:creator>
		
		<category><![CDATA[Algoritmia]]></category>

		<category><![CDATA[Estadística]]></category>

		<category><![CDATA[Probabilidad]]></category>

		<category><![CDATA[Python]]></category>

		<category><![CDATA[Simulación]]></category>

		<guid isPermaLink="false">http://www.metonymie.com/apuntes/?p=52</guid>
		<description><![CDATA[Una simulación matemática hecha en Python, para simular el caso promedio.]]></description>
			<content:encoded><![CDATA[<p>Finalmente para analizar el caso promedio, decidí realizar una serie de simulaciones en Python, aproveche de paso, para presentar este programa como trabajo práctico final en la materia &#8220;Lenguajes de Scripting&#8221; de la <a href="http://www.palermo.edu">facultad</a>. El código del programa de la simulación puede encontrarse <a href="https://github.com/emluque/Post_Stuff/tree/master/Problema_Algoritmia_Basico/Simulacion">aquí</a>, para correrlo compilar <a href="https://github.com/emluque/Post_Stuff/blob/master/Problema_Algoritmia_Basico/Simulacion/algoritmo/algo.c">algo.c</a>, ejecutar <a hrfe="https://github.com/emluque/Post_Stuff/blob/master/Problema_Algoritmia_Basico/Simulacion/limpiar.sh">limpiar.sh</a> y luego <a href="https://github.com/emluque/Post_Stuff/blob/master/Problema_Algoritmia_Basico/Simulacion/version-1-0.py">version-1-0.py</a>. Las muestras se guardan en formato CSV en la carpeta &#8220;resultados/muestras&#8221; y los reportes en &#8220;resultados/reportes&#8221;.</p>
<h2>Algunas notas sobre la simulación</h2>
<p>Me interesaba analizar específicamente el comportamiento de la porción del algoritmo en la que se itera a travéz de la lista pasados que es la porción variable del mismo. Para hacer esto, cree una variable global <code class="inline">total</code> y en cada iteración por la lista <code class="inline">pasados</code> en la función <code class="inline">esta_en_lista()</code> se la incrementa en uno. El programa escribe luego en <code class="inline">STDOUT</code> el valor de esta variable al finalizar. Ver <a href="https://github.com/emluque/Post_Stuff/blob/master/Problema_Algoritmia_Basico/Simulacion/algoritmo/algo.c">algo.c</a>.</p>
<p>En el script de Python se genera una lhttps://github.com/emluque/Post_Stuff/blob/master/Problema_Algoritmia_Basico/Simulacion/version-1-0.pyista randomizada se levanta un proceso al que se le pasa esta lista como argumento y luego se lee el <code class="inline">STDOUT</code> del proceso para obtener el costo. Esto se hace en el archivo: <a href="https://github.com/emluque/Post_Stuff/blob/master/Problema_Algoritmia_Basico/Simulacion/muestras.py">muestras.py</a> y la porción de código que hace esto es:</p>
<pre><code>
  def generar_lista(self):
    lista = []
    for i in range(self.n):
      lista.append(random.randint(1, self.k))
    return &#8220;,&#8221;.join( [str(x) for x in lista])

  def generar_resultado(self, lista ):
    process = subprocess.Popen(["algoritmo/algo", lista] , shell=False, stdin=subprocess.PIPE, stdout=subprocess.PIPE)
    res = &#8220;&#8221;
    for char in process.stdout.read():
      res += char
    
    process.wait()
    #Esta linea que sigue es medio al dope
    if (process.returncode != None):
      if(process.returncode &lt; 0):
        print(&#8221;&#8212;-&#8221;)
        print(process.returncode)
        print(&#8221;&#8212;-&#8221;)
        raise NameError(&#8221;Tio, No andubo el proceso.&#8221;) 
    else:   
      process.kill()

    return int(res)

  def generar_resultados(self):
    for i in range(self.cant):
      strr = self.generar_lista();
      a = self.generar_resultado( strr )
      self.resultados.append(a)
</code></pre>
<h2>Resultados</h2>
<p>Un conjunto de resultados pregenerados (una corrida completa del programa puede tardar varias horas) puede verse <a href="http://www.metonymie.com/projects/2011/simulacion/resultados/reportes/index_2011-11-28-20-38-36.html">aquí</a>.</p>
<h3>Reportes de Tipo 1</h3>
<p>Lo primero que podemos ver en los <a href="http://www.metonymie.com/projects/2011/simulacion/resultados/reportes/reportes-1_2011-11-27-16-7-49.html">reportes de tipo 1</a> es como a mayor tamaño de muestra, la función va tendiendo a un tipo de gráfica especifica. Que no es exactamente igual para diferentes tamaños de alfabeto y cadena, pero se acerca a un <a href="http://es.wikipedia.org/wiki/Funci%C3%B3n_gaussiana">Campana de Gauss</a> aunque un poco deformada en la región derecha y con una media corrida un poco hacia la izquierda del rango de valores posibles.</p>
<p>Por ejemplo, este es el gráfico obtenido para tamaño de cadena 10, tamaño de alfabeto 10 con 100000 muestras:</p>
<p><img src="http://www.metonymie.com/projects/2011/simulacion/resultados/reportes/imagenes/10-10-100000_2011-11-27-16-14-29.png"/></p>
<p>Y este es para 200,200,100000:</p>
<p><img src="http://www.metonymie.com/projects/2011/simulacion/resultados/reportes/imagenes/200-200-100000_2011-11-27-17-43-19.png"/></p>
<h3>Reportes de Tipo 2</h3>
<p>Los <a href="http://www.metonymie.com/projects/2011/simulacion/resultados/reportes/reportes-2_2011-11-27-16-7-49.html">reportes de tipo 2</a>, permiten ver el comportamiento de la gráfica manteniendo fijo el tamaño de cadena y de muestra.</p>
<h3>Que nos muestra el reporte de tipo 2?</h3>
<p>Bueno lo que vemos es que manteniendo el tamaño de cadena fijo, al ir aumentando el tamaño de alfabeto, los valores estadísticos (promedio, mediana, modo) van siendo cada vez mayores, lo mismo sucede con la gráfica. Podemos pensar que la posibilidad de ocurrencia de un elemento mayor a los elementos precedentes aumenta a mayor tamaño de alfabeto, lo cual mueve los valores hacia la derecha. Ver el comportamiento de los valores en el <a href="http://www.metonymie.com/projects/2011/simulacion/resultados/reportes/reporte2-200-50000_2011-11-28-18-27-29.html">reporte para tamaño de cadena 200</a>.</p>
<p>Por otro lado si el tamaño de alfabeto es significativamente mayor al tamaño de cadena, este comportamiento va disminuyendo. Lo cual indica que luego de un determinado valor en función al tamaño de la cadena, la posibilidad de que ocurra un elemento mayor a los elementos anteriores va estabilizándose. Ver <a href="http://www.metonymie.com/projects/2011/simulacion/resultados/reportes/reporte2-10-50000_2011-11-27-21-39-28.html">reporte para tamaño de cadena 10</a>.</p>
<h3>Reportes de Tipo 3</h3>
<p>Los reportes de tipo 3, nos muestran que la gráfica de la función se encuentra corrida hacia la izquierda en relación al rango de valores posibles, y que a mayor valor de n y k más hacia la izquierda se mueve en proporción al rango. Esto parecería coincidir con nuestras intuiciones de los posts anteriores, y parecería confirmarse gráficamente los factores de atenuamiento que habíamos considerado en <a href="http://www.metonymie.com/apuntes/2012/02/28/problema-de-algoritmia-basico-complejidad-algoritmica.html">este</a>y <a href="http://www.metonymie.com/apuntes/2012/02/29/problema-de-algoritmia-basico-algunas-observaciones-sobre-el-peor-caso.html">este posts</a>.</p>
<p>Gráfico de la función con alfabeto n=50, k=50 y muestra de tamaño 500:</p>
<p><img src="http://www.metonymie.com/projects/2011/simulacion/resultados/reportes/imagenes/50-50-50000_2011-11-28-21-51-28.png"/></p>
]]></content:encoded>
			<wfw:commentRss>http://www.metonymie.com/apuntes/2012/02/29/problema-de-algoritmia-basico-simulacion-del-caso-promedio.html/feed</wfw:commentRss>
		</item>
		<item>
		<title>Problema de Algoritmia Básico: Algunas observaciones sobre el peor caso</title>
		<link>http://www.metonymie.com/apuntes/2012/02/29/problema-de-algoritmia-basico-algunas-observaciones-sobre-el-peor-caso.html</link>
		<comments>http://www.metonymie.com/apuntes/2012/02/29/problema-de-algoritmia-basico-algunas-observaciones-sobre-el-peor-caso.html#comments</comments>
		<pubDate>Wed, 29 Feb 2012 15:39:26 +0000</pubDate>
		<dc:creator>admin</dc:creator>
		
		<category><![CDATA[Algoritmia]]></category>

		<category><![CDATA[Caso Promedio]]></category>

		<category><![CDATA[complejidad]]></category>

		<category><![CDATA[Peor Caso]]></category>

		<guid isPermaLink="false">http://www.metonymie.com/apuntes/?p=51</guid>
		<description><![CDATA[Algunas observaciones sobre el peor caso en el problema planteado en la serie de posts anteriores.]]></description>
			<content:encoded><![CDATA[<p>Este post continua con una pequeña observación sobre lo visto en el post <a href="http://www.metonymie.com/apuntes/2012/02/28/problema-de-algoritmia-basico-complejidad-algoritmica.html">Problema de Algoritmia Básico, Análisis de Complejidad</a>.</p>

<p>Voy a continuar en este post trabajando sobre la solución básica, analizando el comportamiento de la función <code class="inline">esta_en_lista()</code> ante diferentes inputs.</p>

<h3>Listas ordenadas con un alfabeto finito</h3>

<p>Supongamos que el alfabeto que tienen las listas posibles tiene un tamaño finito, o lo que es lo mismo que el numero de ints posibles es finito, y ademas que las listas son ordenadas y de mayor tamaño al tamaño del alfabeto.</p>
<p>Por ejemplo, tomemos un alfabeto de tamaño 3:</p>
<p>[1,2,3,1,2,3,1,2,3,....]</p>

<p>Sabemos por el análisis del post referido que mientras se va construyendo la lista <code class="inline">pasados</code>, se necesita iterar por cada elemento menor hasta incluirlo en la lista. Sabemos así mismo que una vez construida la lista <code class="inline">pasados</code> por cada elemento repetido se necesita pasar por todos lo elementos anteriores antes de encontrar al elemento. Por esto, sabemos que el costo asociado a cada elemento es: </p>

<pre>
T(n%3 == 0) = 2a + w
T(n%3 == 1) = 1a + w
T(n%3 == 2) = 0a + w
</pre>
<p>Donde <em>a</em> es el costo de iterar por cada elemento de la lista y <em>w</em> es una constante que representa el resto de las operaciones (constantes) de la función, <em>n</em> es la posición del elemento en la lista y Usamos la notación <em>n%3 == x</em> para representar aquellos valores de n en el que el modulo 3 es igual a <em>x</em></p>
<p>Para el resto del post vamos a olvidarnos de <em>w</em> y preocuparnos solamente por <em>a</em> ya que es aqui donde se producen las operaciones que son variables de la función (por el costo de iterar por la lista).</p>
<p>Es evidente que en una sucesión de listas ordenadas de tamaño 3, la cantidad de veces que se cumplirá la llamada a cada uno de los valores de la recurrencia es 1/3 * n. O sea, en el total de la lista se cumple que 1/3 de los valores son 1, un tercio son 2 y un tercio son 3.</p>

<p>Nos queda entonces el costo total de las llamadas a la función por iterar por cada uno de los elementos como:</p>
<p>Total = 1/3 * n * T(n%3==0) + 1/3 * n * T(n%3 == 1) + 1/3 * n * T(n%3 == 2)</p>

<p>Esta formula puede ampliarse para cualquier tamaño de alfabeto:</p>

<p>Total = 1/k * n * T(n%k == 0) + 1/k * n * T(n%k == k-1) + 1/k * n * T(n%k == k-2) + &#8230; + 1/k * n * T(n%k == 1)</p>

<p>Donde <em>k</em> es el tamaño de alfabeto. Ademas sabemos (de acuerdo al análisis del post anterior) que:</p>

<pre>
T(n%k == 0) = (k-1)a
T(n%k == 1) = (k-2)a
T(n%k == 2) = (k-3)a
...
T(n%k == k-2) = 1a
T(n%k == k-1) = 0a
</pre>

<p>Por lo que esta formula puede plantearse entonces como:</p>
<p>Total = 1/k * n * (k-1)a + 1/k * n * (k-2)a + 1/k * n * (k-3)a + &#8230; + 1/k * n * 1a</p>
<p>O lo que es lo mismo:</p>

<math xmlns="http://www.w3.org/1998/Math/MathML">
<mrow>
	<mstyle displaystyle="true">
		<munderover>
			<mo>&sum;</mo>
			<mrow>
				<mi>i</mi>
				<mo>=</mo>
				<mn>1</mn>
			</mrow>
			<mi>k</mi>
		</munderover>
	</mstyle>
	<mfrac linethickness="1">
		<mn>1</mn>
		<mi>k</mi>
	</mfrac>
	<mi>n</mi>
	<mrow>
		<mo maxsize="1.00em">(</mo>
		<mi>i</mi>
		<mo>-</mo>
		<mn>1</mn>
		<mo maxsize="1.00em">)</mo>
	</mrow>
	<mi>a</mi>
</mrow>
</math>

<h3>Repensando el peor caso</h3>
<p>En el peor caso se cumple que la lista esta ordenada y el tamaño del alfabeto es igual al tamaño de la lista, por consiguiente la formula:</p>

<math xmlns="http://www.w3.org/1998/Math/MathML">
<mrow>
	<mstyle displaystyle="true">
		<munderover>
			<mo>&sum;</mo>
			<mrow>
				<mi>i</mi>
				<mo>=</mo>
				<mn>1</mn>
			</mrow>
			<mi>k</mi>
		</munderover>
	</mstyle>
	<mfrac linethickness="1">
		<mn>1</mn>
		<mi>k</mi>
	</mfrac>
	<mi>n</mi>
	<mrow>
		<mo maxsize="1.00em">(</mo>
		<mi>i</mi>
		<mo>-</mo>
		<mn>1</mn>
		<mo maxsize="1.00em">)</mo>
	</mrow>
	<mi>a</mi>
</mrow>
</math>


<p>Se reduce a:</p>

<math xmlns="http://www.w3.org/1998/Math/MathML">
<mrow>
	<mstyle displaystyle="true">
		<munderover>
			<mo>&sum;</mo>
			<mrow>
				<mi>i</mi>
				<mo>=</mo>
				<mn>1</mn>
			</mrow>
			<mi>n</mi>
		</munderover>
	</mstyle>
	<mfrac linethickness="1">
		<mn>1</mn>
		<mi>n</mi>
	</mfrac>
	<mi>n</mi>
	<mrow>
		<mo maxsize="1.00em">(</mo>
		<mi>i</mi>
		<mo>-</mo>
		<mn>1</mn>
		<mo maxsize="1.00em">)</mo>
	</mrow>
	<mi>a</mi>
</mrow>
</math>
= 
<math xmlns="http://www.w3.org/1998/Math/MathML">
<mrow>
	<mstyle displaystyle="true">
		<munderover>
			<mo>&sum;</mo>
			<mrow>
				<mi>i</mi>
				<mo>=</mo>
				<mn>1</mn>
			</mrow>
			<mi>n</mi>
		</munderover>
	</mstyle>
	<mrow>
		<mo maxsize="1.00em">(</mo>
		<mi>i</mi>
		<mo>-</mo>
		<mn>1</mn>
		<mo maxsize="1.00em">)</mo>
	</mrow>
	<mi>a</mi>
</mrow>
</math>
=
<math xmlns="http://www.w3.org/1998/Math/MathML">
<mrow>
	<mfrac linethickness="1">
		<mrow>
			<mrow>
				<mo maxsize="1.00em">(</mo>
				<mi>n</mi>
				<mo>-</mo>
				<mn>1</mn>
				<mo maxsize="1.00em">)</mo>
			</mrow>
			<mi>n</mi>
		</mrow>
		<mn>2</mn>
	</mfrac>
</mrow>
</math>
<!-- end MathToWeb -->  	



<p>Que es la equivalencia que vimos anteriormente.</p>

<h3>Que pasa si no es una lista ordenada?</h3>
<p>Supongamos que la lista no es ordenada y supongamos un alfabeto de tamaño k. En este caso hasta que no se sucedan los k valores del alfabeto, el costo asociado a cada entero no se corresponde directamente con la tabla planteada anteriormente. Por ejemplo si se presenta un <em>3</em> como primer elemento, no se van a cumplir 2 iteraciones sino 0, ya que la lista <code class="inline">pasados</code> esta vacia. Esto sucederia hasta que aparezcan en la lista todos los elementos del alfabeto.</p>
<p>Podemos plantear (ingenuamente) para este caso una funcion de aproximacion que seria la siguiente:</p>
<p>Total = 1/k * n * (k-1)a + 1/k * n * (k-2)a + 1/k * n * (k-3)a + &#8230; + 1/k * n * 1a <strong>- j</strong></p>

<p>Donde <em>j</em> representara la disminucion producida en relacion a la formula anterior hasta que se suceden todos los valores de la lista.</p>
<p>Es evidente que, manteniendo fijo el tamaño de alfabeto,  y con un tamaño de lista lo significativamente mayor al tamaño de alfabeto, <em>j</em> se vuelve en el caso tipico despreciable en el costo total de la funcion.</p>
<p>Sin embargo analizar el caso promedio sigue siendo muy complicado. Hica varios intentos y me quedaba una formula enorme, por consiguiente decidi hacer una <a href="http://en.wikipedia.org/wiki/Computer_simulation">simulacion matematica</a> para evaluarlo y es el tema del ultimo y final post de esta serie.</p>

<h4>Referencias</h4>
<p><a class="amazon" id="foundations-of-computer-science-c-edition" href="http://www.amazon.com/Foundations-Computer-Science-Principles/dp/0716782847/ref=sr_1_1?ie=UTF8&#038;qid=1316666855&#038;sr=8-1">Foundations of Computer Science: C Edition</a>; Aho, Ullman; W. H. Freeman; 1994</p>
<p><a class="amazon" href="http://www.amazon.com/Introduction-Algorithms-Thomas-H-Cormen/dp/0262033844/ref=sr_1_1?ie=UTF8&#038;qid=1330047935&#038;sr=8-1">Introduction to Algorithms, 2nd Edition</a>; Cormen, Leierson, Rivest, Stein; MIT Press; 2009</p>  ]]></content:encoded>
			<wfw:commentRss>http://www.metonymie.com/apuntes/2012/02/29/problema-de-algoritmia-basico-algunas-observaciones-sobre-el-peor-caso.html/feed</wfw:commentRss>
		</item>
		<item>
		<title>Problema de Algoritmia Básico: Mejoras a la Solución Básica</title>
		<link>http://www.metonymie.com/apuntes/2012/02/28/problema-de-algoritmia-basico-mejoras-a-la-solucion-basica.html</link>
		<comments>http://www.metonymie.com/apuntes/2012/02/28/problema-de-algoritmia-basico-mejoras-a-la-solucion-basica.html#comments</comments>
		<pubDate>Wed, 29 Feb 2012 00:10:18 +0000</pubDate>
		<dc:creator>admin</dc:creator>
		
		<category><![CDATA[Algoritmia]]></category>

		<category><![CDATA[Estructuras de datos]]></category>

		<category><![CDATA[c]]></category>

		<category><![CDATA[Array]]></category>

		<category><![CDATA[Binary Search Tree]]></category>

		<category><![CDATA[Hashtable]]></category>

		<category><![CDATA[Linked List]]></category>

		<guid isPermaLink="false">http://www.metonymie.com/apuntes/?p=50</guid>
		<description><![CDATA[Diferentes estructuras de datos para mejorar la performance del Algoritmo presentado.]]></description>
			<content:encoded><![CDATA[<p>Este post continua con el problema planteado en este <a href="http://www.metonymie.com/apuntes/2012/02/28/problema-de-algoritmia-basico-solucion-basica.html">post</a>.  Habiendo ya <a href="http://www.metonymie.com/apuntes/2012/02/28/problema-de-algoritmia-basico-complejidad-algoritmica.html">analizado la complejidad de la solución básica </a>, el siguiente paso es tratar de mejorarla proponiendo diferentes estructuras de datos para la función <code class="inline">esta_en_lista()</code>.</p>
<h2>Usar un array</h2>
<p>La solución mas inmediata es cambiar la lista <code class="inline">pasados</code> por un array. Esta version se puede ver en <a href="">solucion-array.c</a>.</p>
<p>La gran diferencia entre ambas versiones es la función <code class="inline">esta_en_lista()</code>, que ahora nos queda:</p>
<pre><code>
int esta_en_lista(int value, char pasados[ MAXINT ]) {	

	if( pasados[value] == 1) return 1;
	pasados[value] = 1;
	return -1;

}
</code></pre>
<p> Código en <a href="https://github.com/emluque/Post_Stuff/blob/master/Problema_Algoritmia_Basico/Solucion/solucion-array.c">solucion-array.c</a></p>
<h3>Porque es una mejora?</h3>
<p>El acceso a un array es siempre O(1). Por consiguiente el costo total del algoritmo seria O(n) * O(1), que es equivalente a O(n).</p>
<h3>Cuales son las desventajas?</h3>
<p>La desventaja principal es que lo que ganamos en la mejora de la complejidad de la función, lo perdemos en la complejidad espacial. Basicamente porque ahora necesitamos alocar e inicializar memoria para un array del tamaño del mayor int posible en la lista.</p>
<p>La otra desventaja es que es necesario inicializar el array a 0. Lo cual hace que el algoritmo de por si tenga una cota minima de O( <code class="inline">MAXINT</code> ), en el caso (esperable) de que la lista sea de tamaño mayor que el <code class="inline">MAXINT</code> posible este no es un problema ya que la porción del algoritmo que es O(n) es mayor que el <code class="inline">MAXINT</code>, pero si estuvieramos trabajando con un input de listas chicas, la solución anterior podria ser preferible.</p>
<p>En definitiva, esta solución es mas efectiva cuando el mayor int posible es pequeño y la lista es muy larga.</p>
<h2>Usar un Binary Search Tree</h2>
<p>Esta versión se puede ver en <a href="https://github.com/emluque/Post_Stuff/blob/master/Problema_Algoritmia_Basico/Solucion/solucion-binary-search-tree.c">solucion-binary-search-tree.c</a>. El código nos queda:</p>
<pre><code>
int esta_en_lista(int value, BSTree *pasados) {	
  BSTree cursor = *pasados;

  //El nodo esta vacio
  if( cursor == NULL) {
    BSTree temp = (BSTree) malloc(sizeof(struct BST_Node));
    temp->value = value;
    temp->right = NULL;
    temp->left = NULL;
    *pasados = temp;
    return -1;
  }

  //El nodo tiene el mismo valor
  if( cursor->value == value ) return 1;

  if( cursor->value &lt; value) {
      //Fijarse en el nodo derecho
      return esta_en_lista(value, &#038;(cursor->right));
   } else {
      //Fijarse en el nodo izquierdo
      return esta_en_lista(value, &#038;(cursor->left));
   }
}
</code></pre>
<p>La ventaja inmediata de un <a href="http://en.wikipedia.org/wiki/Binary_search_tree">Binary Search Tree</a> es que la inserción y la busqueda tienen para un input promedio, complejidad O(log(n)). Esto nos mejoraría el algoritmo, llevandolo a O(n log(n)).</p>
<p>Sigue existiendo, sin embargo, el problema de que en el peor caso posible de una lista ordenada ascendentemente: [1,2,3,4,..]; el binary search tree se comporta exactamente igual que una lista. Esto se puede arreglar usando un <a href="http://en.wikipedia.org/wiki/Self-balancing_binary_search_tree">Balanced Binary Search Tree</a>, aunque es una estructura de datos un poco compleja para acordarse de su implementación y codificarla en una entrevista.</p>
<h2>Soluciones esotéricas </h2>
<h3>Usar un bit array</h3>
<p>Si se quiere tener una performance similar a la de un array pero con un uso menor de memoria, se puede usar un <a href="http://en.wikipedia.org/wiki/Bit_array">Bit Array</a>, aunque tampoco escala bien a nivel espacio.</p>
<h3>Usar un hashtable de binary trees</h3>
<p>Esta es la ultima opción que codifique y se puede encontrar en <a href="https://github.com/emluque/Post_Stuff/blob/master/Problema_Algoritmia_Basico/Solucion/solucion-hashtable-de-binary-search-tree.c">solucion-hashtable-de-binary-search-trees.c</a>. Analizar las ventajas y desventajas de esta solución se las dejo al lector.</p>
<h4>Referencias</h4>
<p><a class="amazon" id="foundations-of-computer-science-c-edition" href="http://www.amazon.com/Foundations-Computer-Science-Principles/dp/0716782847/ref=sr_1_1?ie=UTF8&#038;qid=1316666855&#038;sr=8-1">Foundations of Computer Science: C Edition</a>; Aho, Ullman; W. H. Freeman; 1994</p>
<p><a class="amazon" href="http://www.amazon.com/Introduction-Algorithms-Thomas-H-Cormen/dp/0262033844/ref=sr_1_1?ie=UTF8&#038;qid=1330047935&#038;sr=8-1">Introduction to Algorithms, 2nd Edition</a>; Cormen, Leierson, Rivest, Stein; MIT Press; 2009</p> ]]></content:encoded>
			<wfw:commentRss>http://www.metonymie.com/apuntes/2012/02/28/problema-de-algoritmia-basico-mejoras-a-la-solucion-basica.html/feed</wfw:commentRss>
		</item>
		<item>
		<title>Problema de Algoritmia Básico: Complejidad Algorítmica</title>
		<link>http://www.metonymie.com/apuntes/2012/02/28/problema-de-algoritmia-basico-complejidad-algoritmica.html</link>
		<comments>http://www.metonymie.com/apuntes/2012/02/28/problema-de-algoritmia-basico-complejidad-algoritmica.html#comments</comments>
		<pubDate>Tue, 28 Feb 2012 23:48:51 +0000</pubDate>
		<dc:creator>admin</dc:creator>
		
		<category><![CDATA[Algoritmia]]></category>

		<category><![CDATA[c]]></category>

		<category><![CDATA[complejidad]]></category>

		<category><![CDATA[complejidad algoritmica]]></category>

		<category><![CDATA[complejidad computacional]]></category>

		<category><![CDATA[recurrencias]]></category>

		<guid isPermaLink="false">http://www.metonymie.com/apuntes/?p=49</guid>
		<description><![CDATA[Análisis de la complejidad algorítmica de la solución básica del problema presentado en el post anterior.]]></description>
			<content:encoded><![CDATA[<p>Bueno, este post continua con el problema del <a href="http://www.metonymie.com/apuntes/2012/02/28/problema-de-algoritmia-basico-solucion-basica.html">post anterior</a>, lo que voy a hacer en este post es analizar la <a href="http://es.wikipedia.org/wiki/Complejidad_computacional">Complejidad</a> del algoritmo.</p>

<h2>Codigo con anotaciones</h2>
<p>Para el análisis voy a hacer algunas anotaciones sobre el código, básicamente con comentarios sobre el mismo para dividirlo en diferentes partes. El código que se va a usar en el post es el siguiente:</p>
<pre><code>

int esta_en_lista(int value, List *pasados) {

/* B0 */
	List rList, prev;

	rList = *pasados;
	prev = NULL;

	if(rList == NULL) {
/* /B0 */
/* B01	*/
		//Agregarlo antes
		List temp;
		temp = (List) malloc(sizeof(struct Node));
		temp->value = value;
		temp->next = *pasados;
		*pasados = temp;
		return -1;
/* /B01 */
	}

/* B1 */
	while(rList != NULL) {
/* /B1 */

/* B11 */
		if(rList->value == value) return 1;
/* /B11 */
/* B12 */
		if(rList->value > value) {
/* /B12 */
/* B121 */
			List temp;
			temp = (List) malloc(sizeof(struct Node));
			temp->value = value;
			temp->next = rList;
			if(prev==NULL) {
				*pasados = temp;
			} else {
				prev->next = temp;
			}
			return -1;
/* /B121 */
		}
/* B13 */
		prev = rList;
		rList = rList->next;
/* /B13 */
	}
/* B3 */
	//Agregarlo al final
	List temp;
	temp = (List) malloc(sizeof(struct Node));
	temp->value = value;
	temp->next = NULL;
	prev->next = temp;
	return -1;
/* B3 */
}

List * sacar_duplicados(List *lista, List *pasados) {
	//Caso Base
/* A0 */
	if( (*lista) == NULL) {
/* /A0 */
/* A01 */
		return lista;
/* /A01 */
	} else {
	//PASO INDUCTIVO
/* A1 */
		if( esta_en_lista( (*lista)->value, pasados) > 0 ) {
/* /A1 */
/* A10 */
			List next = (*lista)->next;
			free( (*lista) );
			return sacar_duplicados( &#038;(next), pasados);
/* /A10 */
		} else {
/* A11 */
			(*lista)->next = *( sacar_duplicados( &#038;((*lista)->next), pasados) );
			return lista;
/* /A11 */
		}
	}
}


</code></pre> 

<p>Se ve que estan separadas en el codigo las secciones que se ejecutan independientemente. Una aclaración, <strong>jamas</strong> en la <strong>practica</strong> se hace una análisis de complejidad con el nivel de detalle que tiene este post y <strong>dudo fuertemente</strong> que fuera lo que buscaba el entrevistador (la parte intuitiva de cada caso, alcanzaria), pero ya que estoy haciendo un post y considerando que hay pocos ejemplos en castellano, lo vamos a hacer relativamente riguroso y pedagógico.</p>

<h2>Mejores Casos</h2>
<h3>Lista vacia</h3>
<p>Este caso es trivial, pero de todas maneras es importante marcarlo. Se llama a la función <code class="inline">sacar_duplicados()</code> con lista igual a <code class="inline">NULL</code>, se llega al caso base y se retorna <code class="inline">NULL</code>. No hay mucho que analizar.</p>
<h3>Lista con todos el mismo elemento</h3>
<p>Ej: [1,1,1,1,1,1,1,1,1 ...] .</p>
<p>Vamos a pensarlo intuitivamente:</p> 
<p> Básicamente lo que pasa en este caso es que en el primer llamado a <code class="inline">sacar_duplicados()</code>, <code class="inline">pasados</code> esta vacio por consiguiente en la llamada a <code class="inline">esta_en_lista()</code> se agrega el elemento a <code class="inline">pasados</code> y se devuelve <code class="inline">-1</code>.</p>
<p>Ahora, lo interesante es que en todas las sucesivas llamadas, cada vez que se llame a <code class="inline">esta_en_lista()</code> esta función va a iterar por <code class="inline">pasados</code>, encontrarlo en el primer intento y devolver <code class="inline">1</code>.</p>
<p>Es evidente que salvo para el primer elemento de la lista, para todos los demas el código va a tardar exactamente lo mismo (O sea que las llamadas a esta funcion son O(1) ). Como la función <code class="inline">sacar_duplicados()</code> pasa recursivamente por cada uno de los <em>n</em> elementos de la lista, la complejidad en este caso para todo el algoritmo es O(n).</p>
<p>Pensemoslo más detalladamente. En la primer llamada se ejecutan: A0, A1, A11, B0, B01 y la llamada recursiva al siguiente elemento. En cada una de las siguientes llamadas se ejecuta: A0 + A1 + A10 + B0 + B1 + B11 y la llamada recursiva al próximo elemento. En la ultima llamada, la que se hace cuando ya se recorrio toda la lista y se pasa el ultimo nodo que es <code class="inline">NULL</code> se ejecutan: A0 + A01 .</p>
<p>Podemos establecer una <a href=""> relación de recurrencia</a> para analizar el algoritmo pensando en cuantas veces se ejecuta cada una de las partes. Llamemos T(n+1) a la ultima llamada, T(n) a la ultima llamado con un elemento, T(n-1) a la anterior, hasta llegar a T(1) la primer llamada. Nos queda entonces, la siguiente ecuación:</p>
<pre>
T(n+1) = A0 + A01 + T(n)
T(n) = A0 + A1 + A10 + B0 + B1 + B11 + T(n-1)
T(n-1) = A0 + A1 + A10 + B0 + B1 + B11 + T(n-2)
...
T(2) = A0 + A1 + A10 + B0 + B1 + B11 + T(1)
T(1) =  A0 + A1 + A11 + B0 + B01
</pre>
<p>La relación de recurrencia es la que va de T(n) a T(2) ya que esta es la parte recurrente, por otro lado, se podría haber planteado todo el problema solo pensando en esto, ya que es aquí donde se hace la parte del procesamiento que crece al crecer el tamaño de la lista y como tal es la que afecta la complejidad del algoritmo.</p>
<p>Es evidente que de 2 a n hay n-1 llamados, entonces la relacion de recurrencia nos queda resuelta como: (n-1)(A0 + A1 + A10 + B0 + B1 + B11) . El total de toda la ecuación ( sumando T(1) y T(n+1) y llamandola f(n) ) es: f(n) = (n-1)(A0 + A1 + A10 + B0 + B1 + B11) + ( A0 + A01) + (A0 + A1 + A11 + B0 + B01) o f(n) = (n+1)(A0)+(n)(A1+B0)+(n-1)(A10+B1+B11)+A01+A11+B01 . </p>
<p>Es evidente que esta función crece linealmente, o sea que en este caso es O(n).</p>
<h3>Lista ordenada descendentemente</h3>
<p>Ej: [n, n-1, n-2, n-3, ...] .</p>
<p>Este caso es exactamente igual al anterior con la diferencia en que en cada llamado a <code class="inline">esta_en_lista()</code>  se encuentra que el elemento nuevo es menor a todos los elementos anteriores de <code class="inline">pasados</code> en la primera iteración del while y se lo agrega a la misma. De nuevo, las llamadas a esta función son O(1) y el total es O(n)</p>

<h2 id="peor-caso">Peor Caso</h2>
<h3>Lista ordenada ascendentemente</h3>
<p>Ej: [1,2,3,4,5,6 ...]</p>
<p>Bueno, este caso es bastante mas interesante. Intuitivamente, en este caso en cada llamado a <code class="inline">esta_en_lista()</code>, el while itera por cada uno de los elementos anteriores que son menores que el nuevo elemento, hasta que se termina el while y se agrega el nuevo elemento. Claramente en cada nueva llamada a la función el numero de iteraciones crece en 1. Intuitivamente podemos pensaer que como el numero de elementos de pasados es función del tamaño de la lista, esta parte es O(n) y como en <code class="inline">sacar_duplicados()</code> se itera por cada uno de los elementos de la lista el algoritmo total es O(n^2).</p>
<p>Analizemoslo más en profundidad y hagamos la recurrencia, los casos para el T(1) y T(n+1) son iguales. Luego para cada llamado en un elemento en posicion j de la lista se ejecuta (en orden): A0 + A1 + B0 + (j-1)(B1 + B11 + B12 + B13) + B3 + A10 ( que incluye la llamada recursiva al proximo elemento). La parte a ver y que es la que cambia radicalmente en relación con los casos anteriores es (j-1)(B1 + B11 + B12 + B13), que es la porcion del codigo donde se itera por cada elemento de <code class="inline">pasados</code> que preexiste al elemento actual. Esta parte ya no es <strong>constante</strong> para cada llamada a <code class="inline">esta_en_lista()</code>, efectivamente esta parte crece (linealmente) con cada nuevo llamado y luego de un suficiente numero de llamados es donde se concentrara la mayor parte de las operaciones de la función y de todo el algoritmo. Es fácil ver que para un <em>j</em> lo suficientemente grande, el resto de las operaciones se vuelve despreciable en relación al tiempo que se pasa en esta parte. Por ejemplo supongamos que j=1000, nos queda: A0 + A1 + B0 + (999)(B1 + B11 + B12 + B13) + B3 + A10 .</p>
<p>Hagamos la relación de recurrencia:</p>

<pre>
T(n+1) = A0 + A01 + T(n)
T(n) = A0 + A1 + A10 + B0 + B3 + (n-1)(B1 + B11 + B12 + B13) + T(n-1)
T(n-1) = A0 + A1 + A10 + B0 + B3 + (n-2)(B1 + B11 + B12 + B13) + T(n-2)
...
T(2) = A0 + A1 + A10 + B0 + B3 + (1)(B1 + B11 + B12 + B13) + T(1)
T(1) =  A0 + A1 + A11 + B0 + B01
</pre>
<p>Centremonos en la parte recurrente, llamemos k a (B1 + B11 + B12 + B13), l a (A0 + A1 + A10 + B0 + B3) y z a T(1) nos queda:</p>
<pre>
T(n) = l + (n-1)k + T(n-1)
T(n-1) = l + (n-2)k + T(n-2)
...
T(2) = l + (1)k + z 
</pre>
<p>Para poder pensar la recurrencia vamos a ir asignando los valores en cada paso y tratar de ver si existe algún patrón, intentemoslo:</p>
<pre>
T(2) = l + k + z
Total = l + k + z

T(3) = l + 2k + T(2)
Total = 2l + (1 + 2)k + z


T(4) = l + 3k + T(3)
Total = 3l + (1 + 2 + 3)k + z

T(5) = l + 4k + T(3)
Total = 4l + (1 + 2 + 3 + 4)k + z

...

T(n) = (n-1)l + (1 + 2 + 3 + 4 + ... + n-1)k + z
</pre>

<p>La parte del <em>k</em> es simplememnte una sumatoria hasta (n-1), existe una formula bastante básica para la misma que se puede encontrar <a href="http://www.metonymie.com/apuntes/2009/05/07/equivalencias-basicas-de-sumatorias.html"> acá </a> y existe una <a href="http://francisthemulenews.wordpress.com/2010/04/15/iii-carnaval-de-matematicas-toda-la-verdad-sobre-la-anecdota-de-gauss-el-nino-prodigio-su-profesor-y-la-suma-de-1-a-100/">anecdota</a> muy simpática sobre <a href="http://es.wikipedia.org/wiki/Gauss">Gauss</a> sobre la misma. Aplicando equivalencias de sumatoria nos queda para toda esa parte (n * (n-1))/2 = (n^2 - n)/2 = (n²)/2 - n/2.</p>
<p>Veamos entonces como nos queda la recurrencia total (llamando w a T(n+1)): f(n) = (n-1)l + ((n^2)/2)k - (n/2)k + z + w . Esta función es cuadratica y el algoritmo total nos queda O(n^2).</p>

<h2>Caso Medio</h2>
<p>Pensar sobre el caso medio es todavia mas interesante, porque antes que nada tendriamos que definir exactamente que significa el caso medio y este seria el momento en el que le preguntariamos al entrevistador sobre más precisiones para poder entender el problema. Las preguntas básicas serian:</p>
<ul>
<li>El numero de int posibles es finito?</li>
<li>Cuan ordenada esta la lista?</li>
<li>Cual es la probabilidad de que que se repitan elementos?</li>
<li>Es una distribución uniforme?</li>
</ul>
<p>Intuitivamente, cuando pense el problema originalmente, mi idea era considerando:</p> 
<p>1. si la lista no es ordenada se puede dar que en la primera aparicion de un elemento este sea mayor a x elementos que vengan despues que fueran menores en este elemento no se tendria que iterar por todos los anteriores sino solamente por una porción de estos. Ej:</p>
<pre>
[5,1,6,3,4,2,7]

Iteraciones para cada elemento y como queda <code class="inline">pasados</code>:
5 : 0
[5]

1 : 0
[1,5]

6 : 2
[1,5,6]

3 : 1
[1,3,5,6]

4 : 2
[1,3,4,5,6]

2 : 1
[1,2,3,4,5,6]

7 : 6
[1,2,3,4,5,6,7]
</pre>
<p>Se ven claramente dos cosas, el numero de iteraciones no coincide directamente ni con la posición <em>n</em> del elemento en <code class="inline">lista</code>, ni con su orden dentro de <code class="inline">pasados</code>; lo otro es que cuando un elemento es mayor a todos los elementos anteriores, si o si, se debe iterar por todos los elementos <em>n-1</em> de <code class="inline">pasados</code> y ahí si, el numero de iteraciones coincide con la posición <em>n</em> del elemento en <code class="inline">lista</code> (menos 1).</p>
<p>2. Cuando un elemento se repita, independientemente del factor de ordenamiento de la lista, solo debe iterar hasta encontrarse a si mismo dentro de la lista.</p>
<p>Considerando estas 2 cosas habia pensado <strong>ingenuamente</strong> en considerar <strong>arbitrariamente</strong> una constante <em>k</em>, llamemosla factor de atenuación por ordenamiento y otra constante <em>l</em> llamemosla factor de atenuación por duplicados; que combinadas darian una constante <em>c</em> que se podria considerar como afectando la funcion anterior quedando el algoritmo dentro de alguna funcion del tipo f(n): c * n^2; 0 &lt; c &lt; 1 o sea terminando en O(n^2). Especificamente porque quedaria en una funcion cuadratica, porque todos los casos en que un elemento sea mayor a todos los anteriores, si o si, la función <code class="inline">esta_en_lista()</code> es lineal.</p>
<p>El problema es que cuando me puse a pensar un poco mas sobre el problema para escribir el post, me surgieron algunas preguntas y honestamente es un problema mucho más complicado de lo que pensaba. Voy a escribir un <a href="http://www.metonymie.com/apuntes/2012/02/29/problema-de-algoritmia-basico-algunas-observaciones-sobre-el-peor-caso.html">cuarto post</a> con algunas de las conclusiones a las que llegue, incluyendo la muy sorprendente de que si el número de ints posibles es finito y el tamaño de la lista es mayor a este numero, la función puede ser expresada como una función lineal.</p>
<p>Pero bueno, siendo estrictos una función lineal esta acotada por una función cuadratica y por consiguiente sigue siendo O(n^2).</p>
  
<h4>Referencias</h4>
<p><a class="amazon" id="foundations-of-computer-science-c-edition" href="http://www.amazon.com/Foundations-Computer-Science-Principles/dp/0716782847/ref=sr_1_1?ie=UTF8&#038;qid=1316666855&#038;sr=8-1">Foundations of Computer Science: C Edition</a>; Aho, Ullman; W. H. Freeman; 1994</p>]]></content:encoded>
			<wfw:commentRss>http://www.metonymie.com/apuntes/2012/02/28/problema-de-algoritmia-basico-complejidad-algoritmica.html/feed</wfw:commentRss>
		</item>
		<item>
		<title>Problema de Algoritmia Básico: Primera Solución</title>
		<link>http://www.metonymie.com/apuntes/2012/02/28/problema-de-algoritmia-basico-solucion-basica.html</link>
		<comments>http://www.metonymie.com/apuntes/2012/02/28/problema-de-algoritmia-basico-solucion-basica.html#comments</comments>
		<pubDate>Tue, 28 Feb 2012 23:28:12 +0000</pubDate>
		<dc:creator>admin</dc:creator>
		
		<category><![CDATA[Algoritmia]]></category>

		<category><![CDATA[Estructuras de datos]]></category>

		<category><![CDATA[Lógica]]></category>

		<category><![CDATA[c]]></category>

		<category><![CDATA[analisis de algoritmos]]></category>

		<category><![CDATA[inducción]]></category>

		<category><![CDATA[Linked List]]></category>

		<category><![CDATA[Recursividad]]></category>

		<guid isPermaLink="false">http://www.metonymie.com/apuntes/?p=48</guid>
		<description><![CDATA[Análisis de corrección de la solución de un problema de algoritmia básico.]]></description>
			<content:encoded><![CDATA[<p>
Este es una serie de posts sobre un ejemplo básico de analisis de algoritmos. Va a constar de 3 partes: Descripción del problema y solución básica, <a href="http://www.metonymie.com/apuntes/2012/02/28/problema-de-algoritmia-basico-complejidad-algoritmica.html">Análisis de complejidad de la solución básica</a> y <a href="http://www.metonymie.com/apuntes/2012/02/28/problema-de-algoritmia-basico-mejoras-a-la-solucion-basica.html">Posibles mejoras</a>. El codigo de estos posts se puede encontrar <a href="https://github.com/emluque/Post_Stuff/tree/master/Problema_Algoritmia_Basico"> aquí </a>.
</p>
<p>
Hace unos días estuve hablando con un ex-compañero de la facultad sobre una entrevista de trabajo que había tenido en una empresa muy grande de estados unidos. Me comento muy rapidamente y sin muchos detalles sobre el problema que le habían presentado y me resultó interesante como un buen ejercicio de Algoritmia básica.
</p>

<h2> Descripción del problema</h2>

<p>
El problema consiste en dado una lista de numeros enteros, remover los duplicados de la lista. Asumí al hacer el codigo, que interesa mantener la estructura de la lista ( O sea dado una lista: [5,3,4,5,3,2]; encontrar la misma lista pero con los duplicados removidos: [5,3,4,2] ). 
</p>
<p>
Obviamente el problema no es solamente solucionar este problema, sino razonar sobre la corrección y eficiencia de la solución encontrada y ademas codificarlo en algun lenguaje ( especificamente C ).
</p>

<h2>Algunos pasos previos</h2>
<p>Antes de comenzar a trabajar en el problema, me parecio conveniente hacer algunas funciones para poder testear y probar el codigo.</p>

<h3>Struct de la lista</h3>
<p>La estructura de datos que se va a utilizar en el ejercicio</p>
<pre><code>
typedef struct Node *List;
struct Node {
  int value;
  struct Node *next;
};
</code></pre>

<h3> Función para mostrar la lista</h3>
<p>Una función para mostrar la lista por pantalla.</p>
<pre><code>
void mostrar_lista(List lista) {
	//Este primer paso es para el pretty print de la lista
	if(lista != NULL) {
		printf("%d", lista->value);
		lista = lista->next;
	}
	while(lista != NULL) {
		printf("-%d", lista->value);
		lista = lista->next;
	}
}
</code></pre>

<h3> Función para cargar una lista de prueba</h3>
<p>Una función que dado un string con una lista de enteros separados por comas, lo parsea a un linked list.</p>
<p>Esta es una versión mejorada sobre la que hice originalmente, esta versión es <em>forgiving</em> con aquellos caracteres que pueden haber sido mal tipeados por el usuario o por input malintencionado, tomando solo los caracteres que representan dígitos ( el string: &#8220;1,a2,3b,d4d,xxxx,5,,,,6,a8b0,10&#8243; se tranforma en la lista: [1,2,3,4,5,6,80,10] ). La versión original era un poco más simple y no anticipaba malas entradas.</p>  

<pre><code>
List crear_lista(List lista, char str[2000]) {
	int i=0;
	List rList = lista;

	//Es forgiving, solo lee los digitos y descarta lo demas.
	while( str[i] != &#8216;\0&#8242; ) {
		while( str[i] != &#8216;\0&#8242; &#038;&#038; (str[i] &lt; &#8216;0&#8242; || str[i] > &#8216;9&#8242;) ) i++;
		if(str[i] == &#8216;\0&#8242;) break;

		int pre = 0;
	    
		//Calcular el int
		while( str[i] != &#8216;,&#8217; &#038;&#038; str[i] != &#8216;\0&#8242; ) {
			if(!(str[i] &lt; &#8216;0&#8242; || str[i] > &#8216;9&#8242;)) { 
				pre = pre * 10;
				pre = pre + (str[i] - &#8216;0&#8242;);
			} 
			i++; 
		}
	    
		//Crear el nodo nuevo    
		List temp;
		temp = (List) malloc(sizeof(struct Node));
		temp->value = pre;
		temp->next = NULL;

		//Primera iteracion
		if(lista==NULL) {
			//Agregar nodo a lista
			lista = temp;
			//Lista de retorno
			rList = lista;
		} else {
			//Agregar nodo, mover cursor.
			lista->next = temp;
			lista = lista->next;
		} 
		i++;
	}
	return rList;
}
</code></pre>

<h4>Ejemplo de uso: </h4>
<pre><code>
	char str[2000] = &#8220;&#8221;;
	scanf(&#8221;%s&#8221;, str);
	lista = crear_lista(lista, str);
	printf(&#8221;La lista es: \n&#8221;);
	mostrar_lista(lista);
</code></pre>

<h2> Solución básica </h2>
<p>La solución básica consiste de 2 funciones. Una función <code class="inline">int esta_en_lista(int value, List *pasados)</code> que dado un entero <code class="inline">value</code> y una lista <code class="inline">pasados</code> verifica si el valor existe en la lista. Si ya existe devuelve <code class="inline">1</code>, si no, devuelve <code class="inline">-1</code> (podría haber usado constantes TRUE y FALSE como macros y el codigo seria mas bonito) y agrega este valor a la lista <code class="inline">pasados</code> en orden ascendente. La lista <code class="inline">pasados</code> se pasa por referencia ( efectivamente es un puntero a puntero ) dado que como <em>side effect</em> de la función se modifica esta lista, agregandosele ordenadamente cualquier valor que no pre-existe. O sea, en sucesivas llamadas a la funcion se va creando una lista ordenada de valores. La otra función <code class="inline">List * sacar_duplicados(List *lista, List *pasados)</code>, recibe 2 punteros a listas, la lista de enteros de la que queremos sacar duplicados y una lista donde se guardan los valores que ya existen en la lista. Esta es la función que devuelve la primer lista con los valores removidos. Esta pensado para ser llamada con <code class="inline">pasados</code> siendo una lista vacia.</p>

<pre><code>
//Version basica a ser mejorada
//Se pasa un puntero al puntero de lista para hacer los side effects
int esta_en_lista(int value, List *pasados) {

	List rList, prev;

	rList = *pasados;
	prev = NULL;

	if(rList == NULL) {
		//Agregarlo antes
		List temp;
		temp = (List) malloc(sizeof(struct Node));
		temp->value = value;
		temp->next = *pasados;
		*pasados = temp;
		return -1;
	}

	while(rList != NULL) {
		if(rList->value == value) return 1;
		if(rList->value > value) {
			List temp;
			temp = (List) malloc(sizeof(struct Node));
			temp->value = value;
			temp->next = rList;
			if(prev==NULL) {
				*pasados = temp;
			} else {
				prev->next = temp;
			}
			return -1;
		}
		prev = rList;
		rList = rList->next;
	}
	//Agregarlo al final
	List temp;
	temp = (List) malloc(sizeof(struct Node));
	temp->value = value;
	temp->next = NULL;
	prev->next = temp;
	return -1;
}



//ESTA ES LA FUNCION QUE IMPORTA EN UN PRIMER PASO
//EN UN SEGUNDO PASO LO QUE IMPORTA ES MEJORAR LA FUNCION ANTERIOR
List * sacar_duplicados(List *lista, List *pasados) {
	//Caso Base
	if( (*lista) == NULL) {
		return lista;
	} else {
	//PASO INDUCTIVO
		if( esta_en_lista( (*lista)->value, pasados) > 0 ) {
			List next = (*lista)->next;
			free( (*lista) );
			return sacar_duplicados( &#038;(next), pasados);
		} else {
			(*lista)->next = *( sacar_duplicados( &#038;((*lista)->next), pasados) );
			return lista;
		}
	}
}
</code></pre>

<p>Se puede testear el programa compilando y corriendo <em>solucion-basica.c</em> .</p>

<h2>Razonando sobre la corrección del algoritmo</h2>

<h3>Primera Parte</h3>
<p>Lo primero que habria que considerar es si la función <code class="inline">esta_en_lista()</code> es correcta. No voy a hacer un análisis muy formal, pero supongamos los posibles casos:</p>
<ul>
	<li><strong><code class="inline">pasados</code> es una lista vacia:</strong> Se ejecuta el primer if: <code class="inline"> if(rList == NULL) </code>, se crea un nodo nuevo que se agrega a <code class="inline">pasados</code> y se devuelve <code class="inline">-1</code>.</li>
	<li><strong><code class="inline">pasados</code> no es una lista vacia y el valor existe en la lista:</strong>, se ejecuta el primer if y devuelve falso, se ejecuta la sentencia: <code class="inline"> while(rList != NULL) </code>, se itera por todos los nodos hasta que se encuentra el nodo en el que pre-existe el valor, se llega a la condición: <code class="inline">if(rList->value == value)</code> y se devuelve <code class="inline">1</code>.</li> 
	<li><strong><code class="inline">pasados</code> no es una lista vacia, el valor no existe en la lista y es menor a cualquier valor de la lista:</strong> se llega al while, en la primera iteración se llega a la condición: <code class="inline">if(rList->value > value)</code>, se crea el nuevo nodo, se le agrega el resto de la lista como nodo hijo y se evalua la condición: <code class="inline">if(prev==NULL)</code>, como <code class="inline">prev</code> fue inicializado a <code class="inline">NULL</code> y es la primera iteración, se asigna este nuevo nodo a <code class="inline">pasados</code> y se devuelve <code class="inline">-1</code>.</li>
	<li><strong><code class="inline">pasados</code> no es una lista vacia, el valor no existe en la lista y esta entre medio de 2 valores de la lista:</strong> se llega al while, se itera por los nodos hasta que se llega a la condición: <code class="inline">if(rList->value > value)</code>, se crea el nuevo nodo, se le agrega el resto de la lista como nodo hijo y se evalua la condición: <code class="inline">if(prev==NULL)</code>, como <code class="inline">prev</code> esta apuntando al nodo pàdre del actual, se asigna este nuevo nodo como hijo de <code class="inline">prev</code> y se devuelve <code class="inline">-1</code>.</li>
	<li><strong><code class="inline">pasados</code> no es una lista vacia, el valor no existe en la lista y es mayor a cualquier valor de la lista:</strong> se llega al while, se itera por los nodos hasta que se recorre toda la lista y se sale del while, se crea el nuevo nodo, se le agrega null como nodo hijo y se evalua la condición, se asigna este nuevo nodo como hijo de <code class="inline">prev</code> (que en este momento es el ultimo nodo de la lista) y se devuelve <code class="inline">-1</code>.</li>
</ul>

<p>Razonar sobre la corrección del valor que devuelve (<code class="inline">1</code> o <code class="inline">-1</code>) es trivial. Asi como razonar que no agrega un valor que ya existe.</p>
<p>Lo que es importante ver es que si se llama a la función con una lista <strong>ordenada</strong> y un valor que no existe en la misma, por los 3 ultimos casos (y el primero siendo estrictos, ya que una lista vacia es una lista ordenada), a esta lista se le agrega el valor en orden.</p>

<h3>Segunda parte</h3>
<p>Asumiendo que la función anterior es correcta y que la función <code class="inline">esta_en_lista()</code> devuelve un valor correcto y asi mismo que en sucesivas llamadas a la lista <code class="inline">pasados</code> se guardan en esta todos los elementos anteriores, podemos razonar sobre la función <code class="inline">sacar_duplicados()</code> que es la que nos importa.</p>
<p>La función es recursiva, por lo que para pensar sobre su corrección alcanza con hacer <a href="http://en.wikipedia.org/wiki/Mathematical_Induction">induccion</a> para demostrar que la función retorna la misma lista sin duplicados. Voy a hacer un esquema del razonamiento que nos llevaria a una demostracion.</p>
<h4>Paso base</h4>
<p>El paso base es que la lista esta vacia. En ese caso se retorna una lista vacia, como una lista vacia es una lista sin duplicados, no hay nada más que demostrar. El codigo que soporta este caso es:</p>
<pre><code>
	//Caso Base
	if( (*lista) == NULL) {
		return lista;
	} 
</code></pre>
<h4>Paso Inductivo</h4>
<p>Ahora tenemos que demostrar que si llamamos a la funcion con una lista con <em>n</em> elementos sin duplicados entonces llamando a la función con la lista con <em>n+1</em> elementos tambien se retorna una lista sin duplicados.</p>
<p>Para razonar sobre esto alcanza pensar que en el caso en que se llama a la función con <em>(n+1)</em> elementos, se llega a la siguiente porción de codigo:</p>
<pre><code>
		if( esta_en_lista( (*lista)->value, pasados) > 0 ) {
			List next = (*lista)->next;
			free( (*lista) );
			return sacar_duplicados( &#038;(next), pasados);
		} else {
			(*lista)->next = *( sacar_duplicados( &#038;((*lista)->next), pasados) );
			return lista;
		}
</code></pre>
<p>Analizemos los dos casos:</p>
<ul>
<li>Si el valor <em>(n+1)</em> ya existe en la lista, se devuelve la lista hasta <em>n</em>. Como por la hipótesis inductiva, la lista hasta <em>n</em> es una lista sin duplicados y se devuelve esta, la lista que se devuelve en la llamado con <em>n+1</em> elementos es así mismo una lista sin duplicados.</li>
<li>Si el valor <em>(n+1)</em> no existe en la lista, se devuelve la lista hasta <em>n</em> mas el elemento <em>n+1</em>. Como por la hipótesis inductiva, la lista hasta <em>n</em> es una lista sin duplicados y por asunción de caso el elemento nuevo no existe en la lista hasta <em>n</em>, la lista que se devuelve en la llamado con <em>n+1</em> elementos es una lista sin duplicados.</li>  
</ul>
<p>Por exhaustación de casos, se comprueba que se cumple el paso inductivo.</p>


<h2>Algunas observaciones sobre el algoritmo</h2>
<p>El proximo paso y va ser el siguiente post es analizar la complejidad del algoritmo y despues buscar formas de mejorarlo. Como observaciones basicas se puede ver que llamando a la función <code class="inline">sacar_duplicados()</code> con una lista con n elementos, se itera (recursivamente) por cada uno de los n elementos de la lista, por otro lado la lista <code class="inline">pasados</code> va creciendo con cada llamado a <code class="inline">esta_en_lista()</code> que tiene un elemento nuevo y en todo caso de que este elemento sea mayor a cualquiera de los <em>j</em> elementos anteriores de <code class="inline">pasados</code>, <code class="inline">esta_en_lista()</code> itera por cada uno de los <em>j</em> elementos de <code class="inline">pasados</code>. Es pensando en estos temas que vamos a encarar el siguiente post.</p>

<h4>Referencias</h4>
<p><a class="amazon" id="foundations-of-computer-science-c-edition" href="http://www.amazon.com/Foundations-Computer-Science-Principles/dp/0716782847/ref=sr_1_1?ie=UTF8&#038;qid=1316666855&#038;sr=8-1">Foundations of Computer Science: C Edition</a>; Aho, Ullman; W. H. Freeman; 1994</p>]]></content:encoded>
			<wfw:commentRss>http://www.metonymie.com/apuntes/2012/02/28/problema-de-algoritmia-basico-solucion-basica.html/feed</wfw:commentRss>
		</item>
		<item>
		<title>Recuento: Permutaciones y Selecciones ( Formulas básicas)</title>
		<link>http://www.metonymie.com/apuntes/2009/08/26/recuento-permutaciones-y-selecciones-formulas-basica.html</link>
		<comments>http://www.metonymie.com/apuntes/2009/08/26/recuento-permutaciones-y-selecciones-formulas-basica.html#comments</comments>
		<pubDate>Wed, 26 Aug 2009 21:09:00 +0000</pubDate>
		<dc:creator>admin</dc:creator>
		
		<category><![CDATA[Apuntes]]></category>

		<category><![CDATA[Combinatoria]]></category>

		<category><![CDATA[números combinatorios]]></category>

		<category><![CDATA[Permutaciones]]></category>

		<category><![CDATA[selecciones]]></category>

		<guid isPermaLink="false">http://www.metonymie.com/apuntes/?p=47</guid>
		<description><![CDATA[Machete de Formulas básicas de recuento. Permutaciones y Combinaciones.]]></description>
			<content:encoded><![CDATA[<h2 >Principios B&aacute;sicos</h2>


<h3 >Principio de Multiplicaci&oacute;n (Regla del producto)</h3>

<p >Sean A<sub>1</sub>,A<sub>2</sub>,&#8230;, A<sub>n</sub> conjuntos finitos, entonces |A<sub>1</sub> x A<sub>2</sub> x &#8230; x A<sub>n</sub>| = |A<sub>1</sub>| . |A<sub>2</sub>|&nbsp;&nbsp;&#8230;.&nbsp;&nbsp;. |A<sub>n</sub>|</p>

<p >Ej: <br />Tomemos 2 conjuntos:<br />A = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 } ; | A | = 10<br />B = { a, b, c, d, e, f } ; | B | = 5</p>

<p >Supongamos que quiero saber todos las posibles conjuntos formados por 1 elemento de A y otro de B, por ej: { 3, b }, { a, 2 }.</p>

<p >O visto desde lenguajes formales cual es la cantidad de posibles cadenas formadas por primero un elemento de A y después un elemento de B, Ej: &#8220;3b&#8221;, &#8220;2a&#8221;, &#8220;9c&#8221;, &#8220;3c&quot;.</p>

<p >Existen 10 elementos en A y 5 en B por lo que la soluci&oacute;n es: | A | x | B |&nbsp;&nbsp;= 50</p>



<h3 >Principio de Adici&oacute;n (Regla de la Suma)</h3>

<p >Sean A<sub>1</sub>,A<sub>2</sub>,&#8230;, A<sub>n</sub> conjuntos finitos y disjuntos, entonces |A<sub>1</sub> &#8746; A<sub>2</sub> &#8746; &#8230; &#8746; A<sub>n</sub>| = |A<sub>1</sub>| + |A<sub>2</sub>| +&nbsp;&nbsp;&#8230;.&nbsp;&nbsp;+ |A<sub>n</sub>|</p>

<p >Ej:</p>

<p >Supongamos que tengo que elegir un elemento de A o de B (del ejemplo anterior). Como |A| = 10 y |B| = 5 y A &#8745; B = &#8709; entonces |A &#8746; B| = 15, o sea que existen 15 posibles elementos que puedo elegir.</p>



<h3 >Principio de Exclusi&oacute;n y Exclusi&oacute;n</h3>

<p >Si A<sub>1</sub>, A<sub>2</sub> son conjuntos finitos pero no disjuntos. Entonces |A &#8746; B| = |A| + |B| - |A &#8745; B|<br />Esto se generaliza para la uni&oacute;n de varios conjuntos. </p>



<h2 >Permutaciones</h2>


<h3 >Permutaciones</h3>

<p >Supongamos que quiero calcular la cardinalidad del conjunto G formado por las cadenas de 3 caracteres formadas por elementos de C= {n, m, p} tales que no se repita nunca ningún elemento: { &#8220;nmp&#8221;, &#8220;npm&#8221;, &#8220;mnp&#8221;, &#8220;mpn&#8221;, &#8220;pmn&#8221;, &#8220;pnm&#8221; }. <br />Por el principio de multiplicaci&oacute;n, Existen 3 posibilidades para el primero, luego como no se repiten 2 para el segundo y 1 para el tercero. <br />| G | = 3! = 6</p>

<p ><b>La forma de poner n objetos en ord&eacute;n es: P(n, n) = n!</b></p>



<h3 >R-Permutaciones </h3>

<p >Supongamos ahora que quiero saber la cardinalidad del conjunto H formado por todas las cadenas de 3 car&aacute;cteres formadas por elementos de A= { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 } sin que se repita nunca ning&uacute;n elemento. Hay 10 posibilidades para el primer espacio, 9 para el segundo y 8 para el tercero, por lo que:</p>

<p >| H | = <math xmlns='http://www.w3.org/1998/Math/MathML'
    
    
    display='inline'>

 <mfrac>
  <mrow>
   <mn>10</mn>
   <mo>!</mo>
  </mrow>
  <mrow>
   <mn>7</mn>

   <mo>!</mo>
  </mrow>
 </mfrac>
</math></p>

<p ><b>El n&uacute;mero de permutaciones de r objetos de n en ord&eacute;n pero sin repetici&oacute;n es: P(n, r) = </b><b><math xmlns='http://www.w3.org/1998/Math/MathML'
    
    
    display='inline'>
 <mfrac>
  <mrow>

   <mi>n</mi>
   <mo>!</mo>
  </mrow>
  <mrow>
   <mo>(</mo>
   <mrow>
    <mi>n</mi>

    <mo>-</mo>
    <mrow>
     <mi>r</mi>
     <mo>!</mo>
    </mrow>
   </mrow>
   <mo>)</mo>

  </mrow>
 </mfrac>
</math></b></p>



<h3 >Permutaciones con repetici&oacute;n</h3>

<p >Supongamos que quiero saber la cardinalidad del conjunto formado por todas las cadenas de 3 caracteres formadas por elementos de A = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 } en las que se puedan repetir los elementos. Por la regla del producto existen 10 posibilidades para el primer carácter, 10 para el segundo y 10 para el tercero, por lo que el resultado es 10<sup>3</sup>.</p>

<p ><b>El n&uacute;mero de permutaciones de r objetos de n en ord&eacute;n con repetici&oacute;n es: n</b><sup>r</sup></p>




<h2 >Selecciones</h2>


<h3 >r-combinaciones</h3>

<p >Una <b>r-combinaci&oacute;n</b> de elementos de un conjunto es una selecci&oacute;n sin ordenar de r elementos del conjunto, es decir, de subconjuntos de r elementos.</p>

<p ><b>El n&uacute;mero de r-combinaciones de n elementos, donde n es un entero no negativo y r es un entero tal que 0 &lt;= r &lt;= n es: C(n, r) = </b><b><math xmlns='http://www.w3.org/1998/Math/MathML'
    
    
    display='inline'>
 <mrow>
  <mrow>

   <mo>(</mo>
   <mfrac linethickness='0'>
    <mi>n</mi>
    <mi>r</mi>
   </mfrac>
   <mo>)</mo>
  </mrow>

  <mo>=</mo>
 </mrow>
</math></b><b><math xmlns='http://www.w3.org/1998/Math/MathML'
    
    
    display='inline'>
 <mfrac>
  <mrow>
   <mi>n</mi>
   <mo>!</mo>
  </mrow>

  <mrow>
   <mrow>
    <mi>r</mi>
    <mo>!</mo>
   </mrow>
   <mo> . </mo>
   <mrow>
    <mrow>

     <mo>(</mo>
     <mrow>
      <mi>n</mi>
      <mo>-</mo>
      <mi>r</mi>
     </mrow>
     <mo>)</mo>

    </mrow>
    <mo>!</mo>
   </mrow>
  </mrow>
 </mfrac>
</math></b><br />Ej:</p>

<p >Supongamos que quiero calcular la cardinalidad del conjunto J formado por todos los posibles subconjuntos de 2 elementos que contienen elementos de D = {w, x, y, z},&nbsp;&nbsp;C(4,2) = 6, ya que J = { {w, x}, {w,y} , {w,z}, {x, y}, {x, z}, {y, z}}. </p>



<h3 >Relaci&oacute;n entre r-permutaciones y r-combinaciones</h3>

<p >Las r-permutaciones de un conjunto de cardinalidad n se pueden obtener formando las C(n,r) r-combinaciones del conjunto ( o sea todas los posibles subconjuntos de tama&ntilde;o r formados por los n elementos del conjunto ) y luego ordenando estos elementos de todas las formas posibles, como cada subconjunto tiene tama&ntilde;o r esto puede hacerse de P(r, r) formas de lo que sigue que:</p>

<p >P(n, r) = C(n, r) . P(r, r)</p>



<h3 >Selecciones con repetici&oacute;n</h3>

<p ><b>En un conjunto con n elementos hay C(n + r - 1, r) combinaciones con repetici&oacute;n.</b></p>

<p >Ej:</p>

<p >Supongamos que en un grupo de monedas hay 3 tipos distintos de 1, 5 y 10 centavos y hay por lo menos 5 de cada una de ellas. Cuantos posibles combinaciones de 5 monedas es posible extraer de este grupo?&nbsp;&nbsp;Es importante recalcar que las monedas de cada valor son indistinguibles entre si&nbsp;&nbsp;y no importa el orden en que se seleccionan las monedas, por lo que hay 5 combinaciones con repetici&oacute;n de 3 elementos. <br />Supongamos que cuando elejimos las monedas las metemos en una caja con 3 compartimentos, 1 para cada&nbsp;&nbsp;tipo de moneda, entre cada compartimento hay una barra que las separa, de esta manera algunas de las posibles combinaciones podrian representarse como:<br />** | * | ** <br />* | | ****<br />|*****|<br />***|*|*<br />&#8230;. etc.<br />Se ve que el numero de seleccionar 5 monedas de entre 3 tipos distintos corresponde al numero de formas de colocar 2 barras y 5 asteriscos. Por lo que el n&uacute;mero de formas de seleccionar las monedas es equivalente al n&uacute;mero de formas de seleccionar 5 asteriscos de entre 7 posiciones posibles, que se puede hacer de C(7, 5) maneras.</p>




<h2 >Equivalencias de N&uacute;meros Combinatorios</h2>

<p class="Equation"><math xmlns='http://www.w3.org/1998/Math/MathML'
    
    
    display='block'>
 <mrow>
  <mrow>
   <mo>(</mo>
   <mfrac linethickness='0'>

    <mn>0</mn>
    <mn>0</mn>
   </mfrac>
   <mo>)</mo>
  </mrow>
  <mo>=</mo>
  <mrow>

   <mfrac>
    <mrow>
     <mn>0</mn>
     <mo>!</mo>
    </mrow>
    <mrow>
     <mrow>
      <mn>0</mn>

      <mo>!</mo>
     </mrow>
     <mo> . </mo>
     <mrow>
      <mn>0</mn>
      <mo>!</mo>
     </mrow>

    </mrow>
   </mfrac>
   <mo>=</mo>
   <mn>1</mn>
  </mrow>
 </mrow>
</math></p>

<p class="Equation"><math xmlns='http://www.w3.org/1998/Math/MathML'
    
    
    display='block'>

 <mrow>
  <mrow>
   <mo>(</mo>
   <mfrac linethickness='0'>
    <mi>n</mi>
    <mn>0</mn>
   </mfrac>

   <mo>)</mo>
  </mrow>
  <mo>=</mo>
  <mrow>
   <mfrac>
    <mrow>
     <mi>n</mi>

     <mo>!</mo>
    </mrow>
    <mrow>
     <mrow>
      <mn>0</mn>
      <mo>!</mo>
     </mrow>

     <mo> . </mo>
     <mrow>
      <mi>n</mi>
      <mo>!</mo>
     </mrow>
    </mrow>
   </mfrac>
   <mo>=</mo>

   <mn>1</mn>
  </mrow>
 </mrow>
</math></p>

<p class="Equation"><math xmlns='http://www.w3.org/1998/Math/MathML'
    
    
    display='block'>
 <mrow>
  <mrow>
   <mo>(</mo>

   <mfrac linethickness='0'>
    <mi>n</mi>
    <mn>1</mn>
   </mfrac>
   <mo>)</mo>
  </mrow>
  <mo>=</mo>

  <mrow>
   <mfrac>
    <mrow>
     <mi>n</mi>
     <mo>!</mo>
    </mrow>
    <mrow>
     <mrow>

      <mn>1</mn>
      <mo>!</mo>
     </mrow>
     <mo> . </mo>
     <mrow>
      <mi>n</mi>
      <mo>!</mo>

     </mrow>
    </mrow>
   </mfrac>
   <mo>=</mo>
   <mn>1</mn>
  </mrow>
 </mrow>
</math></p>

<p class="Equation"><math xmlns='http://www.w3.org/1998/Math/MathML'
    
    
    display='block'>
 <mrow>
  <mrow>
   <mo>(</mo>
   <mfrac linethickness='0'>
    <mi>n</mi>
    <mi>n</mi>

   </mfrac>
   <mo>)</mo>
  </mrow>
  <mo>=</mo>
  <mrow>
   <mfrac>
    <mrow>
     <mi>n</mi>

     <mo>!</mo>
    </mrow>
    <mrow>
     <mrow>
      <mn>0</mn>
      <mo>!</mo>
     </mrow>

     <mo> . </mo>
     <mrow>
      <mi>n</mi>
      <mo>!</mo>
     </mrow>
    </mrow>
   </mfrac>
   <mo>=</mo>

   <mn>1</mn>
  </mrow>
 </mrow>
</math></p>




<h3 >N&uacute;meros combinatorios complentarios</h3>

<p class="Equation"><math xmlns='http://www.w3.org/1998/Math/MathML'
    
    
    display='block'>

 <mrow>
  <mrow>
   <mrow>
    <mo>(</mo>
    <mfrac linethickness='0'>
     <mi>n</mi>
     <mi>k</mi>

    </mfrac>
    <mo>)</mo>
   </mrow>
   <mo>=</mo>
   <mrow>
    <mfrac>
     <mrow>
      <mi>n</mi>

      <mo>!</mo>
     </mrow>
     <mrow>
      <mrow>
       <mi>k</mi>
       <mo>!</mo>
      </mrow>

      <mo> . </mo>
      <mrow>
       <mrow>
        <mo>(</mo>
        <mrow>
         <mi>n</mi>
         <mo>-</mo>

         <mi>k</mi>
        </mrow>
        <mo>)</mo>
       </mrow>
       <mo>!</mo>
      </mrow>
     </mrow>

    </mfrac>
    <mo>=</mo>
    <mtext>  </mtext>
    <mrow>
     <mfrac>
      <mrow>
       <mi>n</mi>

       <mo>!</mo>
      </mrow>
      <mrow>
       <mrow>
        <mrow>
         <mo>(</mo>
         <mrow>
          <mi>n</mi>

          <mo>-</mo>
          <mi>k</mi>
         </mrow>
         <mo>)</mo>
        </mrow>
        <mo>!</mo>
       </mrow>

       <mo> . </mo>
       <mrow>
        <mi>k</mi>
        <mo>!</mo>
       </mrow>
      </mrow>
     </mfrac>
     <mo>=</mo>

     <mrow>
      <mo>(</mo>
      <mfrac linethickness='0'>
       <mi>n</mi>
       <mrow>
        <mi>n</mi>
        <mo>-</mo>

        <mi>k</mi>
       </mrow>
      </mfrac>
      <mo>)</mo>
     </mrow>
    </mrow>
   </mrow>
  </mrow>

  <mspace linebreak='indentingnewline'/>
  <mspace linebreak='indentingnewline'/>
  <mspace linebreak='indentingnewline'/>
 </mrow>
</math></p>




<h2 >Permutaciones con Objetos Indistinguibles y Distribuciones de Objetos en Cajas</h2>

<p >Ej 4.5.8 de <a href="#matematica-discreta-rosen">MD</a><br />&iquest;Cuantas permutaciones se pueden realizar con las letras de la palabra &#8220;PAPAYA&uml;? . No se puede usar la formula general de Permutaciones porque la letra &#8220;A&#8221; se repite 3 veces y la letra &#8220;P&#8221; se repite 2. Por lo que debe pensarse as&iacute;: se pueden seleccionar las posiciones de las 3 letras &#8220;A&#8221; de C(6, 3) formas distintas, las &#8220;P&#8221; se pueden colocar en C(3,2) formas distinras quedando C(1, 1) para la &#8220;Y&#8221;. Por la regla del Producto queda:<br />C(6,3) . C(3, 2) . C(1,1) =&nbsp;&nbsp;<math xmlns='http://www.w3.org/1998/Math/MathML'
    
    
    display='inline'>

 <mfrac>
  <mrow>
   <mn>6</mn>
   <mo>!</mo>
  </mrow>
  <mrow>
   <mrow>
    <mn>3</mn>

    <mo>!</mo>
   </mrow>
   <mo> . </mo>
   <mrow>
    <mn>3</mn>
    <mo>!</mo>
   </mrow>

  </mrow>
 </mfrac>
</math>. <math xmlns='http://www.w3.org/1998/Math/MathML'
    
    
    display='inline'>
 <mfrac>
  <mrow>
   <mn>3</mn>
   <mo>!</mo>
  </mrow>

  <mrow>
   <mrow>
    <mn>2</mn>
    <mo>!</mo>
   </mrow>
   <mo> . </mo>
   <mrow>
    <mn>1</mn>

    <mo>!</mo>
   </mrow>
  </mrow>
 </mfrac>
</math>. 1 = <math xmlns='http://www.w3.org/1998/Math/MathML'
    
    
    display='inline'>
 <mfrac>
  <mrow>
   <mn>6</mn>

   <mo>!</mo>
  </mrow>
  <mrow>
   <mrow>
    <mn>3</mn>
    <mo>!</mo>
   </mrow>

   <mo> . </mo>
   <mrow>
    <mn>2</mn>
    <mo>!</mo>
   </mrow>
  </mrow>
 </mfrac>
</math>&nbsp;&nbsp;= 60</p>

<p ><b>El n&uacute;mero de permutaciones diferentes de n objetos; donde hay n<sub>1</sub>objetos indistinguibles de tipo 1, n<sub>2</sub>
objetos indistinguibles de tipo 2, &#8230;, n<sub>k</sub>
objetos indistinguibles de tipo k; es:<br /></b><b><math xmlns='http://www.w3.org/1998/Math/MathML'
    
    
    display='inline'>
 <mfrac>
  <mrow>
   <mi>n</mi>
   <mo>!</mo>
  </mrow>
  <mrow>

   <mrow>
    <mrow>
     <mrow>
      <msub>
       <mi>n</mi>
       <mn>1</mn>
      </msub>
      <mo>!</mo>

     </mrow>
     <mo> . </mo>
     <mrow>
      <msub>
       <mi>n</mi>
       <mn>2</mn>
      </msub>
      <mo>!</mo>

     </mrow>
    </mrow>
    <mo>&#8230;</mo>
   </mrow>
   <mo>.</mo>
   <mrow>
    <msub>
     <mi>n</mi>

     <mi>k</mi>
    </msub>
    <mo>!</mo>
   </mrow>
  </mrow>
 </mfrac>
</math></b></p>

<p >Ej 4.5.9 de <a href="#matematica-discreta-rosen">MD</a><br />&iquest;De cu&aacute;ntas formas se pueden distribuir a cuatro jugadores manos de 5 cartas usando una baraja de 5 cartas? . Observese que al primero se le pueden distribuir C(52, 5), al segundo C(47, 5), al terceso C(42, 5) y al cuarto C(37, 5) (Quedando 32 cartas en el maso como una 5ta clase posible). Por la regla del producto queda:<br />C(52, 5) . C(47, 5) . C(42, 5) . C(37, 5) = <math xmlns='http://www.w3.org/1998/Math/MathML'
    
    
    display='inline'>

 <mfrac>
  <mrow>
   <mn>52</mn>
   <mo>!</mo>
  </mrow>
  <mrow>
   <mrow>
    <mn>47</mn>

    <mo>!</mo>
   </mrow>
   <mo> . </mo>
   <mrow>
    <mn>5</mn>
    <mo>!</mo>
   </mrow>

  </mrow>
 </mfrac>
</math>. <math xmlns='http://www.w3.org/1998/Math/MathML'
    
    
    display='inline'>
 <mfrac>
  <mrow>
   <mn>47</mn>
   <mo>!</mo>
  </mrow>

  <mrow>
   <mrow>
    <mn>42</mn>
    <mo>!</mo>
   </mrow>
   <mo> . </mo>
   <mrow>
    <mn>5</mn>

    <mo>!</mo>
   </mrow>
  </mrow>
 </mfrac>
</math><math xmlns='http://www.w3.org/1998/Math/MathML'
    
    
    display='inline'>
 <mfrac>
  <mrow>
   <mn>42</mn>

   <mo>!</mo>
  </mrow>
  <mrow>
   <mrow>
    <mn>47</mn>
    <mo>!</mo>
   </mrow>

   <mo> . </mo>
   <mrow>
    <mn>5</mn>
    <mo>!</mo>
   </mrow>
  </mrow>
 </mfrac>
</math>. <math xmlns='http://www.w3.org/1998/Math/MathML'
    
    
    display='inline'>

 <mfrac>
  <mrow>
   <mn>37</mn>
   <mo>!</mo>
  </mrow>
  <mrow>
   <mrow>
    <mn>32</mn>

    <mo>!</mo>
   </mrow>
   <mo> . </mo>
   <mrow>
    <mn>5</mn>
    <mo>!</mo>
   </mrow>

  </mrow>
 </mfrac>
</math>=&nbsp;&nbsp;<math xmlns='http://www.w3.org/1998/Math/MathML'
    
    
    display='inline'>
 <mfrac>
  <mrow>
   <mn>52</mn>
   <mo>!</mo>
  </mrow>

  <mrow>
   <mrow>
    <mn>5</mn>
    <mo>!</mo>
   </mrow>
   <mo> . </mo>
   <mrow>
    <mn>5</mn>

    <mo>!</mo>
   </mrow>
   <mo> . </mo>
   <mrow>
    <mn>5</mn>
    <mo>!</mo>
   </mrow>

   <mo> . </mo>
   <mrow>
    <mn>5</mn>
    <mo>!</mo>
   </mrow>
   <mo> . </mo>
   <mrow>
    <mn>32</mn>

    <mo>!</mo>
   </mrow>
  </mrow>
 </mfrac>
</math></p>

<p ><b>El n&uacute;mero de formas de distribuir n objetos distinguibles en k cajas distinguibles de forma que en la caja i-&eacute;sima haya n</b><sub>i</sub> <b>objetos, i = 1,2, &#8230;, k; es:<br /></b><b><math xmlns='http://www.w3.org/1998/Math/MathML'
    
    
    display='inline'>

 <mfrac>
  <mrow>
   <mi>n</mi>
   <mo>!</mo>
  </mrow>
  <mrow>
   <mrow>
    <mrow>

     <mrow>
      <msub>
       <mi>n</mi>
       <mn>1</mn>
      </msub>
      <mo>!</mo>
     </mrow>

     <mo> . </mo>
     <mrow>
      <msub>
       <mi>n</mi>
       <mn>2</mn>
      </msub>
      <mo>!</mo>

     </mrow>
    </mrow>
    <mo>&#8230;</mo>
   </mrow>
   <mo>.</mo>
   <mrow>
    <msub>
     <mi>n</mi>

     <mi>k</mi>
    </msub>
    <mo>!</mo>
   </mrow>
  </mrow>
 </mfrac>
</math></b></p>

<h3>Referencias</h3>
<p><a class="amazon" id="matematica-discreta-rosen" href="http://www.amazon.com/Discrete-Mathematics-Applications-Kenneth-Rosen/dp/0073229725/ref=sr_1_1?ie=UTF8&#038;s=books&#038;qid=1251323778&#038;sr=8-1">Matemática Discreta y sus aplicaciones</a>; Rosen, Kenneth H.; 5ª edición, Mc Graw Hill, 2004</p>
<p><a class="amazon" href="http://www.amazon.com/Algebra-I-Armando-Rojo/dp/9872144885/">Algebra I</a>; Rojo, Armando; 21ª ed, Magister Eos; 2006</p>
]]></content:encoded>
			<wfw:commentRss>http://www.metonymie.com/apuntes/2009/08/26/recuento-permutaciones-y-selecciones-formulas-basica.html/feed</wfw:commentRss>
		</item>
		<item>
		<title>Usando linked lists para trabajar con polinomios</title>
		<link>http://www.metonymie.com/apuntes/2009/08/13/usando-linked-lists-para-trabajar-con-polinomios.html</link>
		<comments>http://www.metonymie.com/apuntes/2009/08/13/usando-linked-lists-para-trabajar-con-polinomios.html#comments</comments>
		<pubDate>Thu, 13 Aug 2009 17:32:15 +0000</pubDate>
		<dc:creator>admin</dc:creator>
		
		<category><![CDATA[Estructuras de datos]]></category>

		<category><![CDATA[c]]></category>

		<category><![CDATA[computación simbolica]]></category>

		<category><![CDATA[Linked List]]></category>

		<category><![CDATA[polinomios]]></category>

		<guid isPermaLink="false">http://www.metonymie.com/apuntes/?p=46</guid>
		<description><![CDATA[Un ejemplo de cómputos simbólicos usando una linked list para manipular polinomios.]]></description>
			<content:encoded><![CDATA[<p>Uno de las cosas fascinantes del <a class="wiki" href="http://en.wikipedia.org/wiki/Mathematica">Mathematica</a> y ( ahora del <a href="http://www.wolframalpha.com/">wolfram alpha</a> ) es como resuelve y además simplifica expresiones matemáticas (por ejemplo: derivando funciones y dando cada uno de los pasos). Se usa de esta manera la computadora no para hacer cálculos numéricos ( que arrojen como resultado un número ) sino para computación simbólica ( cálculos que arrojan como resultado una expresión matemática ). </p>

<p>La pregunta es como una maquina que guarda resultados en memoria en forma de expresiones binarias ( esencialmente números, aunque pueden representar otra cosa ) para hacer esto. La respuesta es que la representación de formulas matemáticas puede ser realizada a través de diferentes estructuras de datos y es manipulando estas estructuras que se pueden realizar estos cómputos.</p>

<p>Como un ejemplo básico de esto acá va un ejemplo sacado de <a class="amazon" href="http://www.amazon.com/Data-Structures-Electrical-Computer-Engineering/dp/1584503386/">C &amp; Data Structures</a> sobre como se pueden hacer operaciones sobre polinomios.</p>

<h2>Una estructura de datos para guardar un polinomio y las funciones para llenarlo y mostrarlo.</h2>

<pre><code>

# include &lt;stdio.h>

# include &lt;stdlib.h>

struct pnode

   {

      int exp;

      int coeff;

      struct pnode *link;

   };

struct pnode *insert(struct pnode *p, int e,int c) {

   struct pnode *temp;

   temp = p;

   if(p==NULL) {

      p=(struct pnode *)malloc(sizeof(struct pnode));

      if(p==NULL) {

         printf("Error de alocación de memoria\n");

         exit(0);

      }

      p-> exp = e;

      p->coeff = c;

      p-> link = NULL;

   } else {

        //Si tienen el mismo exponente se suman los coeficientes en vez de agregar un nodo.

       if(p->exp == e) {

            p->coeff = p->coeff + c;

       } else {

            p->link = insert(p->link, e, c);

       }

   }

   return (p);

}



void printpoly ( struct pnode *p ) {

      printf("The polynomial is\n");

      while (p!= NULL) {

        if(p->exp != 1) {

            printf("%dx^%d\t", p->coeff, p-> exp);

        } else {

            printf("%dx\t", p->coeff);

        }

        p = p-> link;

      }

      printf("\n");

}

</code></pre>

<p><strong>Nota:</strong> en la función de insertar si un nuevo termino tiene el mismo exponente que uno anterior se lo suma en vez de agregarlo. Esto es una de las modificaciones al código del libro.</p>

<h2>Ordenando el polinomio</h2>

<pre><code>

/* a function to sort a list */

struct pnode *sortpoly(struct pnode *p)

{

   struct pnode *temp1,*temp2,*max,*prev,*q;

   q = NULL;

   while(p != NULL) {

      prev = NULL;

      max = temp1 = p;

      temp2 = p -> link;

      while ( temp2 != NULL ) {

         if(max -> exp &lt; temp2 -> exp) {

            max = temp2;

            prev = temp1;

         }

         temp1 = temp2;

         temp2 = temp2-> link;

      }

      if(prev == NULL) {

            p = max -> link;

       }  else {

            prev -> link = max -> link;

       }

       max -> link = NULL;

        if( q == NULL) {

            q = max;

        } else {

            temp1 = q;

            while( temp1 -> link != NULL) {

                temp1 = temp1 -> link;

            }

            temp1 -> link = max;

        }

    }

    return (q);

}

</code></pre>

<h2>Una Función para sumar 2 polinomios</h2>

<pre><code>

/* A function to add two polynomials */

   struct pnode *polyadd(struct pnode *p, struct pnode *q)

   {

      struct pnode *r = NULL;

      int e;

      int c;

      while((p!=NULL) &#038;&#038; (q != NULL)) {

            //p mayor exponente que q

            if(p->exp > q->exp) {

                r = insert(r,p->exp,p->coeff);

                p = p->link;



            //q mayor exponenete que p

            } else if(p->exp &lt; q->exp) {

                r = insert(r,q->exp,q->coeff);

                q = q->link;



            //mismo exponente

            } else {

                c = p->coeff + q->coeff;

                e = q->exp;

                r = insert( r, e,c);

                p = p->link;

                q = q->link;

            }

      }

   //Lo que quede de ambos polinomios

    while(p != NULL) {

        r = insert( r, p->exp,p->coeff);

        p = p->link;

    }

    while(q!=NULL) {

        r = insert( r, q->exp,q->coeff);

        q = q->link;

    }

   return(r);

}

</code></pre>

<h2>Una función para multiplicar polinomios.</h2>

<p>Teniendo una función insertar que sume los términos del mismo exponente y una función de suma es muy fácil agregar una función que multiplique polinomios.</p>

<pre><code>

struct pnode *polymult(struct pnode *p, struct pnode *q) {

    struct pnode *r, *r2, *temp;

    r = NULL;

    temp = q;

    while(p != NULL) {

        temp = q;

        while(temp != NULL) {

            r = insert(r, (temp->exp + p->exp), (temp->coeff * p->coeff));

            temp = temp->link;

        }

        p = p->link;

    }

    return r;

}

</code></pre>

<h2>Probando el programa</h2>

<pre><code>

int main()

   {

         int e;

         int n,i, c;

         struct pnode *poly1 = NULL ;

         struct pnode *poly2=NULL;

         struct pnode *result;

         printf("Enter the terms in the polynomial1 \n");

         scanf("%d",&#038;n);

         i=1;

         while ( n-- > 0 )

         {

      printf( "Enter the exponent and coefficient of the term number %d\n",n);

               scanf("%d %d",&#038;e,&#038;c);

               poly1 = insert ( poly1,e,c);

         }

        printf("Enter the terms in the polynomial2 \n");

         scanf("%d",&#038;n);

         i=1;

         while ( n-- > 0 )

         {

      printf( "Enter the exponent and coefficient of the term number %d\n",n);

               scanf("%d %d",&#038;e,&#038;c);

               poly2 = insert ( poly2,e,c);

         }

         poly1 = sortpoly(poly1);

         poly2 = sortpoly(poly2);

         printf("The polynomial 1 is\n");

         printpoly ( poly1 );

         printf("The polynomial 2 is\n");

         printpoly ( poly2 );

         result = polyadd(poly1,poly2);

         printf("The result of addition is\n");

         printpoly ( result );

         result = polymult(poly1, poly2);

         printf("The result of multiplication is\n");

         printpoly ( result );

         return 0;

   }

</code></pre>

<h2>Resultados</h2>

<pre><code>
Enter the terms in the polynomial1 
5
Enter the exponent and coefficient of the term number 4
5 5
Enter the exponent and coefficient of the term number 3
4 4
Enter the exponent and coefficient of the term number 2
3 3
Enter the exponent and coefficient of the term number 1
2 2
Enter the exponent and coefficient of the term number 0
1 1
Enter the terms in the polynomial2 
2
Enter the exponent and coefficient of the term number 1
2 2
Enter the exponent and coefficient of the term number 0
1 1
sdfsdfThe polynomial 1 is
The polynomial is
5x^5	4x^4	3x^3	2x^2	1x	
The polynomial 2 is
The polynomial is
2x^2	1x	
The result of addition is
The polynomial is
5x^5	4x^4	3x^3	4x^2	2x	
The result of multiplication is
The polynomial is
10x^7	13x^6	10x^5	7x^4	4x^3	1x^2	

</code></pre>
<p>Pensando en todo esto es interesante en como en MathML se usan estructuras arboreas ( <a class="wiki" href="http://www.wikipedia.org/wiki/MathML">MathML</a> esta basado en XML ) para representar expresiones matematicas y las implicaciones que puede tener a futuro para la automatización de determinado tipo de calculos.</p>

<h4>Referencias</h4>

<p><a class="amazon" href="http://www.amazon.com/Data-Structures-Electrical-Computer-Engineering/dp/1584503386/">C &amp; Data Structures</a>, Deshpande, Kakde; Charles River Media, 2004</p>]]></content:encoded>
			<wfw:commentRss>http://www.metonymie.com/apuntes/2009/08/13/usando-linked-lists-para-trabajar-con-polinomios.html/feed</wfw:commentRss>
		</item>
	</channel>
</rss>
