Encuentra la pila de identidad


18

Esta pregunta es sobre pilas de arena abelianas . Lea este desafío anterior y mire este video numérico para obtener más información.


Una pila de arena abeliana de tamaño n por n es una cuadrícula que contiene el número 0, 1, 2 y 3 (que representa el número de granos de arena). Agregar dos sandpiles funciona agregando primero elemento por elemento, y luego derribando cualquier elemento que vaya por encima de 3. El orden en que se derriba no importa, el resultado final es el mismo. Cuando una célula cae, su número disminuye en 4, y cada uno de sus vecinos directos aumenta en 1. Esto puede causar una reacción en cadena. Si una celda está en el borde de la cuadrícula, cualquier grano que se caiga de la cuadrícula mientras se cae desaparecerá.

Por ejemplo, estoy agregando dos pilas de arena de 3 por 3 (dando una reacción en cadena bastante extrema):

3 3 3   1 2 1   4 5 4    4 6 4    6 2 6    6 3 6    2 5 2    4 1 4    4 2 4    0 4 0    2 0 2    2 1 2
3 3 3 + 2 1 2 = 5 4 5 -> 6 0 6 -> 2 4 2 -> 3 0 3 -> 5 0 5 -> 1 4 1 -> 2 0 2 -> 4 0 4 -> 0 4 0 -> 1 0 1
3 3 3   1 2 1   4 5 4    4 6 4    6 2 6    6 3 6    2 5 2    4 1 4    4 2 4    0 4 0    2 0 2    2 1 2

En este desafío estamos interesados en un subconjunto de todos los posibles n por n sandpiles. Este subconjunto contiene cualquier pila de arena que puede obtener agregando una pila de arena arbitraria a la pila de arena all-3s n by n . Por ejemplo, justo arriba vimos que 212 | 101 | 212está en el subconjunto, porque lo obtuvimos agregando algo a la pila de arena all-3.

Ahora este subconjunto tiene un elemento interesante: el elemento de identidad . Si toma este elemento y lo agrega a cualquier otro elemento del subconjunto , la suma no cambia. En otras palabras, esta pila de arena actúa como un cero de este subconjunto. Sucede que 212 | 101 | 212es el elemento cero para el subconjunto de 3 por 3. Por ejemplo:

2 2 2   2 1 2   4 3 4    0 5 0    2 1 2    2 2 2
2 2 2 + 1 0 1 = 3 2 3 -> 5 2 5 -> 1 6 1 -> 2 2 2
2 2 2   2 1 2   4 3 4    0 5 0    2 1 2    2 2 2

Ahora este es su desafío: dado n , encuentre el elemento de identidad del subconjunto de la cuadrícula n por n . Exprímalo asignando un color único con suficiente contraste de su elección a cada una de ellas 0, 1, 2, 3y generando una imagen n por n. Su código debe poder producir la caja de 50 por 50 en menos de un minuto en una PC moderna razonable.


Por ejemplo, el elemento de identidad 500 por 500:

Elemento de identidad 500 por 500

Aquí está azul = 3, verde = 2, rojo = 1, blanco = 0. Pero no tiene que usar este esquema de color en su respuesta.


2
Una advertencia para los competidores: describí cuál es la solución, no cómo calcularla. Su código debe ser capaz de producir el caso de 50 por 50 en menos de un minuto, por lo que el forzamiento bruto no es una posibilidad. Hay algoritmos para resolver esto, y no te los voy a dar. Eso es intencional Siento que demasiados desafíos te presentan comida pre-masticada. Daré una recompensa de +100 a la primera respuesta que no trivialice el problema con un builtin incorporado (mirándote, Mathematica), a mi discreción.
orlp

2
Creo que la imagen de la identidad 500x500 se beneficiaría de decir a qué número corresponde cada color.
xnor

¿Qué constituye "contraste suficiente"?
Conor O'Brien

