martes, 31 de diciembre de 2013

Sherlock Homes y el problema del caballo

Después de un rato jugar el videojuego The testament of Sherlock Holmes me encontré con este acertijo conocido.



Se trata del tour del caballo como las máquinas sirven para hacer prueba y error dejé la consola y encendí el PC para escribir la solución a este.

Hay que decir que este problema es bastante sencillo de resolver para tableros pequeños y basta con un dfs o backtracking y se obtienen no sólo una sino todas las soluciones posibles :)

Así pues la idea básica del algoritmo es:


Guardar los deltas de los movimientos en alguna estructura puede ser un arreglo.

función solución(f, c, cam, vis):
  si ya visitó todas las casillas imprima cam y retorne
  para cada movimiento:
    si es posible(no se ha realizado y no se sale del tablero)
      movf = f+deltaf iésimo
      movc = c+deltac iésimo
      solución(movf, movc, cam+(movf+movc), vis+(movf+movc))
      remover movf y movc de cam y de vis

y la llamada inicial sería algo como

solucíon(0,0,'',estruc)
donde estruc es alguna estructura que permita eficientemente controlar qué casillas ya se han visitado.


Si se usa un conjunto se puede saber cuántas se han visitado aunque con una matriz también funcionaría se necesitaría un contador como parámetro adicional.

Que se diviertan!

martes, 15 de octubre de 2013

Búsqueda binaria

Hola, en muchas ocasiones tendremos que escribir una búsqueda binaria y deberemos tener el concepto claro, ya que no siempre se puede ver tan fácilmente que el problema que debemos solucionar, se puede resolver con una búsqueda binaria.

Primero que todo hay que aclarar, la búsqueda binaria, nos sirve para eso, buscar. Segundo, la restricción para usar la búsqueda binaria es que los elementos estén ordenados. Tercero, si queremos obtener un beneficio de esta, debe realizarse sobre una estructura a la cual se pueda acceder a cualquier elemento en tiempo constante, es decir O(1).

La idea del algoritmo está basado en el paradigma divide y vencerás. Divide y vencerás viene de los romanos, ya no recuerdo por qué, pero lo importante es la idea detrás. Se suele hablar de dividir un problema en subproblemas y de esta forma resolverlos más fácilmente. En la búsqueda binaria se aplica simplemente dividiendo el arreglo en dos, olvidando lo que no nos interesa y centrándonos en lo que sí.

Si tenemos un arreglo y sabemos que está ordenado, por ejemplo:

1 3 6 8 9 11 15

y necesitamos buscar el número 9 y hacemos una búsqueda lineal(iterar uno por uno) tendríamos que hacer 6 iteraciones. Sin embargo, si nos damos cuenta, no es necesario iterar por cada uno y verificar que sea el número que buscamos, por ejemplo, si estamos en la mitad(número 8) sabremos que si el valor que buscamos está en el arreglo tendría que estar a la derecha, y no es necesario buscarlo en la mitad de la izquierda. De esta forma ya nos habremos olvidado de la mitad del arreglo. Luego, podríamos, ¿por qué no?, hacer lo mismo con el arreglo resultante y verificar si el número está en este arreglo, ¿cómo? mirando si es el valor de la mitad el que buscamos. Se recomienda que se haga por cuenta propia el intento de escribir el código que realiza esto, lo cual no es muy complicado. A continuación el pseudocódigo:


Por la ley de la tricotomía, para los enteros se cumple que solo puede entrar a un if de los mostrados anteriormente, y como de una u otra forma, en los computadores sólo trabajamos con valores enteros, entonces podemos estar tranquilos.

Escribir esto en código no es muy complicado, solo hay que tener cuidado de no enredarse, ya sea por desesperación o apuro se pueden cometer errores tontos :)

Finalmente, al dividir el arreglo en dos cada vez, estamos obteniendo una complejidad de O(log(n)) lo cual es lo suficientemente rápido para valores muy grandes. Digamos 10**1000 tiene 1000 dígitos y aún así podríamos buscar un valor en 3321 iteraciones, lo cual no es nada comparado con la cantidad de elementos en total.

Si no tenemos los elementos ordenados, tendremos que pensarlo dos veces, ya que la forma más rápida de ordenar es lineal, es decir O(n), pero con ciertas restricciones y para propósito general tenemos O(n*log(n))

En el siguiente link pueden encontrar implementaciones en varios lenguajes de programación:  http://www.codecodex.com/wiki/Binary_search

Información adicional:
http://googleresearch.blogspot.com/2006/06/extra-extra-read-all-about-it-nearly.html
http://community.topcoder.com/tc?module=Static&d1=tutorials&d2=binarySearch