Esto fue escrito como parte del primer empuje de programación periódica Premier Puzzle .
El juego
Se proporcionan una palabra inicial y final de la misma longitud. El objetivo del juego es cambiar una letra en la palabra inicial para formar una palabra válida diferente, repitiendo este paso hasta llegar a la palabra final, utilizando la menor cantidad de pasos. Por ejemplo, dadas las palabras TREE y FLED, la salida sería:
TREE
FREE
FLEE
FLED
2
Especificaciones
- Los artículos de Wikipedia para OWL o SOWPODS podrían ser un punto de partida útil en lo que respecta a las listas de palabras.
- El programa debe admitir dos formas de seleccionar las palabras de inicio y fin:
- Especificado por el usuario a través de la línea de comandos, stdin o lo que sea adecuado para su idioma de elección (solo mencione lo que está haciendo).
- Seleccionando 2 palabras al azar del archivo.
- Las palabras iniciales y finales, así como todas las palabras provisionales, deben tener la misma longitud.
- Cada paso debe imprimirse en su línea.
- La línea final de su salida debe ser el número de pasos intermedios que se requieren para pasar entre las palabras iniciales y finales.
- Si no se puede encontrar una coincidencia entre las palabras iniciales y finales, la salida debe constar de 3 líneas: la palabra inicial, la palabra final y la palabra OY.
- Incluya la notación Big O para su solución en su respuesta
- Incluya 10 pares únicos de palabras iniciales y finales (con su salida, por supuesto) para mostrar los pasos que produce su programa. (Para ahorrar espacio, aunque su programa debería generarlos en líneas individuales, puede consolidarlos en una sola línea para publicar, reemplazando nuevas líneas con espacios y una coma entre cada ejecución.
Objetivos / Criterios ganadores
- Ganará la solución Big O más rápida / mejor que produzca los pasos intermedios más cortos después de una semana.
- Si un empate resulta del criterio Big O, el código más corto ganará.
- Si todavía hay un empate, ganará la primera solución para alcanzar su revisión más rápida y más corta.
Pruebas / salida de muestra
DIVE
DIME
DAME
NAME
2
PEACE
PLACE
PLATE
SLATE
2
HOUSE
HORSE
GORSE
GORGE
2
POLE
POSE
POST
PAST
FAST
3
Validación
Estoy trabajando en un script que se puede usar para validar la salida.
Va a:
- Asegúrese de que cada palabra sea válida.
- Asegúrese de que cada palabra sea exactamente 1 letra diferente de la palabra anterior.
No lo hará:
- Compruebe que se utilizó el menor número de pasos.
Una vez que haya escrito eso, por supuesto actualizaré esta publicación. (:
code-challenge
word-puzzle
1p5
Rebecca Chernoff
fuente
fuente
HOUSE
aGORGE
2. Se da cuenta de que hay 2 palabras intermedias, por lo que tiene sentido, pero el número de operaciones sería más intuitivo.The fastest/best Big O solution producing the shortest interim steps after one week will win.
dado que no puede garantizar que la solución más rápida es la que usa la menor cantidad de pasos, debe proporcionar una preferencia, si una solución usa menos pasos, pero alcanza el objetivo más adelante.BAT
yCAT
tendré cero pasos, ¿verdad?Respuestas:
Dado que la longitud figura como criterio, aquí está la versión de golf con 1681 caracteres (probablemente todavía podría mejorarse en un 10%):
La versión no protegida, que usa nombres y métodos de paquetes y no da advertencias ni extiende clases solo para crear un alias, es:
Como puede ver, el análisis de costos de funcionamiento es
O(filelength + num_words * hash + V * n * (n + hash) + E * hash)
. Si acepta mi suposición de que una inserción / búsqueda de tabla hash es tiempo constante, eso esO(filelength + V n^2 + E)
. Las estadísticas particulares de los gráficos en SOWPODS significan queO(V n^2)
realmente dominaO(E)
para la mayorían
.Resultados de muestra:
IDOLA, IDOLS, IDYLS, ODYLS, ODALS, OVALS, OVELS, HORNOS, EVENS, ETENS, STENS, SKENS, SKINS, SPINS, SPINE, 13
WICCA, PROSY, OY
BRINY, BRINS, TRINS, TAINS, TARNS, YARNS, YAWNS, YAWPS, YAPPS, 7
GALES, GASES, GASTS, GESTS, GESTE, GESSE, DESSE, 5
SURES, DURES, DUNES, DINES, DINGS, DINGY, 4
LICHT, LIGHT, BIGHT, BIGOT, BIGOS, BIROS, GIROS, GIRNS, GURNS, GUANS, GUANA, RUANA, 10
SARGE, SERGE, SERRE, SERRS, SEERS, DEERS, DYERS, OYERS, OVERS, OVELS, OVALS, ODALS, ODYLS, IDYLS, 12
KEIRS, SEIRS, SEERS, CERVEZAS, BRERS, BRERE, BREME, CREME, CREPE, 7
Este es uno de los 6 pares con el camino más corto más largo:
MÁS GANADOR, MÁS FÁCIL, MÁS FÁCIL, MÁS SALUDABLE, MÁS SANO, MÁS SENCILLO, MÁS MADRE, MÁS MEDIO, MÁS SILVESTRE, MÁS SILVESTRE, MÁS SILVESTRE, MÁS SALUDABLE, MÁS SALUDABLE, MÁS CANTIDAD, CANTANTE, CONCURSO, CONFESIÓN, CONFESIÓN, CONFERENCIAS, CONQUISTAS, COCINAS, COOPEROS, COPPERS, COPPERS POPPITS, POPPIES, POPSIES, MOPSIES, MOUSIES, MOUSSES, POUSSES, PLUSSES, PLISSES, PRISSES, PRESSES, PREASES, UREASES, UNEASES, UNCASES, UNCASED, UNBASED, UNBATED, UNMATED, UNMETED, UNMEWED, INMEWED, INDEME ÍNDICES, INDENAS, INDENTES, INCIDENTES, INGRESOS, INFESIONES, INFECTOS, INYECTOS, 56
Y uno de los pares de 8 letras solubles en el peor de los casos:
ENROBING, UNROBING, UNROPING, UNCOPING, UNCAGING, UNCAGING, ENCAGING, ENRAGING, ENRACING, ENLACING, UNLACING, UNLAYING, UPLAYING, SPLAYING, SPRAYING, STRAYING, STROYING, STROKING, STOKING, STUMPING, STUMPING, STUMPING, STUMPING, STUMPING engaste, crujiente, Crispins, Crispens, CAJONES, arrugadores, CRAMPERS, abrazaderas, claspers, clashers, Slashers, slathers, se desliza, Smithers, Smothers, chupetes, Southers, MOUTHERS, MOUCHERS, couchers, monitores de, los cazadores furtivos, POTCHERS, PUTCHERS, pegadores, ALMUERZOS, LYNCHERS, LYNCHETS, LINCHETS, 52
Ahora que creo que tengo todos los requisitos de la pregunta fuera del camino, mi discusión.
Para un CompSci, la pregunta obviamente se reduce al camino más corto en un gráfico G cuyos vértices son palabras y cuyos bordes conectan palabras que difieren en una letra. Generar el gráfico de manera eficiente no es trivial: de hecho, tengo una idea que necesito revisar para reducir la complejidad a O (V n hash + E). La forma en que lo hago implica crear un gráfico que inserta vértices adicionales (correspondientes a palabras con un carácter comodín) y es homeomorfo al gráfico en cuestión. Pensé en usar ese gráfico en lugar de reducirlo a G, y supongo que desde el punto de vista del golf debería haberlo hecho, sobre la base de que un nodo comodín con más de 3 bordes reduce el número de bordes en el gráfico, y el el peor tiempo de ejecución estándar de los algoritmos de ruta más corta es
O(V heap-op + E)
.Sin embargo, lo primero que hice fue ejecutar algunos análisis de los gráficos G para diferentes longitudes de palabras, y descubrí que son extremadamente escasos para palabras de 5 o más letras. El gráfico de 5 letras tiene 12478 vértices y 40759 aristas; agregar nodos de enlace empeora el gráfico. Para cuando tenga hasta 8 letras, hay menos aristas que nodos, y 3/7 de las palabras son "distantes". Así que rechacé esa idea de optimización por no ser realmente útil.
La idea que resultó útil fue examinar el montón. Puedo decir honestamente que he implementado algunos montones moderadamente exóticos en el pasado, pero ninguno tan exótico como este. Uso A-star (ya que C no proporciona ningún beneficio dado el montón que estoy usando) con la heurística obvia de la cantidad de letras diferentes del objetivo, y un poco de análisis muestra que en cualquier momento no hay más de 3 prioridades diferentes en el montón Cuando hago estallar un nodo cuya prioridad es (costo + heurístico) y miro a sus vecinos, hay tres casos que estoy considerando: 1) el costo del vecino es el costo + 1; la heurística del vecino es heurística-1 (porque la letra que cambia se vuelve "correcta"); 2) costo + 1 y heurística + 0 (porque la letra que cambia va de "incorrecta" a "aún incorrecta"; 3) costo + 1 y heurística + 1 (porque la letra que cambia va de "correcta" a "incorrecta"). Entonces, si relajo al vecino, lo insertaré con la misma prioridad, prioridad + 1 o prioridad + 2. Como resultado, puedo usar una matriz de 3 elementos de listas vinculadas para el montón.
Debo agregar una nota sobre mi suposición de que las búsquedas de hash son constantes. Muy bien, se puede decir, pero ¿qué pasa con los cálculos hash? La respuesta es que los estoy amortizando:
java.lang.String
almacena en cachéhashCode()
, por lo que el tiempo total dedicado a calcular hashes esO(V n^2)
(al generar el gráfico).Hay otro cambio que afecta la complejidad, pero la cuestión de si es una optimización o no depende de sus suposiciones sobre las estadísticas. (OMI, poner "la mejor solución Big O" como criterio es un error porque no hay una mejor complejidad, por una simple razón: no hay una sola variable). Este cambio afecta el paso de generación del gráfico. En el código anterior, es:
Eso es
O(V * n * (n + hash) + E * hash)
. Pero laO(V * n^2)
parte proviene de generar una nueva cadena de n caracteres para cada enlace y luego calcular su código hash. Esto se puede evitar con una clase auxiliar:Entonces la primera mitad de la generación del gráfico se convierte en
Al usar la estructura del hashcode podemos generar los enlaces en
O(V * n)
. Sin embargo, esto tiene un efecto knock-on. Inherente a mi suposición de que las búsquedas de hash son de tiempo constante es una suposición de que comparar objetos por igualdad es barato. Sin embargo, la prueba de igualdad de Link estáO(n)
en el peor de los casos. El peor de los casos es cuando tenemos una colisión hash entre dos enlaces iguales generados a partir de palabras diferentes, es decir, ocurreO(E)
veces en la segunda mitad de la generación del gráfico. Aparte de eso, excepto en el improbable caso de una colisión hash entre enlaces no iguales, estamos bien. Así que hemos cambiadoO(V * n^2)
porO(E * n * hash)
. Vea mi punto anterior sobre estadísticas.fuente
Java
Complejidad : ?? (No tengo un título de CompSci, por lo que agradecería ayuda en este asunto).
Entrada : proporcione un par de palabras (más de 1 par si lo desea) en la línea de comando. Si no se especifica una línea de comando, se eligen 2 palabras aleatorias distintas.
fuente
System.nanoTime()
.c en unix
Usando el algoritmo dijkstra.
Una gran fracción del código es una implementación de árbol de disfraz n-ary, que sirve para mantener
Cualquiera que esté interesado en ver cómo funciona probablemente debería leer
findPath
,process
yprocessOne
(y sus comentarios asociados). Y tal vezbuildPath
ybuildPartialPath
. El resto es contabilidad y andamios. Varias rutinas utilizadas durante las pruebas y el desarrollo, pero no en la versión de "producción" se han dejado en su lugar.Estoy usando
/usr/share/dict/words
en mi caja de Mac OS 10.5, que tiene tantas entradas, esotéricos largas que dejar que siga completamente al azar genera una gran cantidad deOY
s.Alguna salida:
El análisis de complejidad no es trivial. La búsqueda es una profundización iterativa de dos lados.
W
.S_min = (<number of different letter>-1)
debe a que si solo estamos separados por una letra, calificamos el cambio en 0 pasos intermedios. El máximo es difícil de cuantificar ver el TRICKY - SWOOSH ejecutado arriba. Cada mitad del árbol seráS/2-1
paraS/2
B
.Entonces, el número mínimo de operaciones es de alrededor
2 * (S/2)^B * W
, no es realmente bueno.fuente
O(|V|+|E|)
lugar deO(|E|+|V| log |V|)
?