@ ConorO'Brien Cualquier conjunto de colores que sean suficientemente distinguibles. Podría hacerlo 100% objetivo con alguna medida de color, pero siento que es exagerado. No me importa si usa rojo, amarillo, verde, escala de grises o lo que sea, simplemente no use 4 tonos de gris que estén dentro del 1% entre sí (como # 000000, # 000001, # 000002, # 000003).
orlp

ejem Creo que esta pregunta ahora es elegible para recompensas. ¿Puedo obtener el bono de +100? :)
JungHwan Min

Respuestas:


2

Octava, 120 113 bytes

function a=W(a);while nnz(b=a>3);a+=conv2(b,[t=[0 1 0];!t;t],'same')-b*4;end;end;@(n)imagesc(W((x=ones(n)*6)-W(x)))

Gracias a JungHwan Min por proporcionar un enlace al documento de referencia en su respuesta de Mathematica.
Gracias a Stewie Griffin me ahorró 7 bytes[any(any(x)) -> nnz(x)]

Aquí se utilizan dos funciones:

1 f.: para la estabilización de una matriz
2. Una función anónima que toma ncomo entrada y muestra la matriz de identidad.

Pruébalo en rextester! para la generación de una matriz 50 * 50

Tiempo transcurrido para el cálculo de la matriz: 0.0844409 seconds.

Explicación:

Considere una función f que estabiliza una matriz, la tarea de encontrar la identidad es simplemente

