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, 31 de diciembre de 2013
Sherlock Homes y el problema del caballo
Publicado por
Nicolas Castro
en
21:39
0
comentarios
Enviar por correo electrónicoEscribe un blogCompartir en XCompartir con FacebookCompartir en Pinterest
Etiquetas:
algorithm,
algoritmia,
backtrack,
dfs,
knight tour,
problema,
problema del caballo,
programacion,
programming,
sherlock holmes
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))
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
Publicado por
Nicolas Castro
en
17:33
0
comentarios
Enviar por correo electrónicoEscribe un blogCompartir en XCompartir con FacebookCompartir en Pinterest
Etiquetas:
algorithm,
algoritmia,
algoritmo,
busqueda binaria,
divide y venceras,
java,
programacion,
programming
martes, 18 de diciembre de 2012
Solucionando sudokus
Su Doku es un juego muy conocido, no entraré en detalles históricos. Este problema puede ser visto como el problema de colorear un grafo, de esta forma los colores son cada uno de los números del uno al nueve, los nodos son cada una de las casillas y están conectados si están en un mismo recuadro, en una misma fila o en una misma columna.
Pasando a la solución por backtracking bien se puede ver que la fuerza bruta consiste en ubicar las casillas vacias y poner un numero en cada una de ellas y ver cuando ya no hayan casillas vacias si el sudoku ha sido resuelto, de no ser así se vuelve a atrás y se intenta con otros números. El problema de este enfoque es que si hay n casillas vacías se tendrán n! (n factorial) formas de llenar estas casillas. Tal vez para unas 9 casillas vacías funcione, pero si se intenta con 10 o más en un ordenador común en la actualidad, puede tardar mucho tiempo.
Así que la mejor forma de hacerlo es ver cuál casillas de las vacías tiene menos posibilidades y optar por una de ellas y hacer lo mismo recursivamente hasta que no hayan casillas vacías, en este punto se verifica si el sudoku está bien, de ser así ya está, si no entonces se vuelve atrás y se opta por otra de las posibilidades en la casilla que tenia varias posibilidades. Finalmente, si el sudoku está bien construido se podrá llegar a una solución.
En pseudocódigo esto podría ser algo como (la implementación en java o cpp no va más de 80 líneas! :) )
Creo que esta es la forma mas simple de resolver este problema, otro enfoque es usando la ténica de Knuth de "Dancing links". El enfoque presentado aquí no es el más eficiente pero es bastante sencillo de programar y puede ser útil en un concurso de programación :)
Cualquier comentario o duda, comenten.
Nota: les dejo algunos problemas que se pueden resolver con este algoritmo.
https://icpcarchive.ecs.baylor.edu/index.php?option=onlinejudge&page=show_problem&problem=2246
http://projecteuler.net/problem=96
Publicado por
Nicolas Castro
en
19:35
0
comentarios
Enviar por correo electrónicoEscribe un blogCompartir en XCompartir con FacebookCompartir en Pinterest
Etiquetas:
algoritmo solucionar sudoku,
algoritmo solucionar sudoku java cpp,
backtrack,
coloracion grafo sudoku,
graph coloring,
project euler problem 96,
solving sudoku algorithm,
su su sudoku
domingo, 18 de noviembre de 2012
Mínimo común divisor
En algunos problemas se debe encontrar el divisor común mínimo de uno o más números. Sí, el divisor común mínimo, no el máximo. El mímimo común divisor es muy similar al máximo común divisor, en ocasiones es el mismo pero no se deben confundir. El GDC(máximo común divisor) entre 54 y 24 es 6 mientras que el mínimo común divisor es 2.
Para obtener el gcd se puede usar el algoritmo de euclides, el cual en código es:
Una vez que se tiene el gcd se puede encontrar el divisor común mínimo.
Si r = gcd(a,b) y r = c*d entonces c y d dividen a y b.
De esta forma, lo que debemos hacer es descomponer el gcd en sus factores primos y sacamos el mínimo. De esta forma obtendríamos el mínimo común divisor.
Nota: un problema que se resuelve con esta idea es este.
Para obtener el gcd se puede usar el algoritmo de euclides, el cual en código es:
static int gcd(int a, int b){
if(b==0)return a;
return gcd(b,a%b);
}
Una vez que se tiene el gcd se puede encontrar el divisor común mínimo.
Si r = gcd(a,b) y r = c*d entonces c y d dividen a y b.
De esta forma, lo que debemos hacer es descomponer el gcd en sus factores primos y sacamos el mínimo. De esta forma obtendríamos el mínimo común divisor.
Nota: un problema que se resuelve con esta idea es este.
Publicado por
Nicolas Castro
en
13:38
0
comentarios
Enviar por correo electrónicoEscribe un blogCompartir en XCompartir con FacebookCompartir en Pinterest
lunes, 16 de julio de 2012
Interfaz en java se congela
Hola, no se si alguna vez les haya pasado cuando están trabajando en alguna GUI en java y necesitan realizar una tarea mas o menos pesada, la GUI se bloquea y se queda congelada como si la aplicación hubiera muerto, pero en realidad esta haciendo el trabajo, en estos casos lo que se puede hacer para no dar la impresión que el programa ha muerto (e inhabilitar el botón mientras se hace la tarea, o poner un "loading...") es pasarle la tarea que se hacia en el botón a un hilo y tan pronto el hilo acabe que vuelva a activar el botón o que quite el "loading...".
Espero les sirva y si tienen algo que agregar o alguna duda no duden en preguntar.
Espero les sirva y si tienen algo que agregar o alguna duda no duden en preguntar.
Publicado por
Nicolas Castro
en
12:38
0
comentarios
Enviar por correo electrónicoEscribe un blogCompartir en XCompartir con FacebookCompartir en Pinterest
Etiquetas:
gui,
gui bloqueada,
gui congelada,
interfaz,
interfaz grafica,
java,
no refresca,
programacion
martes, 5 de junio de 2012
Criba de Eratostenes
Hola, antes habia puesto un par de algoritmos para saber si un numero era primo, pero y si queremos saber la cantidad de primos en un intervalo dado? Tardariamos mucho si el intervalo es muy grande. Asi que lo mejor para este caso es usar el algoritmo de la criba de eratostenes.
Lo que se hace, es tomar un primo y tachar todos los multiplos de ese primo, luego tomamos el siguiente numero no tachado(que seria un primo) y eliminamos todos sus multiplos, y asi sucesivamente, hasta que todos los no primos se hayan tachado en el intervalo. Pero como sabemos eso? Pues bien, esto lo sabemos porque como se dijo antes solo necesitamos iterar hasta la raiz de un numero para saber si es primo. Es decir que cuando lleguemos a la raiz del maximo numero del intervalo habremos eliminado todos los no primos.
Asi pues, el algoritmo en java para hacer esto seria:
public static boolean criba(int n){
boolean primos[] = new boolean[n+1];
Arrays.fill(primos,true);
primos[0] = primos[1] = false;
for(int i=2;i<(int)Math.sqrt(n)+1;i++)
if(primos[i])
for(int j=i*i;j<primos.length;j+=i)
primos[j] = false;
return primos;
}
Con el primer ciclo recorremos los numeros hasta la raiz cuadrada, y con el segundo tachamos sus multiplos si el numero "i" es primo. De esta forma los valores que queden en true seran los primos, es decir primos[2] sera true.
El algoritmo en C podria ser algo como lo siguiente:
char* criba(int n){
char *p = (char*)malloc((n+1)*sizeof(char));
memset(p,' ', n+1);
n = n+1;
int i=0,j=0;
int f = sqrt((double)n)+1;
p[0] = p[1] = 'n';
for(i=0;i<f;i++)
if(p[i]==' ')
for(j=i*i;j<n;j+=i)
p[j]='n';
return p;
}
Y en C los valores que queden con una 'n', no seran primos y los que queden con ' ' seran los primos, eso ya es de gustos :)Si hay alguna pregunta o algo que agregar, comenten.
PD: les dejo unos cuantos problemas en los cuales hay que aplicar la criba para resolverlos.
http://www.codechef.com/problems/PRPALIN
http://projecteuler.net/problem=7
http://projecteuler.net/problem=10
http://projecteuler.net/problem=249
Publicado por
Nicolas Castro
en
16:45
1 comentarios
Enviar por correo electrónicoEscribe un blogCompartir en XCompartir con FacebookCompartir en Pinterest
Etiquetas:
algorithm,
algoritmia,
algoritmo,
c,
criba de eratostenes,
implementacion,
java,
primo,
programa,
programacion,
programming,
sieve of eratosthenes
martes, 1 de mayo de 2012
Exponenciacion binaria y modular
Hola, estuve haciendo un par de cosas y tuve que usar un algoritmo de exponenciacion binaria y modular pero no queria que fuera recursivo asi que luego de algunos intentos, esto fue lo que consegui, espero les pueda servir de ayuda, tanto como a mi(aunque esta en java se entiende si manejas otro lenguaje :P) :
public static int expomod(int a, long b,int mod){
int res = 1;
while(b>0){
if((b&1)==1)
res=(a*res)%mod;
b>>=1;
a=((a%mod)*(a%mod))%mod;
}
return res;
}
y el algoritmo en python es:
def ex(a, b,m): r = 1 while(b): if(b&1): r = (r*a)%m b>>=1 a = ((a%m)*(a%m))%m return r
La verdad luego de ver el algoritmo recursivo, se entiende este. Lo que se hace es cambiar la recursividad a las variables, por decirlo de alguna forma. Saludos y espero sus comentarios!
Un buen tutorial donde se tratan a fondo otros algoritmos relacionados es este de topcoder.
Nota: un problema en donde se puede aplicar este algoritmo es este.
Publicado por
Nicolas Castro
en
1:13
0
comentarios
Enviar por correo electrónicoEscribe un blogCompartir en XCompartir con FacebookCompartir en Pinterest
Etiquetas:
algoritmia,
algoritmo,
binaria,
binary exponentiaiton,
exponenaciacion,
iterativo,
java,
modular,
problema,
programacion,
programming,
project euler,
python,
recursividad
Suscribirse a:
Entradas (Atom)



