Sobre la serie
En primer lugar, puede tratar esto como cualquier otro desafío de golf de código y responderlo sin preocuparse por la serie. Sin embargo, hay una tabla de clasificación en todos los desafíos. Puede encontrar la tabla de clasificación junto con más información sobre la serie en la primera publicación .
Aunque tengo un montón de ideas alineadas para la serie, los desafíos futuros aún no están establecidos en piedra. Si tiene alguna sugerencia, hágamelo saber en la publicación de sandbox relevante .
Hoyo 6: Tira un d20
Un dado muy común en los juegos de rol de mesa es el dado de veinte lados (un icosaedro , comúnmente conocido como d20 ). Es tu tarea tirar un dado. Sin embargo, si solo estuviera devolviendo un número aleatorio entre 1 y 20, eso sería un poco trivial. Entonces su tarea es generar una red aleatoria para un dado dado.
Utilizaremos la siguiente red:
Es una franja triangular, por lo que se puede representar fácilmente como una lista de enteros. Por ejemplo, si le dan la entrada:
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20]
Eso correspondería al siguiente dado (hecho divertido: esta es la red utilizada por Magic: the Gathering contadores de vida / dados de spin-down).
Sin embargo, esta no es la única red que representa este dado. Dependiendo de cómo desenrollemos las caras, hay 60 redes diferentes. Aquí hay dos más:
[1, 8, 9, 10, 2, 3, 4, 5, 6, 7, 17, 18, 19, 11, 12, 13, 14, 15, 16, 20]
[10, 9, 18, 19, 11, 12, 3, 2, 1, 8, 7, 17, 16, 20, 13, 14, 4, 5, 6, 15]
O gráficamente (no roté las etiquetas de la cara por simplicidad):
El reto
Dada una lista de enteros que representan un dado (como se describió anteriormente) y un entero N
, se obtienen de forma N
independiente, redes d20 uniformemente aleatorias correspondientes al dado dado. (Es decir, cada una de las 60 redes posibles debería tener la misma probabilidad de ser generada).
Por supuesto, debido a las limitaciones técnicas de los PRNG, será imposible una uniformidad perfecta. Para evaluar la uniformidad de su envío, se considerará que las siguientes operaciones producen distribuciones perfectamente uniformes:
- Obtener un número de un PRNG (sobre cualquier rango), que está documentado como (aproximadamente) uniforme.
- Mapear una distribución uniforme sobre un conjunto más grande de números en un conjunto más pequeño mediante módulo o multiplicación (o alguna otra operación que distribuya valores de manera uniforme). El conjunto más grande tiene que contener al menos 1024 veces tantos valores posibles como el conjunto más pequeño.
Dados estos supuestos, su algoritmo debe producir una distribución perfectamente uniforme.
Su programa debería ser capaz de generar 100 redes en menos de un segundo (por lo tanto, no intente generar redes aleatorias hasta que una corresponda con el dado indicado anteriormente).
Puede escribir un programa o función, tomando la entrada a través de STDIN (o la alternativa más cercana), argumento de línea de comando o argumento de función y generando el resultado a través de STDOUT (o la alternativa más cercana), el valor de retorno de la función o el parámetro de función (out).
La entrada y salida pueden estar en cualquier formato de lista plana conveniente, inequívoco. Puede suponer que los valores faciales de d20 son enteros distintos y positivos, que se ajustan al tipo de entero natural de su idioma.
Este es el código de golf, por lo que gana el envío más corto (en bytes). Y, por supuesto, la presentación más corta por usuario también entrará en la tabla de clasificación general de la serie.
Resultados de muestra
Para la entrada
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20]
Las 60 redes posibles (siempre que no haya cometido un error), sin ningún orden en particular, son:
[11, 10, 9, 18, 19, 20, 13, 12, 3, 2, 1, 8, 7, 17, 16, 15, 14, 4, 5, 6]
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20]
[8, 7, 17, 18, 9, 10, 2, 1, 5, 6, 15, 16, 20, 19, 11, 12, 3, 4, 14, 13]
[3, 12, 13, 14, 4, 5, 1, 2, 10, 11, 19, 20, 16, 15, 6, 7, 8, 9, 18, 17]
[3, 4, 5, 1, 2, 10, 11, 12, 13, 14, 15, 6, 7, 8, 9, 18, 19, 20, 16, 17]
[11, 19, 20, 13, 12, 3, 2, 10, 9, 18, 17, 16, 15, 14, 4, 5, 1, 8, 7, 6]
[4, 14, 15, 6, 5, 1, 2, 3, 12, 13, 20, 16, 17, 7, 8, 9, 10, 11, 19, 18]
[2, 10, 11, 12, 3, 4, 5, 1, 8, 9, 18, 19, 20, 13, 14, 15, 6, 7, 17, 16]
[4, 5, 1, 2, 3, 12, 13, 14, 15, 6, 7, 8, 9, 10, 11, 19, 20, 16, 17, 18]
[10, 2, 1, 8, 9, 18, 19, 11, 12, 3, 4, 5, 6, 7, 17, 16, 20, 13, 14, 15]
[3, 2, 10, 11, 12, 13, 14, 4, 5, 1, 8, 9, 18, 19, 20, 16, 15, 6, 7, 17]
[7, 8, 1, 5, 6, 15, 16, 17, 18, 9, 10, 2, 3, 4, 14, 13, 20, 19, 11, 12]
[13, 12, 11, 19, 20, 16, 15, 14, 4, 3, 2, 10, 9, 18, 17, 7, 6, 5, 1, 8]
[16, 15, 14, 13, 20, 19, 18, 17, 7, 6, 5, 4, 3, 12, 11, 10, 9, 8, 1, 2]
[15, 16, 17, 7, 6, 5, 4, 14, 13, 20, 19, 18, 9, 8, 1, 2, 3, 12, 11, 10]
[20, 13, 12, 11, 19, 18, 17, 16, 15, 14, 4, 3, 2, 10, 9, 8, 7, 6, 5, 1]
[5, 4, 14, 15, 6, 7, 8, 1, 2, 3, 12, 13, 20, 16, 17, 18, 9, 10, 11, 19]
[10, 11, 12, 3, 2, 1, 8, 9, 18, 19, 20, 13, 14, 4, 5, 6, 7, 17, 16, 15]
[4, 3, 12, 13, 14, 15, 6, 5, 1, 2, 10, 11, 19, 20, 16, 17, 7, 8, 9, 18]
[19, 20, 13, 12, 11, 10, 9, 18, 17, 16, 15, 14, 4, 3, 2, 1, 8, 7, 6, 5]
[1, 8, 9, 10, 2, 3, 4, 5, 6, 7, 17, 18, 19, 11, 12, 13, 14, 15, 16, 20]
[8, 1, 5, 6, 7, 17, 18, 9, 10, 2, 3, 4, 14, 15, 16, 20, 19, 11, 12, 13]
[18, 9, 8, 7, 17, 16, 20, 19, 11, 10, 2, 1, 5, 6, 15, 14, 13, 12, 3, 4]
[12, 3, 2, 10, 11, 19, 20, 13, 14, 4, 5, 1, 8, 9, 18, 17, 16, 15, 6, 7]
[2, 3, 4, 5, 1, 8, 9, 10, 11, 12, 13, 14, 15, 6, 7, 17, 18, 19, 20, 16]
[10, 9, 18, 19, 11, 12, 3, 2, 1, 8, 7, 17, 16, 20, 13, 14, 4, 5, 6, 15]
[9, 8, 7, 17, 18, 19, 11, 10, 2, 1, 5, 6, 15, 16, 20, 13, 12, 3, 4, 14]
[16, 17, 7, 6, 15, 14, 13, 20, 19, 18, 9, 8, 1, 5, 4, 3, 12, 11, 10, 2]
[17, 7, 6, 15, 16, 20, 19, 18, 9, 8, 1, 5, 4, 14, 13, 12, 11, 10, 2, 3]
[1, 5, 6, 7, 8, 9, 10, 2, 3, 4, 14, 15, 16, 17, 18, 19, 11, 12, 13, 20]
[9, 18, 19, 11, 10, 2, 1, 8, 7, 17, 16, 20, 13, 12, 3, 4, 5, 6, 15, 14]
[16, 20, 19, 18, 17, 7, 6, 15, 14, 13, 12, 11, 10, 9, 8, 1, 5, 4, 3, 2]
[5, 1, 2, 3, 4, 14, 15, 6, 7, 8, 9, 10, 11, 12, 13, 20, 16, 17, 18, 19]
[8, 9, 10, 2, 1, 5, 6, 7, 17, 18, 19, 11, 12, 3, 4, 14, 15, 16, 20, 13]
[13, 20, 16, 15, 14, 4, 3, 12, 11, 19, 18, 17, 7, 6, 5, 1, 2, 10, 9, 8]
[6, 15, 16, 17, 7, 8, 1, 5, 4, 14, 13, 20, 19, 18, 9, 10, 2, 3, 12, 11]
[6, 5, 4, 14, 15, 16, 17, 7, 8, 1, 2, 3, 12, 13, 20, 19, 18, 9, 10, 11]
[7, 6, 15, 16, 17, 18, 9, 8, 1, 5, 4, 14, 13, 20, 19, 11, 10, 2, 3, 12]
[19, 18, 17, 16, 20, 13, 12, 11, 10, 9, 8, 7, 6, 15, 14, 4, 3, 2, 1, 5]
[14, 15, 6, 5, 4, 3, 12, 13, 20, 16, 17, 7, 8, 1, 2, 10, 11, 19, 18, 9]
[17, 18, 9, 8, 7, 6, 15, 16, 20, 19, 11, 10, 2, 1, 5, 4, 14, 13, 12, 3]
[6, 7, 8, 1, 5, 4, 14, 15, 16, 17, 18, 9, 10, 2, 3, 12, 13, 20, 19, 11]
[14, 13, 20, 16, 15, 6, 5, 4, 3, 12, 11, 19, 18, 17, 7, 8, 1, 2, 10, 9]
[20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1]
[7, 17, 18, 9, 8, 1, 5, 6, 15, 16, 20, 19, 11, 10, 2, 3, 4, 14, 13, 12]
[15, 6, 5, 4, 14, 13, 20, 16, 17, 7, 8, 1, 2, 3, 12, 11, 19, 18, 9, 10]
[9, 10, 2, 1, 8, 7, 17, 18, 19, 11, 12, 3, 4, 5, 6, 15, 16, 20, 13, 14]
[2, 1, 8, 9, 10, 11, 12, 3, 4, 5, 6, 7, 17, 18, 19, 20, 13, 14, 15, 16]
[12, 13, 14, 4, 3, 2, 10, 11, 19, 20, 16, 15, 6, 5, 1, 8, 9, 18, 17, 7]
[17, 16, 20, 19, 18, 9, 8, 7, 6, 15, 14, 13, 12, 11, 10, 2, 1, 5, 4, 3]
[18, 17, 16, 20, 19, 11, 10, 9, 8, 7, 6, 15, 14, 13, 12, 3, 2, 1, 5, 4]
[18, 19, 11, 10, 9, 8, 7, 17, 16, 20, 13, 12, 3, 2, 1, 5, 6, 15, 14, 4]
[11, 12, 3, 2, 10, 9, 18, 19, 20, 13, 14, 4, 5, 1, 8, 7, 17, 16, 15, 6]
[15, 14, 13, 20, 16, 17, 7, 6, 5, 4, 3, 12, 11, 19, 18, 9, 8, 1, 2, 10]
[19, 11, 10, 9, 18, 17, 16, 20, 13, 12, 3, 2, 1, 8, 7, 6, 15, 14, 4, 5]
[12, 11, 19, 20, 13, 14, 4, 3, 2, 10, 9, 18, 17, 16, 15, 6, 5, 1, 8, 7]
[20, 16, 15, 14, 13, 12, 11, 19, 18, 17, 7, 6, 5, 4, 3, 2, 10, 9, 8, 1]
[13, 14, 4, 3, 12, 11, 19, 20, 16, 15, 6, 5, 1, 2, 10, 9, 18, 17, 7, 8]
[5, 6, 7, 8, 1, 2, 3, 4, 14, 15, 16, 17, 18, 9, 10, 11, 12, 13, 20, 19]
[14, 4, 3, 12, 13, 20, 16, 15, 6, 5, 1, 2, 10, 11, 19, 18, 17, 7, 8, 9]
Para cualquier otra red, simplemente reemplace cada aparición de i
con el i
número th en la entrada (donde i
está basado en 1).
Desafíos relacionados
Tabla de clasificación
La primera publicación de la serie genera una tabla de clasificación.
Para asegurarse de que sus respuestas aparezcan, comience cada respuesta con un título, utilizando la siguiente plantilla de Markdown:
## Language Name, N bytes
¿Dónde N
está el tamaño de su envío? Si mejora su puntaje, puede mantener los puntajes antiguos en el título, tachándolos. Por ejemplo:
## Ruby, <s>104</s> <s>101</s> 96 bytes
(El idioma no se muestra actualmente, pero el fragmento sí lo requiere y analiza, y puedo agregar una tabla de clasificación por idioma en el futuro).
fuente
Respuestas:
Ruby, 160 bytes (rev B)
17 bytes guardados gracias a las sugerencias de Martin Büttner.
Ruby, 177 bytes (rev A)
Explicación
Es posible generar todas las orientaciones posibles mediante rotaciones alrededor de una cara (3 veces), un vértice (5 veces) y dos bordes (2 veces). Pero no solo cualquier cara y bordes. Los ejes deben tener la relación correcta y las rotaciones deben hacerse en el orden correcto, o pueden suceder cosas extrañas.
Así es como lo hice (aunque entiendo que Martin lo hizo de manera diferente).
Todas las orientaciones de un tetraedro se pueden generar mediante combinaciones de las siguientes tres operaciones de simetría:
a) Rotación alrededor de dos ejes de 2 pliegues a la derecha a través de los puntos medios de los bordes (estos están en ángulo recto entre sí. Si los multiplicamos juntos descubrimos un tercer eje de 2 pliegues a través del par de bordes restante).
b) Rotación sobre un eje de 3 pliegues diagonal a los ejes de 2 pliegues, pasando a través de un vértice y una cara.
La simetría del icosaedro es un superconjunto de la del tetraedro. En la imagen a continuación de https://en.wikipedia.org/wiki/Icosahedron , las caras amarillas y rojas definen dos tetraedros diferentes (o, alternativamente, un solo octaedro), mientras que los bordes comunes a dos caras azules están en tres pares en ángulos rectos (y recostarse sobre las caras de un cubo).
Todas las orientaciones del icosaedro pueden generarse mediante las operaciones de simetría mencionadas anteriormente más una operación adicional de 5 veces.
Las rotaciones alrededor de tres de los cuatro ejes mencionados anteriormente están representadas por las cadenas mágicas entre las
''
marcas. La rotación sobre el segundo eje doble es diferente, y se puede hacer simplemente invirtiendo la matriza[]
.Sin golf en el programa de prueba:
Solución alternativa 131 bytes (inválida debido al enfoque de paseo aleatorio binario, solo da una distribución aproximadamente correcta).
Esta es una codificación (al igual que los programas utilizados para codificar el cubo de Rubik).
Las rotaciones específicas que uso son dos de las más obvias:
-Una rotación de 120 grados (alrededor de las caras 1 y 20 según el primer diagrama)
-Una rotación de 72 grados (sobre los vértices comunes a 1,2,3,4,5 y 16,17,18,19,20 según el primer diagrama).
Lanzamos una moneda 99 veces, y cada vez que realizamos una de estas dos rotaciones dependiendo de si es cara o cruz.
Tenga en cuenta que alternar estos determinísticamente conduce a secuencias bastante cortas. Por ejemplo, con los sentidos de rotación correctos, se puede producir una rotación de 180 grados con un período de solo 2.
fuente
b.map
no funciona directamente, necesitob.chars.map
hacerlo funcionar (por cierto que no funciona en mi máquina ya que tengo Ruby 1.9.3 pero funciona en Ideone). Es un ahorro justo. No creo que cambiar las cadenas mágicas de los caracteres no imprimibles para guardar-64
funcionará:%w{}
interpreta\n
(así como el espacio) como un separador entre las cadenas en la matriz generada. No tengo idea de lo que hará con otros caracteres ASCII no imprimibles.Código de máquina IA-32, 118 bytes
Hexdump:
Es un poco largo, por lo que algunas explicaciones van primero. Solía casi el mismo algoritmo que el vigente respuesta según el nivel del río St . Para que mi respuesta sea diferente, tomé otras permutaciones básicas, que no necesariamente tienen un significado geométrico intuitivo, pero de alguna manera son más convenientes.
El código básicamente tiene que generar una permutación, que es una aplicación secuencial de lo siguiente:
p3
, aplicada 0 ... 2 vecesq2
, aplicada 0 o 1 vecesp5
, se aplica 0 ... 4 vecesp2
, se aplica 0 o 1 vecesHay muchas opciones posibles para estas permutaciones. Uno de ellos es el siguiente:
Esta opción es mejor que otras porque las permutaciones aquí tienen largas series de índices secuenciales, que se pueden comprimir mediante codificación de longitud de ejecución: solo 29 bytes para las 4 permutaciones.
Para simplificar la generación de números aleatorios, elegí los poderes (cuántas veces se aplica cada permutación) en el rango 1 ... 30 para todos ellos. Esto lleva a mucho trabajo adicional en el código, porque, por ejemplo, se
p3
convierte en una permutación de identidad cada vez que se multiplica por sí mismo 3 veces. Sin embargo, el código es más pequeño de esa manera, y siempre que el rango sea divisible por 30, la salida tendrá una distribución uniforme.Además, las potencias deben ser positivas para que la operación de decodificación de longitud de ejecución se realice al menos una vez.
El código tiene 4 bucles anidados; El esquema es así:
Espero que este pseudocódigo sea más claro que el código de ensamblaje en línea a continuación.
Algunos detalles divertidos de implementación:
call
y luegopop
para acceder a los datos (permutaciones codificadas) almacenados en el códigojecxz
instrucción convenientemente me permite usar un byte cero como terminación para el proceso de decodificación de longitud de ejecuciónPor suerte, el número 30 (2 * 3 * 5) es casi una potencia de 2. Esto me permite usar un código corto para generar un número en el rango 1 ... 30:
Utilizo una instrucción de "división de propósito general" (
aam
) para separar un byte en campos de bits (longitud de 3 bits e índice de 5 bits); por suerte, en esa posición en el códigocl = 0
, así que me beneficio de ambos "lados" dexchg
fuente