Saltar al contenido

Genera una cuadrícula de sumas

Luego de de una prolongada recopilación de datos pudimos resolver esta problema que suelen tener muchos usuarios. Te dejamos la respuesta y esperamos que resulte de mucha ayuda.

Solución:

APL, 53 49 43 42 40 39 36

Me las arreglé para replicar J’s ;. en APL, y usó el enfoque de Gareth, ahorrando 13 caracteres.

×5⌷⍵:5⌷⍵⋄+/⍵¨3,⌿3,/×∘?∘9¨∘.∨⍨9⍴0 1

Ejecución de muestra:

      ×5⌷⍵:5⌷⍵⋄+/⍵¨3,⌿3,/×∘?∘9¨∘.∨⍨9⍴0 1
9  9 6  1 7  5 6
7 55 5 39 9 54 9
9  8 2  1 8  1 9
2 43 8 41 6 42 5
7  3 4  4 8  3 2
2 29 1 26 2 35 8
6  4 2  3 2  3 7

Explicación:

  • ∘.∨⍨9⍴0 1 genera una máscara de bits.
  • ×∘?∘9¨ multiplica cada bit por un valor aleatorio de 1 a 9 inclusive, generando una cuadrícula enmascarada de números aleatorios.
  • 3,⌿3,/ usa lo que solo se puede describir como piratería para devolver los cuadros superpuestos de 3 por 3 en la matriz enmascarada. Estos también se aplanan en el proceso.
  • ×5⌷⍵:5⌷⍵⋄+/⍵¨ itera sobre la matriz, asignando cada elemento a . Para cada iteración, toma la quinta (en el medio, recordando que la indexación APL se basa en 1) y devuelve su signo. En este caso, esto equivale a probar si el número es mayor que 0. Si devuelve 1 (verdadero), devuelve ese elemento. De lo contrario, devuelva la suma de los elementos en el cuadro aplanado de 3 por 3. Usa el :⋄ operador ternario, que es el equivalente de ?: en muchos idiomas.

J 636159555251494739 37 caracteres