f(ones(n)*6 - f(ones(n)*6).

eso ones(n)*6significa una matriz * n de 6.

entonces para n=3:

M = [6 6 6
     6 6 6
     6 6 6];

El resultado será f(M-f(M))

Para la función de estabilización, la convolución 2D se usa para acelerar la tarea; En cada iteración hacemos una matriz binaria bcon el mismo tamaño de la matriz de entrada y la establecemos en 1 si el elemento correspondiente de la matriz de entrada es> 3. Luego aplicamos una convolución 2D de la matriz binaria con la siguiente máscara

0 1 0
1 0 1
0 1 0

representando a cuatro vecinos directos.
El resultado de la convolución se agrega a la matriz y se sustrae 4 veces la matriz binaria.

El ciclo continuó hasta que todos los elementos de la matriz son <= 3

Versión sin golf :

function a=stabilize(a)
    mask = [t=[0 1 0];!t;t];
    while any(any(b=a>3))
        a+=conv2(b,mask,'same')-b*4;
    end
end
n= 50;
M = ones(n)*6;
result = stabilize(M-stabilize(M));
imagesc(result);

8

Mathematica, 177 157 135 133 bytes

Colorize[f=BlockMap[⌊{l={0,1,0},1-l,l}#/4⌋~Total~2+#[[2,2]]~Mod~4&,#~ArrayPad~1,{3,3},1]&~FixedPoint~#&;k=Table[6,#,#];f[k-f@k]]&

Toma un número n. El resultado es la pila de arena de identidad. 0 es negro, 1 es gris claro, 2 es magenta y 3 es gris azulado.

Lamentablemente, Mathematica no tiene un generador incorporado para esto ...

Utiliza el algoritmo establecido en el artículo de Scott Corry y David Perkinson .

Toma 91.7 segundos en mi computadora portátil de 5 años para calcular la pila de arena de identidad 50x50. Estoy seguro de que una computadora de escritorio moderna razonable es más del 50% más rápida. (También tengo un código mucho más rápido al final).

Explicación

f= ...

Definir función f(la entrada es una matriz sandpile): una función que ...

BlockMap[ ... ]~FixedPoint~#&

... repite la BlockMapoperación hasta que la salida no cambie. BlockMapoperación: ...


#~ArrayPad~1

... rellena la matriz de entrada con una capa de 0 ...

{3,3},1

... particionarlo en matrices 3x3, con desplazamiento 1 ...

⌊{l={0,1,0},1-l,l}#/4⌋~Total~2+#[[2,2]]~Mod~4&

... y para cada partición, agregue el número de granos de arena derribados en la celda central y el valor de la celda central mod 4.

es decir, la salida de fes la versión estabilizada de la entrada.


k=Table[6,#,#]

Definir kcomo una matriz n por n de 6s.

f[k-f@k]]

Calcule f (k - f (k)).

Colorize[ ... ]

Aplica colores al resultado.

Versión más rápida (142 bytes)

Colorize[r=RotateLeft;a=ArrayPad;f=a[b=#~a~1;b+r[g=⌊b/4⌋,s={0,1}]+g~r~-s+r[g,1-s]+r[g,s-1]-4g,-1]&~FixedPoint~#&;k=Table[6,#,#];f[k-f@k]]&

Mismo código, pero utiliza la rotación de la lista incorporada en lugar de BlockMap. Calcula n = 50 en 4.0 segundos en mi computadora portátil.


Teniendo en cuenta que siguió el espíritu del límite de tiempo (implementando un algoritmo real en lugar de la fuerza bruta), y es muy posible que una computadora de escritorio potente sea un 50% más rápida, lo permitiré. Dado que implementa un algoritmo real sin un trivializador incorporado, esto califica para la bonificación de +100. Sin embargo, tendrás que esperar, ya que todavía no puedo comenzar una recompensa.
orlp

Dicho esto, implementar esto de manera bastante trivial en Python (un lenguaje notoriamente lento), solo toma ~ 2s para n = 50. ¿Quizás pueda acelerarlo un poco?
orlp

@orlp Listo, pero es más largo que el código original. ¿Debo hacer que la versión más rápida sea mi respuesta principal o puedo ponerla al final?
JungHwan Min

Al igual que esto está bien, creo.
orlp

0

Python 3 + Numpy + PIL, 385 370 364 bytes

import numpy as q,PIL.Image as w
n=int(input())
z=n,n
def r(p):
 while len(p[p>3]):
  for x,y in q.ndindex(z):
   if p[x,y]>3:
    p[x,y]-=4;p[x-1,y]+=x>0;p[x,y-1]+=y>0
    if~-n>x:p[x+1,y]+=1
    if~-n>y:p[x,y+1]+=1
s=q.full(z,6)
t=s.copy()
r(t)
i=s-t
r(i)
w.fromarray(q.uint8(q.array(q.vectorize(lambda x:[x//1*65]*3,otypes=[object])(i).tolist()))).save('i.png')

Toma entrada en STDIN. Emite la imagen como escala de grises a i.png. El negro corresponde a 0, el gris oscuro a 1, el gris claro a 2 y el blanco a 0.

Utiliza la fórmula I = R(S - R(S)), donde Iestá el elemento de identidad, Ses la matriz llena de seises y Res la función de reducción.

Probablemente podría guardar algunos bytes al cambiar a Python 2 y hacerlo from numpy import*, pero (1) no tengo Numpy instalado en Python 2 y (2) el programa no estaba terminando from numpy import*.

Sin golf:

import numpy as np
from PIL import Image

# Compute the identity element

n = int(input('Size of the sandpile: '))

def reduce_pile(sandpile):
  while any(element >= 4 for element in np.nditer(sandpile)):
    for x, y in np.ndindex((n, n)):
      if sandpile[x, y] >= 4:
        sandpile[x, y] -= 4
        if x > 0: sandpile[x - 1, y] += 1
        if y > 0: sandpile[x, y - 1] += 1
        if x < n - 1: sandpile[x + 1, y] += 1
        if y < n - 1: sandpile[x, y + 1] += 1

s = np.full((n, n), 6, dtype=np.int32)
s_prime = np.copy(s)

reduce_pile(s_prime)

identity = s - s_prime
reduce_pile(identity)

# Output it to identity.png as an image

colours = [[255, 255, 255], [255, 0, 0], [0, 255, 0], [0, 0, 255]]
img_array = np.vectorize(lambda x: colours[x], otypes=[object])(identity)
img_array = np.array(img_array.tolist(), dtype=np.uint8)

img = Image.fromarray(img_array)
img.save('identity.png')

Es posible que pueda guardar bytes utilizando scipyo matplotlibpara mostrar los datos en lugar de generar una imagen explícitamente con PIL.
orlp
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.