Considere un gráfico de cuadrícula n por n que se vea así.
Es importante notar que este gráfico es 11 por 11 .
En cualquier punto dado, un hombre se para en una intersección y solo se mueve vertical u horizontalmente paso a paso a la siguiente intersección. Lamentablemente, ha bebido demasiado, por lo que elige la dirección en la que se mueve aleatoriamente desde las 4 direcciones posibles (arriba, abajo, izquierda, derecha). Esto es hasta 4 como si estuviera parado en una pared, solo tiene 3 opciones, por supuesto, y en una esquina solo tiene 2.
Comienza en la esquina inferior izquierda y su objetivo es llegar a casa, que es la esquina superior derecha. El tiempo es simplemente la cantidad de pasos que le toma.
Sin embargo, eres un adversario malicioso que quiere que llegue a casa lo más lento posible. Puede eliminar cualquier número de bordes del gráfico en cualquier momento durante su caminata. La única restricción es que siempre debes dejarle algún camino para que llegue a casa y no puedes eliminar un borde que ya ha usado.
El desafío es idear un adversario lo más malicioso posible y luego probarlo en un gráfico de
100 por 10020 por 20 con un caminante borracho al azar. Su puntaje es simplemente el tiempo promedio que le toma al caminante aleatorio llegar a casa más de101000 carreras.
Puede usar cualquier idioma y bibliotecas que desee siempre que estén disponibles gratuitamente y sean fácilmente instalables en Linux.
¿Qué necesito implementar?
Debe implementar el código para el caminante aleatorio y también para el adversario, y el código debe combinarse para que la salida cuando se ejecute sea simplemente el promedio de 1000 ejecuciones utilizando su código de adversario. El código aleatorio del caminante debe ser muy simple de escribir, ya que solo elige entre (x-1, y), (x + 1, y), (x, y-1) y (x, y + 1) asegurándose de que ninguno de ellos ha sido eliminado o está fuera de rango.
El código del adversario es, por supuesto, más difícil y también necesita recordar qué bordes ha atravesado el borracho para que no intente eliminar ninguno de ellos y asegurarse de que todavía haya una ruta a casa para el borracho, lo cual es un poco más complicado que hacer rapido
El Anexo 10 no es suficiente, pero no quería castigar a las personas que lograron dar largas caminatas. Ahora lo he aumentado a 1000 debido a una solicitud popular. Sin embargo, si su caminata es tan larga que no puede hacer 1000 carreras en una cantidad de tiempo realista, solo informe la cantidad máxima de carreras que puede.
Tabla de puntaje alto para 100 por 100.
- 976124.754 por Optimizer.
- 103000363.218 por Peter Taylor.
Editar 1. Cambió el tamaño del gráfico a 20 por 20 para ayudar al tiempo de ejecución de las pruebas de las personas. Haré una nueva puntuación de tabla alta para ese tamaño a medida que las personas envíen las puntuaciones.
Tabla de puntaje alto para 20 por 20.
230,794.38 (100k runs) by justhalf
227,934 by Sparr
213,000 (approx) by Peter Taylor
199,094.3 by stokastic
188,000 (approx) by James_pic
64,281 by Geobits
Respuestas:
230,794.38 en 20x20, 100k carreras
Última actualización: finalmente construí una solución dinámica de 2 vías perfecta. Dije perfecto ya que la versión anterior en realidad no es simétrica, era más fácil obtener un camino más largo si el borracho tomaba un camino sobre el otro. El actual es simétrico, por lo que puede obtener un mayor número esperado de pasos. Después de algunas pruebas, parece estar alrededor de 230k, una mejora con respecto a la anterior que es de aproximadamente 228k. Pero estadísticamente hablando, esos números todavía están dentro de su gran desviación, por lo que no afirmo que esto sea significativamente mejor, pero creo que debería ser mejor que la versión anterior.
El código está al final de esta publicación. Se actualiza para que sea mucho más rápido que la versión anterior, completando 1000 ejecuciones en 23 segundos.
A continuación se muestra una ejecución de muestra y un laberinto de muestra:
Envíos anteriores
¡Finalmente puedo igualar el resultado de Sparr! = D
Basado en mis experimentos anteriores (ver al final de esta publicación), la mejor estrategia es tener un doble camino y cerrar uno cuando el borracho llegue a cualquiera de ellos, y la variable proviene de cuán bueno podemos predecir dinámicamente dónde irá el borracho. aumentar la posibilidad de que se meta en un camino más largo.
Entonces, según mi
DOUBLE_PATH
estrategia, construí otro, que cambia el laberinto (miDOUBLE_PATH
laberinto era fácilmente modificable) dependiendo del movimiento del borracho. A medida que toma un camino con más de una opción disponible, cerraré los caminos para dejar solo dos opciones posibles (una de la que vino, otra la no viajada).Esto suena similar a lo que Sparr ha logrado, como lo muestra el resultado. La diferencia con la suya es demasiado pequeña para que se considere mejor, pero diría que mi enfoque es más dinámico que él, ya que mi laberinto es más modificable que el de Sparr =)
El resultado con una muestra final de laberinto:
Sección de Experimentos
Lo mejor resulta ser la misma estrategia que stokastic, me enorgullece experimentar usando varias estrategias e imprimir buenos resultados :)
Cada uno de los laberintos impresos a continuación es el último laberinto después de que el borracho haya llegado a casa, por lo que pueden ser ligeramente diferentes de una carrera a otra debido a la aleatoriedad en el movimiento del borracho y la dinamicidad del adversario.
Describiré cada estrategia:
Sendero Sencillo
Este es el enfoque más simple, que creará una única ruta desde la entrada hasta la salida.
Isla (nivel 0)
Este es un enfoque que intenta atrapar al borracho en una isla casi aislada. No funciona tan bien como esperaba, pero esta es una de mis primeras ideas, así que la incluyo.
Hay dos caminos que conducen a la salida, y cuando el borracho se acerca a uno de ellos, el adversario lo cierra y lo obliga a encontrar la otra salida (y posiblemente queda atrapado nuevamente en la isla)
Doble camino
Esta es la estrategia más discutida, que consiste en tener dos caminos de igual longitud hacia la salida, y cerrar uno de ellos cuando el borracho se acerque a uno de ellos.
Isla (nivel 1)
Inspirados por los múltiples caminos de la isla y el alto conteo de caminatas en un solo camino, conectamos la isla a la salida y hacemos un laberinto de un solo camino en la isla, creando en total tres caminos para salir, y de forma similar al caso anterior, cerramos cualquiera de los salga cuando el borracho se acerque.
Esto funciona un poco mejor que la ruta simple, pero aún así no vence a la ruta doble.
Isla (nivel 2)
Intentando expandir la idea anterior, creé una isla anidada, creando en total cinco caminos, pero no parece funcionar tan bien.
Isla (nivel 3)
Al darse cuenta de que la doble ruta en realidad funciona mejor que la ruta única, ¡hagamos que la isla tenga doble ruta!
El resultado es una mejora sobre la Isla (nivel 1), pero aún así no supera el doble camino puro.
A modo de comparación, el resultado para el doble camino del tamaño de la isla es de 131,134.42 movimientos en promedio. Entonces, esto agrega un número bastante significativo de movimientos (alrededor de 40k), pero no lo suficiente como para vencer el doble camino.
Isla (nivel 4)
Nuevamente, experimentar con una isla anidada, y nuevamente no funciona tan bien.
Conclusión
Con todo, esto prueba que tener un solo camino largo desde la posición actual del borracho hasta la salida funciona mejor, lo que se logra mediante la estrategia de doble camino, ya que después de cerrar una salida, el borracho tendrá que recorrer la distancia máxima posible para llegar a la salida.
Esto sugiere que la estrategia básica debe seguir siendo el doble camino, y solo podemos modificar la dinámica en la que se crean los caminos, lo que ha hecho Sparr. ¡Así que creo que su estrategia es el camino a seguir!
Código
fuente
227934 (20x20)
Mi tercer intento. Utiliza el mismo enfoque general que @stokastic con dos caminos hacia la salida. Cuando el caminante llega al final de un camino, se cierra, lo que requiere que regrese para llegar al final del otro camino para salir. Mi mejora es generar las rutas a medida que avanza el caminante, de modo que cualquier ruta que avance más en la primera mitad del proceso terminará siendo más larga que la otra ruta.
salida (con tiempo):
Ejemplo de mi laberinto, con mitades de aproximadamente la misma longitud del camino, que muestra el camino izquierdo / inferior cortado de la salida (abajo a la derecha)
PD: Soy consciente de una mejora muy pequeña en este algoritmo que requiere un código más inteligente para generar una forma diferente para los dos caminos, escaleras en lugar de zigzags de altura constante.
fuente
135,488,307.9 para 98x98
199094.3 para 20x20
He implementado una solución que crea dos caminos hacia el final, y cierra exactamente uno de ellos una vez que el borracho lo alcanza. Esto simula una longitud de ruta que, como mínimo, será 1,5 veces la longitud de una ruta única de principio a fin. Después de 27 carreras llegué a un promedio de alrededor de 135 millones. Desafortunadamente, toma varios minutos por caminata, así que tendré que correrlo durante las próximas horas. Una advertencia: mi generador de doble ruta solo funciona si el tamaño del gráfico tiene la forma 4 * n + 2, lo que significa que lo más cerca que puedo llegar a 100 es 102 o 98. Voy a publicar resultados usando 98, que espero aún superar el método de la ruta en zigzag. Trabajaré en un mejor sistema de ruta más adelante. Actualmente los resultados se muestran en forma de (numSteps, currentAverage) después de cada caminata.
EDITAR: corregido, por lo que el código ahora funciona en tamaños de gráfico que son múltiplos de 2, en lugar de 4 * n + 2.
Código: (agregue el argumento 'Verdadero' al constructor de caminantes en la línea 187 para dibujar tortugas en el gráfico).
Datos sin procesar: (numSteps actual, promedio de ejecución)
fuente
Enfoque de 4 caminos, 213k
El enfoque de un camino es
y puntúa un promedio de
N^2
.El enfoque de dos caminos es
pero luego, la primera vez que el borracho se acerca al punto final, se corta:
Se puntúa un promedio de
(N/2)^2 + N^2
.El enfoque de cuatro caminos usa dos cortes:
Suponga que el bucle externo es de longitud
xN
y el bucle interno de longitud(1-x)N
. Por simplicidad, lo normalizaréN=1
.Desde el comienzo hasta el primer corte se obtiene un promedio de
(x/2)^2
. Desde el primer corte hasta el segundo corte tiene dos opciones, de longitudesx
y1-x
; esto da un promedio de(1-x)x^2 + x(1-x)^2 = x-x^2
. Finalmente el camino restante da1
. Entonces el puntaje total esN^2 (1 + x - 3/4 x^2)
.Inicialmente supuse que mantener las rutas disponibles de igual longitud en cada paso sería óptimo, por lo que mi enfoque inicial solía
x = 1/2
dar una puntuación de1.3125 N^2
. Pero después de hacer el análisis anterior, resulta que la división óptima se da cuando sex = 2/3
puntúa1.3333 N^2
.con código
fuente
f
hastac
en su código se trataN/2
, sea a través dee
(yd
) o no, ¿verdad?N^2
, no2^N
. Y sí, hacer que esta dinámica sea la mejor, el desafío es cómo hacerlo dinámico manteniendo la propiedad de cuatro rutas. @PeterTaylor: ¡Buenas imágenes!Experimenté cortando la cuadrícula casi por completo en cada
k
fila. Esto efectivamente lo convierte en algo similar a una caminata aleatoria en unk
byN * N/k
rejilla. La opción más efectiva es cortar cada fila para que obliguemos al borracho a zigzaguear.Para el caso 20x20 (
SIZE=19
) tengocon código
fuente
Para los que no quieren reinventar la rueda.
No te preocupes Lo reinventaré por ti :)
Esto está en Java, por cierto.
Creé una clase Walker que trata de caminar al azar. También incluye un método útil para determinar si un movimiento es válido (si ya se ha pisado).
Supongo que todos ustedes, personas inteligentes, pueden resolver poner números aleatorios para el constructor, se lo dejé a ustedes para que pudieran probar ciertos casos. Además, solo llame a la función walk () para (¡lo adivinó!) Hacer que el borracho camine (al azar).
Implementaré la función canComeHome () en otro momento. Preferiblemente después de buscar la mejor manera de hacerlo.
fuente
previouslyTraveled.add(new int[]{x,y,move[0],move[1]})
debería serx+move[0]
yy+move[1]
. ElWidth-1
yHeight-1
, y la ineficiencia en la comprobación de las rutas eliminadas. He editado tu código (con función adicional para imprimir el laberinto). Siéntase libre de retroceder si cree que es inapropiado.Edge
no se implementa correctamenteComparable<Edge>
. Si desea que las aristas se comparen como iguales, incluso si las invierte, debe tener en cuenta la inversión también en el caso no igual. La forma más fácil de hacerlo sería cambiar el constructor para mantener los puntos ordenados.Comparable
?A
yB
se invierte el mismo borde yC
es diferente, puede obtenerA.compareTo(B) == B.compareTo(A) == 0
butA.compareTo(C) < 0
yB.compareTo(C) > 0
.canComeHome()
)64,281
Actualización desde que la cuadrícula se cambió de 100x100 a 20x20 (1000 pruebas). La puntuación en 100x100 (100 pruebas) fue aproximadamente 36M.
Si bien esto no va a superar una caminata de 1D, quería jugar con una idea que tenía.
La idea básica es que la cuadrícula se divide en habitaciones cuadradas, con solo un camino que conduce 'hacia el hogar' de cada una. El camino abierto es el que el borracho se acerca al último , lo que significa que tiene que explorar todas las salidas posibles, solo para que todos menos uno se estrellen en su cara.
Después de jugar con el tamaño de las habitaciones, llegué a la misma conclusión que Peter, dividirlo más pequeño es mejor. Las mejores puntuaciones vienen con un tamaño de habitación de 2.
El código es descuidado, no importa el desorden. Puede activar el
SHOW
interruptor y mostrará una imagen de los caminos en cadaSHOW_INT
paso para que pueda verlo en acción. Una ejecución completa se parece a:(Esta es la imagen de la cuadrícula anterior de 100x100. 20x20 es así, pero, bueno, más pequeño. El siguiente código se ha actualizado para nuevos tamaños / ejecuciones).
fuente
188k, con 2 caminos
Todas las mejores entradas parecen adoptar el enfoque de generar 2 caminos, y luego cortar uno cuando el borracho se acerca al final del camino. No creo que pueda superar la entrada de justhalf, pero no pude evitar preguntarme: ¿por qué 2 caminos? ¿Por qué no 3, 5 o 20?
TL; DR : 2 caminos parecen ser óptimos
Entonces hice un experimento. Basado en el marco de Stretch Maniac, escribí una entrada para probar varios números de caminos. Puede ajustar el
featureSize
parámetro para variar el número de rutas. AfeatureSize
de 20 da 1 ruta, 10 da 2 rutas, 7 da 3, 5 da 4, y así sucesivamente.Hay algunas optimizaciones que podría hacer pero que no he hecho, y no es compatible con ninguno de los trucos de adaptación que solo utiliza.
De todos modos, aquí están los resultados para varios
featureSize
valores:Y aquí hay un mapa con 3 caminos:
fuente
N
sea la longitud de la ruta (que esn^2-1
), la ruta única requeriráN^2
movimientos en promedio , mientras que las tres rutas(N/3)^2 + (2N/3)^2 + (2N/3)^2 = N^2
más un valor relativamente pequeño, entonces tres las rutas no tienen una ganancia significativa sobre la ruta única, y mucho menos la ruta doble. (El cálculo se basa en el resultado de probabilidad que establece que el movimiento aleatorio en la trayectoria 1-D de longitudN
requiere unN^2
movimiento promedio de un extremo al otro.)131k (20x20)
Mi primer intento fue eliminar todos los bordes horizontales, excepto la fila superior e inferior, luego, cada vez que el caminante llegaba al final de una columna, eliminaba el borde delante de él, hasta que visitaba el fondo de cada columna y finalmente lo hacía. poder llegar a la salida. Esto dio como resultado un promedio de 1/8 tantos pasos como el enfoque de 1d caminata de @ PeterTaylor.
Luego decidí probar algo un poco más tortuoso. He dividido el laberinto en una serie de galones huecos anidados, y le pido que atraviese el perímetro de cada galón al menos 1,5 veces. Esto tiene un tiempo promedio de aproximadamente 131k pasos.
fuente
Hacer nada
Como el hombre se mueve al azar, uno podría pensar que eliminar cualquier nodo solo aumentará sus posibilidades de llegar a casa a largo plazo.
Primero, echemos un vistazo al caso unidimensional, esto se puede lograr eliminando nodos hasta que termine con un camino ondulado, sin interminables o ciclos, que visite (casi) cada punto de la cuadrícula. En una
N x N
cuadrícula, la longitud máxima de dicha ruta esL = N*N - 2 + N%2
(98 para una cuadrícula de 10x10). Caminar a lo largo del camino puede ser descrito por una matriz de transición generada porT1d
.(La ligera asimetría hace que sea difícil encontrar una solución analítica, excepto para matrices muy pequeñas o infinitas, pero de todos modos obtenemos una solución numérica más rápida de lo que tomaría diagonalizar la matriz).
El vector de estado tiene un solo
1
en la posición inicial y después de losK
pasos(T1d**K) * state
nos da la distribución de probabilidad de estar a una cierta distancia desde el inicio (¡eso es equivalente a promediar todas2**K
las caminatas posibles a lo largo del camino!)Ejecutar la simulación de
10*L**2
pasos y guardar el último elemento del vector de estado después de cada paso, lo que nos da la probabilidad de haber llegado a la meta después de un cierto número total de pasos: la distribución de probabilidad acumulativacd(t)
. La diferenciación nos da la probabilidadp
de alcanzar la meta exactamente en un momento determinado. Para encontrar el tiempo promedio que integramost*p(t) dt
El tiempo promedio para alcanzar la meta es proporcional a
L**2
un factor que va muy rápidamente a 1. La desviación estándar es casi constante en alrededor del 79% del tiempo promedio.Este gráfico muestra el tiempo promedio para alcanzar la meta para diferentes longitudes de ruta (correspondiente a tamaños de cuadrícula de 5x5 a 15x15)
Así es como se ve la probabilidad de alcanzar la meta. La segunda curva parece completa porque en cada paso extraño la posición es extraña y, por lo tanto, no puede estar en la meta.
De eso podemos ver que la estrategia equilibrada de doble ruta funciona mejor aquí. Para cuadrículas más grandes, donde la sobrecarga de hacer más caminos es insignificante en comparación con su tamaño, podríamos estar mejor aumentando el número de caminos, de forma similar a como lo describió Peter Taylor, pero manteniendo las longitudes equilibradas
¿Qué pasa si no eliminamos ningún nodo?
Entonces tendríamos el doble de nodos transitables, más cuatro direcciones posibles en lugar de dos. Parecería que hace muy poco probable llegar a alguna parte. Sin embargo, las simulaciones demuestran lo contrario, después de tan sólo 100 pasos en una cuadrícula de 10x10 el hombre es bastante probable que llegue a su objetivo, por lo que le Trappin en las islas es un intento inútil, ya que usted está negociando un potencial de
N**2
largo y sinuoso camino con un tiempo promedio de finalización deN**4
por una isla que se pasa porN**2
pasosfuente