3 3(4&{+4{*|+/)@,;._3(**1+?)+./~9$0 9

Con agradecimiento a Volatility por su ahorro de 10 caracteres.

Explicación (cada paso tendrá diferentes números aleatorios …):

Genere la máscara para generar los números aleatorios (utiliza $:

   9 9$9$0 9
0 9 0 9 0 9 0 9 0
0 9 0 9 0 9 0 9 0
0 9 0 9 0 9 0 9 0
0 9 0 9 0 9 0 9 0
0 9 0 9 0 9 0 9 0
0 9 0 9 0 9 0 9 0
0 9 0 9 0 9 0 9 0
0 9 0 9 0 9 0 9 0
0 9 0 9 0 9 0 9 0

Ahora tenemos un gancho. Este es en realidad un feliz accidente de cuando estaba reduciendo una versión anterior. Estaba destinado a ser transpuesto |: y O +. con el original. Tenía sentido ya que estaba usando unos y ceros en ese momento, pero ahora tengo nueves y ceros. Da la casualidad de que funciona de la misma manera con el significado de GCD de +.. Por suerte para mi. 🙂

   (+.|:)9 9$9$0 9
0 9 0 9 0 9 0 9 0
9 9 9 9 9 9 9 9 9
0 9 0 9 0 9 0 9 0
9 9 9 9 9 9 9 9 9
0 9 0 9 0 9 0 9 0
9 9 9 9 9 9 9 9 9
0 9 0 9 0 9 0 9 0
9 9 9 9 9 9 9 9 9
0 9 0 9 0 9 0 9 0

Entonces, ahora que tenemos una cuadrícula de 9 y 0, queremos generar algunos números aleatorios. ? genera un número aleatorio desde 0 hasta (pero sin incluir) un número dado. Dada una lista, generará un número aleatorio de esta manera para cada miembro de la lista. Entonces, en este caso, generará un número de 0 a 8 por cada 9 en la tabla y un número de punto flotante de 0 a 1 para cada 0.

   ?(+.|:)9 9$9$0 9
 0.832573 7 0.926379 7 0.775468 6 0.535925 3  0.828123
        7 0        5 5        4 3        4 5         4
0.0944584 2 0.840913 2 0.990768 1 0.853054 3  0.881741
        3 8        7 0        8 3        3 4         8
 0.641563 4 0.699892 7 0.498026 1 0.438401 6  0.417791
        6 8        7 5        2 3        6 6         3
 0.753671 6 0.487016 4 0.886369 7 0.489956 5  0.902991
        3 4        7 8        1 4        8 0         8
0.0833539 4 0.311055 4 0.200411 6 0.247177 5 0.0464731

Pero queremos números del 1 al 9, no del 0 al 8. Así que sumamos 1.

   (1+?)(+.|:)9 9$9$0 9
 1.4139 4  1.7547 7 1.67065 4 1.52987 1 1.96275
      2 8       2 4       3 9       6 9       9
1.15202 7 1.11341 5  1.0836 1 1.24713 2 1.13858
      9 3       3 2       4 7       3 8       6
1.06383 9 1.67909 4 1.09801 8  1.4805 6  1.0171
      9 5       5 5       9 5       9 4       3
1.22819 1 1.85259 4 1.95632 6 1.33034 3 1.39417
      4 2       5 1       3 7       2 5       6
1.06572 5  1.9942 5 1.78341 5 1.16516 6 1.37087

Esto es muy bueno, pero hemos perdido los ceros que quería, así que lo multiplicaremos por la máscara original después de convertir todos los nueves en unos. Hago esto comprobando si el valor es mayor que 1. Eso nos da: (1&<*1+?).
Hay un par de cosas que suceden aquí:

  • Hemos creado una bifurcación que nos permite empaquetar mucho trabajo en muy pocos personajes.
  • Nos hemos unido (&) el 1 al < verbo.

Así que todos combinaron el (1&<*1+?) está generando números aleatorios y poniendo a cero todos los números que fueron generados por ceros en la cuadrícula original.

   (1&<*1+?)(+.|:)9 9$9$0 9
0 3 0 2 0 7 0 1 0
9 5 2 7 7 1 4 5 7
0 6 0 8 0 3 0 1 0
4 8 7 5 9 7 7 9 4
0 9 0 6 0 9 0 9 0
6 1 2 1 4 6 8 9 4
0 3 0 8 0 6 0 6 0
2 5 2 2 2 2 3 9 3
0 9 0 3 0 5 0 3 0

El siguiente bit es el (en mi opinión, de todos modos 🙂 bit inteligente.
El corte ;. el verbo tiene una forma x u;._3 y que corta la entrada en cuadros descritos por x, y luego aplica el verbo u a ellos. En este caso tenemos 3 3(4&{++/*0=4&{)@,;._3.

  • los 3 3 describe las cajas que queremos - 3x3.
  • los (4&{++/*0=4&{)@, es un tren de verbos que describe lo que queremos hacer con cada caja.

Para demostrar el ;. verbo que usaré < para mostrar cada cuadro:

   3 3(<);._3(1&<*1+?)(+.|:)9 9$9$0 9
┌─────┬─────┬─────┬─────┬─────┬─────┬─────┐
│0 8 0│8 0 7│0 7 0│7 0 4│0 4 0│4 0 3│0 3 0│
│9 1 3│1 3 2│3 2 3│2 3 8│3 8 5│8 5 5│5 5 9│
│0 6 0│6 0 1│0 1 0│1 0 2│0 2 0│2 0 4│0 4 0│
├─────┼─────┼─────┼─────┼─────┼─────┼─────┤
│9 1 3│1 3 2│3 2 3│2 3 8│3 8 5│8 5 5│5 5 9│
│0 6 0│6 0 1│0 1 0│1 0 2│0 2 0│2 0 4│0 4 0│
│7 1 6│1 6 7│6 7 1│7 1 2│1 2 1│2 1 6│1 6 1│
├─────┼─────┼─────┼─────┼─────┼─────┼─────┤
│0 6 0│6 0 1│0 1 0│1 0 2│0 2 0│2 0 4│0 4 0│
│7 1 6│1 6 7│6 7 1│7 1 2│1 2 1│2 1 6│1 6 1│
│0 7 0│7 0 5│0 5 0│5 0 9│0 9 0│9 0 7│0 7 0│
├─────┼─────┼─────┼─────┼─────┼─────┼─────┤
│7 1 6│1 6 7│6 7 1│7 1 2│1 2 1│2 1 6│1 6 1│
│0 7 0│7 0 5│0 5 0│5 0 9│0 9 0│9 0 7│0 7 0│
│7 9 9│9 9 7│9 7 1│7 1 9│1 9 4│9 4 9│4 9 5│
├─────┼─────┼─────┼─────┼─────┼─────┼─────┤
│0 7 0│7 0 5│0 5 0│5 0 9│0 9 0│9 0 7│0 7 0│
│7 9 9│9 9 7│9 7 1│7 1 9│1 9 4│9 4 9│4 9 5│
│0 3 0│3 0 2│0 2 0│2 0 7│0 7 0│7 0 9│0 9 0│
├─────┼─────┼─────┼─────┼─────┼─────┼─────┤
│7 9 9│9 9 7│9 7 1│7 1 9│1 9 4│9 4 9│4 9 5│
│0 3 0│3 0 2│0 2 0│2 0 7│0 7 0│7 0 9│0 9 0│
│3 1 6│1 6 1│6 1 7│1 7 6│7 6 8│6 8 9│8 9 9│
├─────┼─────┼─────┼─────┼─────┼─────┼─────┤
│0 3 0│3 0 2│0 2 0│2 0 7│0 7 0│7 0 9│0 9 0│
│3 1 6│1 6 1│6 1 7│1 7 6│7 6 8│6 8 9│8 9 9│
│0 9 0│9 0 3│0 3 0│3 0 4│0 4 0│4 0 3│0 3 0│
└─────┴─────┴─────┴─────┴─────┴─────┴─────┘

Algunas cosas a tener en cuenta:

  • Los cuadros se superponen: la segunda y tercera columnas en el cuadro superior izquierdo son la primera y la segunda en el cuadro a la derecha.
  • Hay cajas de 7x7. Es por eso que inicialmente teníamos una cuadrícula de 9x9.
  • Cada lugar que requerimos una suma tiene un 0 en el centro de la caja.

Ahora solo tenemos que pasar el valor en el centro de la espalda (si no es cero) o sumar los números en el cuadro 3x3 (si el centro es cero).
Para hacer esto, necesitamos un fácil acceso al número del centro. , ayuda aquí. Convierte la cuadrícula de 3x3 en una lista de 9 elementos con el número central en el número 4.
4&{ utilizará { para extraer el valor central y luego compararlo con 0: 0=4&{. Esto devuelve un 0 o 1 para verdadero o falso, que luego multiplicamos por la suma +/. Si era cero en el centro, ahora tenemos la suma requerida. Si no es así tenemos cero, así que para terminar solo sumamos el valor del centro 4&{+.
Esto le da al verbo entrenar (4&{++/*0=4&{)@,

   3 3(4&{++/*0=4&{)@,;._3(1&<*1+?)(+.|:)9 9$9$0 9
2  6 9  3 7  9 7
3 47 6 51 5 49 5
3  9 9  6 6  2 8
7 48 6 47 1 37 5
5  4 5  7 7  2 6
5 35 3 49 8 51 9
1  6 6  6 7  4 8

Ruby (135 caracteres)

a=(0..48).maprand(9)+1
([0,0,j=8]*3).eachl
a.each_slice(7)r

Salida de muestra

2  1  6  9  4  5  1  
9  34 4  37 2  31 3  
7  2  3  1  8  1  7  
5  42 4  40 2  47 9  
3  9  9  4  9  4  7  
3  44 4  41 2  47 4  
6  9  1  5  7  6  8  

Desglose

No es muy obvio cómo funciona esto, así que aquí hay un desglose rápido. NOTA: Probablemente pueda omitir algunos de estos pasos y pasar a versiones más cortas más rápidamente, pero creo que es lo suficientemente educativo como para ver las diferentes formas en que eliminé los caracteres, especialmente al detectar patrones en literales para convertir números de 2 dígitos en versiones de 1 dígito.

Versión ingenua

A diferencia de las otras soluciones de Ruby que se basan en una matriz bidimensional, puede (eventualmente) obtener una versión más corta comenzando con una matriz unidimensional y trabajando con valores de compensación, ya que los patrones se repiten.

ary=(0..48).map  rand(9) + 1 

offsets = [-8,-7,-6,-1,1,6,7,8]

3.times do |i|
  [8,10,12].each do |j|
    ary[j + 14*i] = ary.values_at(*offsets.map e).inject(:+)
  end
end

ary.each.with_index do |e,i|
  $> << ("%-3s" % e)
  $> << ?n if i % 7==6
end

El principio clave aquí es que estamos trabajando en las posiciones de índice 8, 10, 12, simplemente compensados ​​por múltiplos de 14. Las posiciones 8, 10 y 12 son los centros de las cuadrículas de 3x3 que estamos resumiendo. En la salida de muestra, 34 es la posición 8, 42 es la posición 8 + 14 * 1, etc. Reemplazamos la posición 8 con 34 por posiciones desplazadas desde la posición 8 por [-8,-7,-6,-1,1,6,7,8] - en otras palabras 34 = sum(ary[8-8], ary[8-7], ..., ary[8+8]). Este mismo principio se aplica a todos los valores de [8 + 14*i, 10 + 14*i, 12 + 14*i], ya que el patrón se repite.

Optimizándolo

Primero, algunas optimizaciones rápidas:

  • En lugar de 3.times ... y calculando j + 14*i cada vez, "alinea" las posiciones [8,10,12,22,24,26,36,38,40].
  • los offsets array se usa una vez, así que reemplace la variable con el literal.
  • Reemplazar do ... end con ... y cambie la impresión a $> << foo. (Hay un truco aquí que involucra puts nil y () == nil.)
  • Nombres de variables más cortos.

El código después de esto es 177 caracteres:

a=(0..48).maprand(9)+1
[8,10,12,22,24,26,36,38,40].each
a.map.with_index

Para la siguiente reducción, tenga en cuenta que el inject no necesita que la matriz de compensaciones esté en orden. Podemos tener [-8,-7,-6,-1,1,6,7,8] o algún otro orden, ya que la suma es conmutativa.

Así que primero empareje los aspectos positivos y negativos para obtener [1,-1,6,-6,7,-7,8,-8].

Ahora puedes acortar

[1,-1,6,-6,7,-7,8,-8].map  j+e .inject(:+)

para

[1,6,7,8].flat_map e

Esto resulta en

a=(0..48).maprand(9)+1
[8,10,12,22,24,26,36,38,40].eacha[j]=a.values_at(*[1,6,7,8].flat_mape).inject(:+)
a.map.with_indexe,i

cual es 176 caracteres.

Cambiar de 8 y pasar a las diferencias

Los valores literales de dos caracteres parecen acortarse, así que tome [8,10,12,22,24,26,36,38,40] y cambiar todo por 8, actualizando j al comienzo del bucle. (Tenga en cuenta que +=8 evita la necesidad de actualizar los valores de compensación de 1,6,7,8.)

a=(0..48).maprand(9)+1
[0,2,4,14,16,18,28,30,32].eachj+=8;a[j]=a.values_at(*[1,6,7,8].flat_map[j+e,j-e]).inject(:+)
a.map.with_index

Esto es 179, que es más grande, pero el j+=8 realmente se puede eliminar.

Primer cambio

[0,2,4,14,16,18,28,30,32]

a una serie de diferencias:

[2,2,10,2,2,10,2,2]

y suma de forma acumulativa estos valores a una inicial j=8. Esto eventualmente cubrirá los mismos valores. (Probablemente podríamos pasar directamente a esto en lugar de cambiar por 8 primero).

Tenga en cuenta que también agregaremos un valor ficticio de 9999 al final de la matriz de diferencias y agregar a j en el fin, no la comienzo del bucle. La justificación es que 2,2,10,2,2,10,2,2 aspecto muy cerca de ser los mismos 3 números repetidos 3 veces, y calculando j+difference al final del ciclo, el valor final de 9999 no afectará realmente a la salida, ya que no hay un a[j] llamar donde j hay algún valor sobre 10000.

a=(0..48).maprand(9)+1
j=8
[2,2,10,2,2,10,2,2,9999].each
a.map.with_index$><<"%-3s"%e<<(?nif i%7==6)

Con esta matriz de diferencias, el j+=8 es ahora solo j=8, por supuesto, ya que de lo contrario agregaríamos repetidamente 8 demasiados. También hemos cambiado la variable de bloque de j para l.

Entonces desde el 9999 elemento no tiene ningún efecto en la salida, podemos cambiarlo a 10 y acortar la matriz.

a=(0..48).maprand(9)+1
j=8
([2,2,10]*3).each
a.map.with_index

Este es 170 caracteres.

Pero ahora el j=8 parece un poco torpe, y puedes guardar 2 personajes cambiando [2,2,10] hacia abajo en 2 para obtener convenientemente un 8 que puede utilizar para la asignación. Esto también necesita j+=l convertirse j+=l+2.

a=(0..48).maprand(9)+1
([0,0,j=8]*3).eachl
a.map.with_indexe,i

Este es 169 caracteres. Una forma redonda de exprimir 7 caracteres, pero es genial.

Ajustes finales

los values_at La llamada es en realidad una especie de redundancia, y podemos incorporar una Array#[] llama. Entonces

a.values_at(*[1,6,7,8].flat_mape).inject(:+)

se convierte en

[1,6,7,8].flat_map.inject(:+)

También puedes ver eso flat_map + j+e/j-e + inject puede reducirse a una suma más directa con una inicial 0 en la matriz.

Esto te deja con 152 caracteres:

a=(0..48).maprand(9)+1
([0,0,j=8]*3).eacha[j]=[0,1,6,7,8].injects+a[j+e]+a[j-e];j+=l+2
a.map.with_index

Finalmente:

  • map.with_index puede llegar a ser each_slice.
  • Cambie el método de impresión.

135:

a=(0..48).maprand(9)+1
([0,0,j=8]*3).eacha[j]=[0,1,6,7,8].injects+a[j+e]+a[j-e];j+=l+2
a.each_slice(7)

valoraciones y comentarios

Al final de la post puedes encontrar las aclaraciones de otros administradores, tú además tienes la habilidad dejar el tuyo si te gusta.

¡Haz clic para puntuar esta entrada!
(Votos: 5 Promedio: 4.2)



Utiliza Nuestro Buscador

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *