Aquí hay un archivo de texto ASCII de 1.2Mb que contiene el texto de Moby-Dick de Herman Melville ; o la ballena . Su tarea es escribir un programa o función (o clase, etc. - ver más abajo) que se le dará a este archivo un carácter a la vez, y en cada paso debe adivinar el siguiente carácter.
Este es un desafío de código . Tu puntaje será
2*L + E
donde L
es el tamaño de su envío en bytes y E
es el número de caracteres que adivina incorrectamente. El puntaje más bajo gana.
Otros detalles
Su envío será un programa o función (etc.) que será invocado o invocado o enviado datos varias veces. (1215235 veces para ser exactos). Cuando se llama a la n º tiempo que se le dará el n º carácter de whale.txt
o whale2.txt
y debe adivinar su salida para el ( n + 1 ) ésimo carácter. El E
componente de su puntuación será el número total de caracteres que adivina incorrectamente.
La mayoría de las presentaciones necesitarán almacenar algún estado entre invocaciones, para que puedan rastrear cuántas veces se han llamado y cuáles fueron las entradas anteriores. Puede hacerlo escribiendo en un archivo externo, utilizando static
variables globales o, enviando una clase en lugar de una función, utilizando una mónada de estado o cualquier otra cosa que funcione para su idioma. Su envío debe incluir cualquier código requerido para inicializar su estado antes de la primera invocación.
Su programa debe ejecutarse de manera determinista, de modo que siempre haga las mismas conjeturas con la misma entrada (y, por lo tanto, siempre obtenga la misma puntuación).
Su respuesta debe incluir no solo su envío, sino también el código que utilizó para calcular la E
parte de su puntaje. No es necesario que esté escrito en el mismo idioma que su envío y no se contará para su recuento de bytes. Se le recomienda que sea legible.
Con respecto a la interfaz entre su envío y este programa de cálculo de puntaje, todo está bien, siempre que su programa siempre dé un byte de salida antes de recibir su próximo byte de entrada. (Entonces, por ejemplo, no puede simplemente pasarle una cadena que contenga toda la entrada y recuperar una cadena que contenga toda la salida).
En realidad, debe ejecutar su programa de prueba y calcular / verificar su puntaje antes de enviar su entrada. Si su envío es demasiado lento para que pueda verificar su puntaje, entonces no está calificado para competir, incluso si sabe cuál sería su puntaje en principio.
El L
componente de su puntaje se calculará de acuerdo con las reglas habituales para los desafíos de golf de código. Si su envío contendrá varios archivos, tome nota de las reglas sobre puntuación y estructura de directorios en ese caso. Cualquier información que use su código debe incluirse en su L
puntaje.
Puede importar bibliotecas existentes, pero no puede cargar ningún otro archivo externo, y su código puede no acceder a la whale.txt
owhale2.txt
archivo de cualquier otra manera que no sea la descrita anteriormente. No puede cargar ninguna red neuronal previamente entrenada u otras fuentes de datos estadísticos. (Está bien usar redes neuronales, pero debe incluir los datos de peso en su envío y contarlos para su recuento de bytes). Si por alguna razón su idioma o bibliotecas incluyen una función que proporciona parte o la totalidad del texto de Moby Dick , no puede usar esa función. Además de eso, puede usar cualquier otra función incorporada o de biblioteca que desee, incluidas las relacionadas con el procesamiento de texto, la predicción o la compresión, siempre que formen parte de su idioma o de sus bibliotecas estándar. Para rutinas más exóticas y especializadas que incluyen fuentes de datos estadísticos, deberá implementarlas usted mismo e incluirlas en su recuento de bytes.
Es probable que algunas presentaciones incluyan componentes que son generados por el código. Si este es el caso, incluya en su respuesta el código que se utilizó para producirlos y explique cómo funciona . (Mientras este código no sea necesario para ejecutar su envío, no se incluirá en su recuento de bytes).
Por razones históricas, hay dos versiones del archivo, y puede usar cualquiera de ellas en una respuesta. En whale2.txt
(vinculado anteriormente) el texto no está ajustado, por lo que las nuevas líneas aparecen solo al final de los párrafos. En el original, whale.txt
el texto se ajusta a un ancho de 74 caracteres, por lo que debe predecir el final de cada línea y el texto. Esto hace que el desafío sea más complicado, por lo que whale2.txt
se recomienda para nuevas respuestas. Ambos archivos son del mismo tamaño, 1215236 bytes.
Para resumir, todas las respuestas deben incluir lo siguiente:
- Su presentación en sí. (El código, más cualquier archivo de datos que use; estos pueden ser enlaces si son grandes).
- Una explicación de cómo funciona su código. Explique el método de E / S y cómo predice el siguiente carácter. La explicación de su algoritmo es importante, y las buenas explicaciones me darán recompensas.
- El código que usó para evaluar su puntaje. (Si esto es idéntico a una respuesta anterior, simplemente puede vincularla).
- Cualquier código que utilizó para generar su envío, junto con una explicación de ese código. Esto incluye el código que utilizó para optimizar los parámetros, generar archivos de datos, etc. (Esto no cuenta para el recuento de bytes, pero debe incluirse en su respuesta).
Tabla de clasificación
Recompensas
De vez en cuando ofreceré recompensas para alentar diferentes enfoques.
El primero, 50 puntos, fue otorgado a A. Rex por la respuesta con mejor puntuación en ese momento.
El segundo, 100 puntos, también fue otorgado a A. Rex, por la misma respuesta, porque agregaron una muy buena explicación a su respuesta existente.
La próxima recompensa, 200 puntos , se otorgará a cualquiera
- Una respuesta competitiva que utiliza una nueva técnica. (Esto se basará en mi juicio subjetivo ya que es mi representante el que entra en la recompensa, pero puedes confiar en mí para ser justo. ¡Ten en cuenta que tu respuesta debe contener una explicación suficiente para que entienda cómo funciona!) No tome el puntaje más alto, solo necesita hacerlo razonablemente bien en comparación con las respuestas existentes. Estoy particularmente interesado en ver soluciones basadas en redes neuronales recurrentes, pero otorgaré la recompensa a cualquier cosa que parezca lo suficientemente diferente de los modelos de Markov que dominan las puntuaciones más altas actuales.
O:
- Cualquier otra persona que supere la puntuación máxima de A. Rex (actualmente 444444), utilizando cualquier método.
Una vez que se reclame la recompensa de 200 puntos, lo más probable es que ofrezca una de 400 puntos, actualizando los requisitos en consecuencia.
fuente
Respuestas:
/// , 2 * 1 + 1020874 = 1020876
Imprime un espacio.
fuente
Nodo.js, 2 * 224 + 524279 = 524727
Consulte el registro de cambios al final de esta publicación para obtener actualizaciones de puntaje.
Una función que toma y devuelve un byte.
Consiste en un modelo PPM simple que mira los últimos 8 caracteres para predecir el siguiente.
Confiamos en un patrón de longitud L cuando lo hemos encontrado al menos T [L] veces, donde T es una matriz de umbrales arbitrarios: [1,1,2,1,2,3,5,2] . Además, siempre confiamos en un patrón cuyo primer carácter coincida
[A-Z '"(]
.Seleccionamos el patrón de confianza más largo y devolvemos la predicción con la puntuación más alta asociada a este patrón en el momento de la llamada.
Notas
Obviamente, esto no está optimizado para la velocidad, pero se ejecuta en aproximadamente 15 segundos en mi computadora portátil.
Si se nos permitiera repetir el proceso varias veces seguidas sin restablecer el modelo, el número de errores convergería a ~ 268000 después de 5 iteraciones.
La tasa de éxito actual de la función de predicción es ~ 56.8%. Como notó @immibis en los comentarios, si las conjeturas incorrectas y correctas se mezclan, el resultado ni siquiera es legible.
Por ejemplo, este fragmento cerca del final del libro:
se convierte en:
Al reemplazar las conjeturas erróneas con guiones bajos, tenemos una mejor idea de lo que funcionó correctamente:
NB : El ejemplo anterior se creó con una versión anterior del código, trabajando en la primera versión del archivo de entrada.
Código de prueba
Cambiar registro
fuente
sidg tlanses,oeth to, shuld hottut tild aoersors Ch, th! Sa, yr! Sheu arinning whales aut ihe e sl he traaty of rrsf tg homn Bho dla tiasot a shab sor ty, af etoors tnd hocket sh bts ait mtubb tiddin tis aeewnrs, dnhost maundy cnd sner aiwt d boelh cheugh -aaieiyns aasiyns taaeiins! th, tla
. Se las arregla para obtener algunas palabras completas a veces. Al igualwhales
.Perl, 2 · 70525 + 326508 = 467558
Vaticinador
Para ejecutar este programa, necesita este archivo aquí , que debe nombrarse
B
. (Puede cambiar este nombre de archivo en la segunda instancia del carácterB
anterior). Consulte a continuación cómo generar este archivo.El programa utiliza una combinación de modelos de Markov esencialmente como en esta respuesta del usuario 2699 , pero con algunas pequeñas modificaciones. Esto produce una distribución para el siguiente personaje. Utilizamos la teoría de la información para decidir si aceptar un error o gastar bits de almacenamiento en
B
sugerencias de codificación (y si es así, cómo). Usamos codificación aritmética para almacenar de manera óptima bits fraccionales del modelo.El programa tiene una longitud de 582 bytes (incluida una nueva línea final innecesaria) y el archivo binario
B
tiene una longitud de 69942 bytes, por lo que, según las reglas para la puntuación de varios archivos , calificamosL
como 582 + 69942 + 1 = 70525.El programa casi seguramente requiere una arquitectura de 64 bits (little-endian?). Se tarda aproximadamente 2,5 minutos en ejecutarse en una
m5.large
instancia en Amazon EC2.Código de prueba
El arnés de prueba asume que el envío está en el archivo
submission.pl
, pero esto se puede cambiar fácilmente en la segunda línea.Comparación de texto
Esta muestra (elegida en otra respuesta ) aparece bastante tarde en el texto, por lo que el modelo está bastante desarrollado en este punto. Recuerde que el modelo está aumentado por 70 kilobytes de "pistas" que lo ayudan directamente a adivinar los caracteres; no está impulsado simplemente por el breve fragmento de código anterior.
Generando pistas
El siguiente programa acepta el código de envío exacto anterior (en la entrada estándar) y genera el
B
archivo exacto anterior (en la salida estándar):Se tarda aproximadamente el tiempo de ejecución que el envío, ya que realiza cálculos similares.
Explicación
En esta sección, intentaremos describir lo que hace esta solución con suficiente detalle para que usted pueda "probarla en casa". La técnica principal que diferencia esta respuesta de las otras es algunas secciones más abajo como mecanismo de "rebobinado", pero antes de llegar allí, necesitamos establecer los conceptos básicos.
Modelo
El ingrediente básico de la solución es un modelo de lenguaje. Para nuestros propósitos, un modelo es algo que toma una cantidad de texto en inglés y devuelve una distribución de probabilidad en el siguiente carácter. Cuando usamos el modelo, el texto en inglés será un prefijo (correcto) de Moby Dick. Tenga en cuenta que el resultado deseado es una distribución , y no solo una suposición para el personaje más probable.
En nuestro caso, esencialmente utilizamos el modelo en esta respuesta por user2699 . No utilizamos el modelo de la respuesta con la puntuación más alta (que no sea la nuestra) de Anders Kaseorg precisamente porque no pudimos extraer una distribución en lugar de una sola conjetura. En teoría, esa respuesta calcula una media geométrica ponderada, pero obtuvimos resultados algo pobres cuando lo interpretamos demasiado literalmente. "Robamos" un modelo de otra respuesta porque nuestra "salsa secreta" no es el modelo sino el enfoque general. Si alguien tiene un modelo "mejor", entonces debería poder obtener mejores resultados utilizando el resto de nuestras técnicas.
Como comentario, la mayoría de los métodos de compresión como Lempel-Ziv pueden verse como un "modelo de lenguaje" de esta manera, aunque uno podría tener que entrecerrar los ojos un poco. (¡Es particularmente complicado para algo que hace una transformación de Burrows-Wheeler!) Además, tenga en cuenta que el modelo por user2699 es una modificación de un modelo de Markov; esencialmente nada más es competitivo para este desafío o tal vez incluso modelar texto en general.
Arquitectura general
A los efectos de la comprensión, es bueno dividir la arquitectura general en varias piezas. Desde la perspectiva de más alto nivel, debe haber un poco de código de administración de estado. Esto no es particularmente interesante, pero para completar, queremos enfatizar que en cada punto al programa se le pide la siguiente suposición, tiene disponible un prefijo correcto de Moby Dick. No utilizamos nuestras conjeturas incorrectas pasadas de ninguna manera. En aras de la eficiencia, el modelo de lenguaje probablemente puede reutilizar su estado de los primeros N caracteres para calcular su estado para los primeros (N + 1) caracteres, pero en principio, podría volver a calcular las cosas desde cero cada vez que se invoca.
Dejemos a un lado este "controlador" básico del programa y echemos un vistazo dentro de la parte que adivina el siguiente personaje. Ayuda conceptualmente a separar tres partes: el modelo de lenguaje (discutido anteriormente), un archivo de "pistas" y un "intérprete". En cada paso, el intérprete le pedirá al modelo de lenguaje una distribución para el siguiente carácter y posiblemente leerá alguna información del archivo de sugerencias. Luego combinará estas partes en una suposición. Más adelante se explicará con precisión qué información está en el archivo de sugerencias y cómo se usa, pero por ahora ayuda a mantener estas partes separadas mentalmente. Tenga en cuenta que, en cuanto a la implementación, el archivo de sugerencias es literalmente un archivo separado (binario), pero podría haber sido una cadena o algo almacenado dentro del programa. Como una aproximación,
Si se está utilizando un método de compresión estándar como bzip2 como en esta respuesta , el archivo de "sugerencias" corresponde al archivo comprimido. El "intérprete" corresponde al descompresor, mientras que el "modelo de lenguaje" es un poco implícito (como se mencionó anteriormente).
¿Por qué usar un archivo de pistas?
Elija un ejemplo simple para analizar más a fondo. Suponga que el texto tiene
N
caracteres largos y bien aproximados por un modelo en el que cada carácter es (independientemente) la letraE
con probabilidad ligeramente inferior a la mitad, deT
manera similar con probabilidad ligeramente inferior a la mitad yA
con probabilidad 1/1000 = 0.1%. Supongamos que no hay otros personajes posibles; en cualquier caso, elA
es bastante similar al caso de un personaje nunca antes visto de la nada.Si operamos en el régimen L 0 (como lo hacen la mayoría, pero no todas, las otras respuestas a esta pregunta), no hay mejor estrategia para el intérprete que elegir una de
E
yT
. En promedio, obtendrá aproximadamente la mitad de los caracteres correctos. Entonces E ≈ N / 2 y el puntaje ≈ N / 2 también. Sin embargo, si usamos una estrategia de compresión, podemos comprimir a un poco más de un bit por carácter. Como L se cuenta en bytes, obtenemos L ≈ N / 8 y, por lo tanto, puntaje ≈ N / 4, el doble de bueno que la estrategia anterior.Lograr esta tasa de un poco más de un bit por carácter para este modelo es ligeramente no trivial, pero un método es la codificación aritmética.
Codificación aritmética
Como es comúnmente conocido, una codificación es una forma de representar algunos datos usando bits / bytes. Por ejemplo, ASCII es una codificación de 7 bits / caracteres de texto en inglés y caracteres relacionados, y es la codificación del archivo Moby Dick original en consideración. Si algunas letras son más comunes que otras, entonces una codificación de ancho fijo como ASCII no es óptima. En tal situación, muchas personas buscan la codificación de Huffman . Esto es óptimo si desea un código fijo (sin prefijo) con un número entero de bits por carácter.
Sin embargo, la codificación aritmética es aún mejor. En términos generales, es capaz de usar bits "fraccionales" para codificar información. Hay muchas guías de codificación aritmética disponibles en línea. Omitiremos los detalles aquí (especialmente de la implementación práctica, que puede ser un poco complicada desde una perspectiva de programación) debido a los otros recursos disponibles en línea, pero si alguien se queja, tal vez esta sección se pueda desarrollar más.
Si uno tiene texto realmente generado por un modelo de lenguaje conocido, entonces la codificación aritmética proporciona una codificación esencialmente óptima del texto de ese modelo. En cierto sentido, esto "resuelve" el problema de compresión para ese modelo. (Por lo tanto, en la práctica, el problema principal es que el modelo no se conoce, y algunos modelos son mejores que otros para modelar texto humano). Si no se permitió cometer errores en este concurso, entonces en el lenguaje de la sección anterior , una forma de producir una solución a este desafío habría sido usar un codificador aritmético para generar un archivo de "sugerencias" a partir del modelo de lenguaje y luego usar un decodificador aritmético como "intérprete".
En esta codificación esencialmente óptima, terminamos gastando -log_2 (p) bits para un personaje con probabilidad p, y la tasa de bits general de la codificación es la entropía de Shannon . Esto significa que un carácter con probabilidad cercana a 1/2 toma aproximadamente un bit para codificar, mientras que uno con probabilidad 1/1000 toma aproximadamente 10 bits (porque 2 ^ 10 es aproximadamente 1000).
Pero la métrica de puntuación para este desafío fue bien elegida para evitar la compresión como la estrategia óptima. Tendremos que encontrar alguna forma de cometer algunos errores como compensación para obtener un archivo de sugerencias más corto. Por ejemplo, una estrategia que podría probarse es una estrategia de ramificación simple: generalmente intentamos usar la codificación aritmética cuando podemos, pero si la distribución de probabilidad del modelo es "mala" de alguna manera, simplemente adivinamos el carácter más probable y no lo hacemos ' Intenta codificarlo.
¿Por qué cometer errores?
Analicemos el ejemplo anterior para motivar por qué podríamos querer cometer errores "intencionalmente". Si usamos codificación aritmética para codificar el carácter correcto, gastaremos aproximadamente un bit en el caso de un
E
oT
, pero unos diez bits en el caso de unA
.En general, esta es una codificación bastante buena, gastando un poco más de un bit por personaje, aunque hay tres posibilidades; básicamente,
A
es bastante improbable y no terminamos gastando sus diez bits correspondientes con demasiada frecuencia. Sin embargo, ¿no sería bueno si pudiéramos cometer un error en el caso de unA
? Después de todo, la métrica para el problema considera que 1 byte = 8 bits de longitud son equivalentes a 2 errores; Por lo tanto, parece que uno debería preferir un error en lugar de gastar más de 8/2 = 4 bits en un personaje. ¡Gastar más de un byte para guardar un error definitivamente suena subóptimo!El mecanismo de "rebobinado"
Esta sección describe el principal aspecto inteligente de esta solución, que es una forma de manejar conjeturas incorrectas sin costo alguno.
Por el simple ejemplo que hemos estado analizando, el mecanismo de rebobinado es particularmente sencillo. El intérprete lee un bit del archivo de sugerencias. Si es un 0, adivina
E
. Si es un 1, adivinaT
. La próxima vez que se llame, verá cuál es el carácter correcto. Si el archivo de sugerencias está bien configurado, podemos asegurarnos de que en el caso de unaE
oT
, el intérprete adivine correctamente. ¿Pero que pasaA
? La idea del mecanismo de rebobinado es simplemente no codificarA
en absoluto . Más precisamente, si el intérprete luego se entera de que el carácter correcto era unA
, metafóricamente " rebobina la cinta": devuelve el bit que leyó anteriormente. El bit que lee tiene la intención de codificarE
oT
, Pero no ahora; Se usará más tarde. En este simple ejemplo, esto básicamente significa que sigue adivinando el mismo personaje (E
oT
) hasta que lo haga bien; luego lee otro bit y continúa.La codificación para este archivo de sugerencias es muy simple: convierta todos los
E
s en 0 bitsT
ys en 1 bits, todo mientras ignora losA
s por completo. Mediante el análisis al final de la sección anterior, este esquema comete algunos errores pero reduce el puntaje general al no codificar ninguno de losA
s. Como efecto más pequeño, también ahorra en la longitud del archivo de sugerencias, porque terminamos usando exactamente un bit para cada unoE
yT
, en lugar de un poco más de un bit.Un pequeño teorema
¿Cómo decidimos cuándo cometer un error? Supongamos que nuestro modelo nos da una distribución de probabilidad P para el siguiente carácter. Vamos a separar los posibles personajes en dos clases: codificados y no codificados . Si el carácter correcto no está codificado, terminaremos usando el mecanismo de "rebobinado" para aceptar un error sin costo alguno. Si se codifica el carácter correcto, usaremos alguna otra distribución Q para codificarlo mediante codificación aritmética.
Pero, ¿qué distribución Q deberíamos elegir? No es demasiado difícil ver que los caracteres codificados deberían tener una probabilidad más alta (en P) que los caracteres no codificados. Además, la distribución Q solo debe incluir los caracteres codificados; después de todo, no estamos codificando los otros, por lo que no deberíamos estar "gastando" entropía en ellos. Es un poco más complicado ver que la distribución de probabilidad Q debería ser proporcional a P en los caracteres codificados. Poner estas observaciones juntas significa que debemos codificar los caracteres más probables pero posiblemente no los caracteres menos probables, y que Q es simplemente P reescalado en los caracteres codificados.
Además, resulta que hay un teorema genial sobre qué "corte" se debe elegir para los caracteres de codificación: debe codificar un carácter siempre que sea al menos 1 / 5.393 tan probable como los otros caracteres codificados combinados. Esto "explica" la aparición de la constante aparentemente aleatoria
5.393
más cerca del final del programa anterior. El número 1 / 5.393 ≈ 0.18542 es la solución a la ecuación -p log (16) - p log p + (1 + p) log (1 + p) = 0 .Quizás sea una idea razonable escribir este procedimiento en código. Este fragmento está en C ++:
Poniendolo todo junto
Desafortunadamente, la sección anterior es un poco técnica, pero si juntamos todas las otras piezas, la estructura es la siguiente. Cada vez que se le pide al programa que prediga el siguiente carácter después de un carácter correcto dado:
La codificación del archivo de sugerencias funciona de manera similar. En ese caso, el programa sabe cuál es el siguiente carácter correcto. Si es un carácter que debe codificarse, entonces, por supuesto, uno debe usar el codificador aritmético en él; pero si es un carácter no codificado, simplemente no actualiza el estado del codificador aritmético.
Si comprende los antecedentes teóricos de la información, como las distribuciones de probabilidad, la entropía, la compresión y la codificación aritmética, pero intentó y no entendió esta publicación (excepto por qué el teorema es verdadero), háganoslo saber y podemos intentar aclarar las cosas. ¡Gracias por leer!
fuente
B
archivo? Si es así, ¿puede incluir eso en su respuesta?Python 3, 2 · 267 + 510193 = 510727
Vaticinador
Utiliza una combinación bayesiana ponderada del orden 0,…, 16 modelos de Markov, con pesos [1, 6, 12, 30, 65, 99, 87, 117, 118, 89, 95, 118, 96, 184, 126, 219, 126].
El resultado no es muy sensible a la selección de estos pesos, pero los optimicé porque pude, utilizando el mismo algoritmo de escalada de aceptación tardía que utilicé en mi respuesta a "Formar una mayoría del Senado" , donde cada mutación candidata es solo un incremento de ± 1 a un solo peso.
Código de prueba
fuente
b"\0\3\6\r\34'&-20'\22!P\n[\26"
es la representación ascii de los pesos, donde se escapan pequeños valores no imprimibles en octal.Python 3 ,
2 * 279 + 592920 = 5934782 * 250 + 592467 = 5929672 * 271 + 592084 = 5926262 * 278 + 592059 = 5926152 * 285 + 586660 = 5872302 * 320 + 585161 = 5858012 * 339 + 585050 = 585728Pruébalo en línea!
Una función que usa variables globales. A medida que avanza, construye un modelo a nivel de palabra: dado lo que se ha visto hasta ahora en esta palabra , ¿cuál es el siguiente carácter más común? A medida que entra más información, aprende palabras comunes del texto bastante bien, y también aprende el carácter más común para comenzar la siguiente palabra.
Por ejemplo:
Al principio no funciona muy bien, pero al final salen grandes partes de palabras reales. La opción de reserva es un espacio, y después de un solo espacio es una "a", a menos que la letra anterior sea una de "nedtfo", un dígito o un guión o apóstrofe. También predice agresivamente los saltos de línea después de 71 caracteres, o si se esperaba un espacio después de 66. Ambos se ajustaron a los datos ("t" es mucho más común después de un espacio, pero ya se ha predicho más a menudo, así que " un "es una mejor suposición fuera de esos seis casos especiales).
Aprender qué pares de palabras se unieron y presentar el mapeo resultó no valer la pena.
Termina con un texto como este:
que corresponde a esta parte de la entrada:
Puedes ver dónde los sustantivos propios en particular salen bastante bien, pero los extremos de las palabras también son en su mayoría correctos. Cuando se ve "dou" espera "duda", pero una vez que aparece la "l" se pone "doblón".
Si lo ejecuta por segunda vez con el mismo modelo que acaba de construir, inmediatamente obtiene otros 92k correctos (51.7% -> 59.3%), pero siempre es inferior al 60% desde la segunda iteración.
El código de medición está en el enlace TIO, o aquí hay una versión un poco mejor:
guess.txt
tiene la salida adivinada al final.fuente
C ++, puntaje: 2 * 132 + 865821 = 866085
¡Gracias a @Quentin por guardar 217 bytes!
Una solución muy simple que, dado un carácter, solo genera el carácter que aparece con más frecuencia después del carácter de entrada.
Verifique el puntaje con:
Editar: el uso
whale2.txt
da una mejor puntuación.fuente
L
guardar un montón de caracteres :)Python, 2 * 516 + 521122 = 522154
Algoritmo:
Otra presentación de Python, este algoritmo calcula la próxima letra más probable mirando secuencias de longitud 1, ..., l. Se usa la suma de probabilidades, y hay algunos trucos para obtener mejores resultados.
Resultados:
Principalmente galimatías, aunque puede ver que se repite en la frase ocasional, como "Padre Mapple".
Código de prueba:
Bastante simple, muestra algunos ejemplos del texto en diferentes puntos. Utiliza whale2.txt, ya que esto evita cierta lógica adicional para calcular nuevas líneas.
fuente
C (gcc) ,
6797876528928476 bytes,679619652740 conjeturas incorrectasPruébalo en línea!
Actualización: ~ 27000 puntos con archivo actualizado, 16 puntos (8 bytes) con una función mejor desarrollada.
Explicación
La forma en que esto funciona es que a medida que el código se ejecuta a través del texto, memoriza el último carácter que finalizó cualquier secuencia de 4 caracteres y devuelve ese valor. Algo similar al enfoque de Arnauld anterior, pero se basa en la probabilidad inherente de que dos secuencias de 4 caracteres dadas terminen de la misma manera.
De-golf:
fuente
sh + bzip2, 2 * 364106 = 728212
2 * 381249 + 0 = 762498seguido por el archivo bzip2-comprimido whale2.txt con el primer byte perdido
Ignora su entrada; da como resultado la respuesta correcta. Esto proporciona una línea de base en un extremo; daniero proporciona una línea de base en el otro extremo.
Script de constructor:
Arnés de prueba de E / S (tcc; corte la primera línea para gcc). Cualquiera puede utilizar este arnés de prueba en una plataforma adecuada que envíe un programa completo que espere lectura / escritura de E / S. Utiliza la E / S byte-at-a-time para evitar trampas. El programa secundario debe vaciar la salida después de cada byte para evitar el bloqueo.
fuente
but may not load any other external files, and your code may not access the whale.txt file in any way other than described above.
cláusula?nth
tiempo, se le dará el enésimo carácter dewhale.txt
owhale2.txt
y debe generar su conjetura para el(n+1)th
personaje." - ¿Cómo se cumple este requisito? El código muestra el texto completo dewhale.txt
cada vez que se ejecuta.Python 3 , 879766
Pruébalo en línea!
... Los
///
respuesta que imprime un espacio obtiene 10 votos a favor, mientras que mi código solo puede obtener 3 ...Explicación:
Para cada personaje, el programa:
frequency[prev][char]
frequency[char]
que tienen la puntuación total
Como no hay forma de cargar un archivo grande en TIO (excepto preguntarle a Dennis), el ejemplo que se ejecuta en el enlace TIO solo ejecuta el programa para una pequeña parte del texto.
En comparación con la respuesta anterior, esta tiene 362 caracteres incorrectos más, pero el código es más corto en 255 bytes. El multiplicador hace que mi presentación tenga una puntuación más baja.
fuente
C #, 378 * 2 + 569279 = 570035
Este enfoque utiliza una tabla de búsqueda para aprender el carácter más común que sigue a una cadena dada. Las teclas de la tabla de búsqueda tienen un máximo de 4 caracteres, por lo que la función primero actualiza la tabla de búsqueda con el carácter actual y luego simplemente verifica qué carácter es más probable que suceda después de los 4 anteriores, incluido el actual. . Si esos 4 caracteres no se encuentran en la tabla de búsqueda, imprime un espacio.
Esta versión utiliza el
whale2.txt
archivo, ya que mejora en gran medida el número de conjeturas exitosas.El siguiente es el código utilizado para probar la clase:
El código se ejecuta en apenas 2 segundos. Solo para el registro, esto es lo que obtengo cuando modifico el tamaño de las claves de la tabla de búsqueda (incluye los resultados de una segunda ejecución sin restablecer el modelo):
Sería interesante saber por qué un tamaño de clave de 4 caracteres es la mejor opción en este algoritmo.
Comparación de texto
Original:
Recreado:
Suposiciones:
Cambiar registro
whale2.txt
y, por lo tanto, se eliminó la optimización.fuente
Java 7, 1995 caracteres, (1995 * 2 + 525158) 529148
Java es una mierda para programas pequeños. De todos modos, probé varios enfoques extremadamente complejos y complicados que produjeron resultados sorprendentemente malos. Posteriormente volví y simplemente hice un enfoque simple, que resultó en un tamaño de programa más pequeño y mejores resultados.
Este enfoque es realmente extremadamente simple. Alimenta ciegamente los caracteres x anteriores (además de todas las subcadenas de esos caracteres) en una tabla hash, asignada al carácter actual. Luego realiza un seguimiento de qué patrones predicen con mayor precisión el carácter actual. Si los patrones que preceden a ciertos personajes se encuentran varias veces, tienen éxito en predecir el personaje. Da prioridad a las cadenas más largas y da prioridad a cualquier carácter que siga con mayor frecuencia una cadena dada. Este algoritmo no sabe nada sobre el tipo de documento o el idioma inglés.
Me decidí a usar 9 caracteres e intentar unir palabras enteras con esos 9 caracteres anteriores cuando sea posible. Cuando no intenta hacer una coincidencia de palabras dentro de las cadenas, la longitud óptima es de 6 caracteres, produciendo varios miles de predicciones más.
Una observación interesante fue que el uso de 20 caracteres resultó en malas predicciones la primera vez, pero con una precisión del 99.9 por ciento en los pases posteriores. El algoritmo fue básicamente capaz de memorizar el libro en trozos superpuestos de 20 bytes, y esto fue lo suficientemente distinto como para permitirle recordar todo el libro de un personaje a la vez.
fuente
Python 3,
2 × 497 + 619608 = 6206022 × 496 + 619608 = 620600Intenté esto de forma independiente, pero terminé con lo que efectivamente es una versión inferior de la respuesta de Michael Homer. Espero que eso no vuelva mi respuesta completamente obsoleta.
Esto construye con el tiempo un diccionario de palabras (definido crudamente como cadenas terminadas por
o
\n
, distingue entre mayúsculas y minúsculas e incluye puntuación). Luego busca en este diccionario palabras que comiencen con lo que hasta ahora conoce de la palabra actual, ordena la lista resultante por frecuencia de ocurrencia (lentamente) y adivina que el siguiente carácter es el siguiente en la palabra coincidente más común. Si ya tenemos la palabra coincidente más común, o ya no existe una palabra coincidente, devuelve.
También crea un diccionario repugnantemente ineficiente de pares de palabras. Al llegar al límite de una palabra, adivina que el siguiente carácter es la primera letra de la segunda palabra en el par de palabras coincidentes más comunes, o
t
si no hay una coincidencia. Sin embargo, no es muy inteligente. A continuaciónMoby
, el programa adivina correctamente que el siguiente personaje esD
, pero luego olvida todo sobre el contexto y generalmente termina llamando a la ballena "Pato Moby" (porque la palabra "holandés" parece ser más frecuente en la primera mitad del texto). ) Sería fácil solucionar esto priorizando los pares de palabras sobre las palabras individuales, pero espero que la ganancia sea marginal (ya que generalmente es correcta desde el tercer carácter en adelante, y los pares de palabras no son tan útiles en primer lugar).Podría ajustar esto para que coincida mejor con el texto proporcionado, pero no creo que el ajuste manual del algoritmo basado en el conocimiento previo de la entrada esté realmente en el espíritu del juego, aparte de elegir t como el personaje alternativo después de un espacio ( y probablemente tampoco debería haber hecho eso), lo evité. Ignoré la longitud de línea conocida del archivo de entrada y en su lugar inserté
\n
después de cada 13 espacios; esto es casi seguro una coincidencia muy pobre, la intención principal era mantener la longitud de línea razonable en lugar de coincidir con la entrada.El código no es exactamente rápido (~ 2 horas en mi máquina), pero en general obtiene la mitad de los caracteres correctos (49%). Espero que el puntaje sea marginalmente mejor si se ejecuta
whale2.txt
, pero no lo he hecho.El inicio de la salida se ve así:
pero al final, se parece un poco más a ... algo. Mi pasaje favorito casi al final del libro,
sale como
Eso habría hecho que The Wrath of Khan fuera mucho más confuso. Y "solitario" → "hormigueo" es una sustitución particularmente satisfactoria.
Editar: guardado un byte eliminando un espacio extraño
Puntuación
Esto ejecuta el programa para el texto de Moby Dick y genera el texto "predicho" en stdout, y abusa de stderr para escribir la partitura. Recomiendo redirigir la salida a un archivo.
fuente
lambda i:i[1]
sería más barato que tratar conoperator
?C ++, 2 · 62829 + 318786 = 444444
Para ejecutar este programa, necesita este archivo aquí , que debe nombrarse
C
.El programa usa la misma combinación de modelos de Markov que en nuestra respuesta anterior . Como antes, esta combinación es esencialmente el modelo de esta respuesta del usuario 2699 , pero con algunas pequeñas modificaciones.
Al ver cómo esta respuesta usa exactamente el mismo modelo que antes, la mejora es un mejor mecanismo teórico de información que el mecanismo de "rebobinado" descrito anteriormente. Esto le permite cometer menos errores y al mismo tiempo tener una longitud combinada más pequeña. El programa en sí no se juega mucho al golf porque no es el principal contribuyente a la puntuación.
El programa tiene una longitud de 2167 bytes (incluidas todas las pestañas para la sangría y muchos otros caracteres innecesarios, pero antes del código de prueba), y el archivo binario
C
tiene una longitud de 60661 bytes, por lo que, según las reglas para calificar múltiples archivos , calificamosL
como 2167 + 60661 + 1 = 62829.El programa tarda aproximadamente 8 minutos en ejecutarse en una
m5.4xlarge
instancia en Amazon EC2 y utiliza un poco más de 16 GB de memoria. (Este uso excesivo de memoria no es necesario, simplemente tampoco lo optimizamos).fuente
Python 3, 526640
274 bytes, errores 526092 (usando
whale2.txt
). Esto definitivamente es capaz de mejorar aún más, pero ha alcanzado la etapa de "lo suficientemente bueno para publicar".La idea es almacenar las frecuencias de todas las ejecuciones de 2, 3, 4, ..., 10 caracteres. Para cada una de estas longitudes L, verificamos si los caracteres L-1 más recientes coinciden con un patrón almacenado; si es así, nuestra suposición g L es el siguiente carácter más frecuente que sigue ese patrón. Recopilamos hasta nueve conjeturas de esta manera. Para decidir qué suposición usar, ponderamos la frecuencia de cada patrón por su longitud hasta la octava potencia. Se elige la suposición con la mayor suma de frecuencias ponderadas. Si no hay patrones que coincidan, adivinamos el espacio.
(La longitud máxima del patrón y el exponente de ponderación se eligieron por prueba y error para dar la menor cantidad de conjeturas incorrectas).
Aquí está mi versión sin trabajo de trabajo en progreso:
Y el arnés de prueba:
Aquí hay algunos resultados de muestra desde cerca del comienzo del texto. Ya empezamos a ver la posibilidad de terminar las palabras comunes después de ver su primera carta (
in
,to
,and
,by
, también, al parecer,school
).Cerca del final, todavía hay muchos errores, pero también muchas secuencias muy buenas (
shmage seashawks
por ejemplo).Es interesante observar algunos de los errores y adivinar qué palabra "esperaba" el algoritmo. Por ejemplo, después
sail
, el programa dos veces prediceo
--parasailor
, supongo. O de nuevo, después de, a
lon
esperado, posiblemente debido a la ocurrencia común de, and
.Registro de cambios:
fuente
Python 2, puntaje: 2 * (407 + 56574) + 562262 = 676224
Las búsquedas de palabras que coinciden con los caracteres anteriores a partir de una lista de
todas lasmayoría de las palabras utilizadas en el texto, ordenados por el número de sus apariciones.Código:
Datos: https://www.dropbox.com/s/etmzi6i26lso8xj/d?dl=0
Banco de pruebas:
Editar: el uso
whale2.txt
da una mejor puntuación.fuente
C ++ (CCG), 725 × 2 + 527076 = 528526
Otra presentación de frecuencia de prefijo. Sigue corriendo
whale2.txt
y obtén un puntaje similar (ligeramente peor) que otros.Este busca con avidez la cadena más larga que comienza con un sufijo de la historia, y si hay múltiples candidatos, desempate con cadenas más cortas.
Por ejemplo: si los últimos 7 caracteres son
abcdefgh
, y la cadenaabcdefghi
yabcdefghj
aparece con la frecuencia más grande en todas las cadenas de la formaabcdefgh*
, la salida serái
oj
, desempate con sufijos más cortos (bcdefgh
,cdefgh
, ...).Por razones desconocidas, nada más que 7 y mi computadora no tiene suficiente RAM para ejecutarlo. Incluso con 7, necesito cerrar todos los navegadores web para ejecutarlo.
Código de prueba:
Sin golf:
Salida de ejemplo:
Este está cerca del final del texto. La mayoría de las palabras largas se predicen con bastante precisión (
intervals
,pivot-hole
,distance
)Las mayúsculas no parecen buenas.
fuente
Python 2, 756837
Usa algo que podría ser cadenas de Markov?
fuente
zlib.decompress('...')
evalúa{'G?':' ', 'G;':' ','G"':' ',.......}
ya
es un diccionario que asigna de 2 caracteres a 1 carácter. Básicamente, variante de 2 caracteres de la respuesta de Steadybox .xxd
,hexdump
,uuencode
, O similaresHaskell, (1904 + 1621 + 208548 + 25646) * 2 + 371705 = 847143
Ejemplo:
Utiliza tres archivos auxiliares precalculados:
seqwords
contiene las 236 palabras más comunes.wordsseq
contiene una secuencia comprimida de LZMA de estas palabras, y para todas las palabras que no están entre las 236 más comunes, la longitud.dicttries
contiene, para cada longitud de palabra, un árbol de decisión que contiene todas las palabras restantes. De estos intentos, las entradas se seleccionan a medida que avanzamos.De esta manera, logramos una tasa de error significativamente menor que todos los otros esquemas con pérdidas; Desafortunadamente, el
wordsseq
archivo sigue siendo demasiado grande para ser competitivo.Aquí hay una versión completa que crea los archivos y realiza el análisis:
fuente
C ++ (WIP), 1923 * 2 + 1017344 = 1021190
Tal como están las cosas, esta solución es WIP y, por lo tanto, no tiene golf. También teniendo en cuenta que el tamaño real del código apenas tiene ningún impacto en la puntuación, pensé que publicaría mi respuesta primero antes de comenzar a optimizarlo.
(Código completo disponible aquí: https://github.com/BrainStone/MobyDickRNG - Incluye programa completo y búsqueda de semillas)
Esta solución se basa en un RNG. Primero analizo el texto. Creo un mapa que cuenta las ocurrencias de dos caracteres consecutivos. Luego creo un mapa de distribución. Todo esto se hace estáticamente, por lo que debe estar de acuerdo con las reglas.
Luego, mientras intento imprimir el texto, busco y extraigo un carácter aleatorio de los posibles. Si bien esto generalmente produce peores resultados que simplemente generar la siguiente letra más común, es probable que haya semillas de Dios que produzcan mejores resultados. Es por eso que la semilla está codificada. Actualmente estoy buscando la mejor semilla. Y actualizaré esta respuesta una vez que encuentre mejores semillas. ¡Así que mantente informado!
Si alguien quiere buscar semillas por sí mismo o usar diferentes RNG, no dude en bifurcar el repositorio.
Método utilizado para calcular la puntuación: https://github.com/BrainStone/MobyDickRNG/blob/master/src/search.cpp#L15
Tenga en cuenta que aunque la puntuación total es la peor en este momento, supera el recuento de errores de solo generar espacios. Y hay muchas posibilidades de que el puntaje disminuya, al verificar más semillas.
Registro de cambios
Semillas marcadas: 0-50000. Puntuación: 2305 * 2 + 1017754 = 1022364
Semillas marcadas: 0-80000. Puntuación: 1920 * 2 + 1017754 = 1021594 (-770)
Semillas marcadas: 0-32000000. Puntuación: 1923 * 2 + 1017344 = 1021190 (-404)
fuente
Rubí, 1164418 (ay)
Solo quería ver qué tan bien podría hacerlo sin verificar ninguna otra respuesta.
No estoy seguro de si esto está permitido porque incluye un literal que generé al analizar el archivo, pero incluso si no fuera así, no es como si estuviera en peligro de vencer a alguien.
Como genere
x
Primero, generé
a.txt
con lo siguiente:Entonces generé
a.csv
:Luego lo analicé
x
con el siguiente script de Ruby:Cómo anoté
fuente
Python 3 , (146 * 2 + 879757) 880049 bytes
Pruébalo en línea!
Tabla de frecuencias bastante sencilla. Cada posición en la cadena corresponde al código ascii del carácter actual (menos 10 = 0x0a = '\ n', el carácter más bajo en el archivo), y el carácter en cada índice es el siguiente carácter más frecuente. Suponiendo que calculé las frecuencias correctamente ...
Probado con el código de la prueba de user202729
fuente
def f(c):return(" ">c)*c or"t ... e"[ord(c)-32]
?[Python 3] (644449 * 2 + 0) 1288898 puntos
Precisión perfecta en solo 644449 bytes
El código completo no puede caber en una respuesta, así que lo puse aquí y reemplacé el literal de cadena binaria grande con b '###' en el texto de respuesta.
Esto se genera con el siguiente código, donde "modified.py" es el archivo generado y "cheatsheet.txt" es el archivo whale2.txt que comienza en el segundo carácter.
El código se puede ejecutar agregando lo siguiente al final de "modified.py". "whale2.txt" debe estar en el mismo directorio que "modified.py", y la salida se escribirá en "out.txt".
Esta respuesta no accede directamente a whale.txt o whale2.txt. Hace uso de las bibliotecas de compresión estándar existentes según lo permitido explícitamente en las reglas.
fuente