Найти личность песочницу

18

Этот вопрос об абелевых песочных кучах . Прочитайте этот предыдущий вызов и посмотрите это видео, чтобы узнать больше.


Абелева кучи песка размера п по п - это сетка, содержащая числа 0, 1, 2 и 3 (представляющие количество песчинок). Добавление двух песочниц работает, сначала добавляя элемент за элементом, а затем опуская любой элемент, который идет выше 3. Порядок, в котором вы свергнете, не имеет значения, конечный результат тот же. Когда клетка падает, ее число уменьшается на 4, а каждый из ее прямых соседей увеличивается на 1. Это может вызвать цепную реакцию. Если ячейка находится на краю сетки, любые зерна, которые падают с сетки при свертывании, исчезают.

Например, я добавляю две 3 к 3 песочницы (что дает довольно экстремальную цепную реакцию):

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

В этой задаче нас интересует подмножество всех возможных n по n песочниц. В этом подмножестве содержится любая песочница, которую вы можете получить, добавив произвольную песочницу к песочнице all-3s n по n . Например, чуть выше мы увидели, что 212 | 101 | 212это подмножество, потому что мы получили его, добавив что-то в песочницу all-3.

Теперь у этого подмножества есть интересный элемент: элемент идентичности . Если вы возьмете этот элемент и добавите его к любому другому элементу в подмножестве , сумма не изменится. Другими словами, эта песочница действует как ноль этого подмножества. Так уж получилось, что 212 | 101 | 212это нулевой элемент для подмножества 3 на 3. Например:

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

Теперь это ваша задача: по заданному n найти идентификационный элемент подмножества n по n сетке . Выведите его, назначив уникальный цвет с достаточной контрастностью по вашему выбору для каждого из них 0, 1, 2, 3и выводя изображение n на n. Ваш код должен быть в состоянии создать случай 50 на 50 менее чем за минуту на разумном современном ПК.


Например, элемент 500 на 500:

Элемент 500 на 500

Вот синий = 3, зеленый = 2, красный = 1, белый = 0. Но вам не нужно использовать эту цветовую схему в своем ответе.

orlp
источник
2
Предупреждение для конкурентов: я описал, что такое решение, а не как его вычислить. Ваш код должен быть в состоянии создать случай 50 на 50 менее чем за минуту, поэтому грубое форсирование не возможно. Есть алгоритмы для решения этой проблемы, и я не дам вам их. Это намеренно. Я чувствую, что слишком много проблем дарит вам предварительно пережеванную пищу. Я дам +100 награду за первый ответ, который не упрощает проблему со встроенным (глядя на вас, Mathematica), на мое усмотрение.
orlp
2
Я думаю, что изображение идентичности 500x500 было бы полезно сказать, какому номеру соответствует каждый цвет.
xnor
Что составляет «достаточный контраст»?
Конор О'Брайен
@ ConorO'Brien Любой набор цветов, которые достаточно различимы. Я мог бы сделать это на 100% объективным с некоторой цветовой мерой, но я чувствую, что это излишне. Мне все равно, если вы используете красный, желтый, зеленый, оттенки серого или что-то еще, просто не используйте 4 оттенка серого, которые находятся в пределах 1% друг от друга (например, # 000000, # 000001, # 000002, # 000003).
orlp
Гм, я считаю, что этот вопрос теперь имеет право на награду. Могу ли я получить бонус +100? :)
JungHwan Мин

Ответы:

2

Октава, 120 113 байтов

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)))

Спасибо JungHwan Min за предоставленную ссылку на справочный документ в его ответе Mathematica.
Благодаря Stewie Griffin спас мне 7 байтов[any(any(x)) -> nnz(x)]

Здесь используются две функции:

1 f.: для стабилизации матрицы
2. Анонимная функция, которая принимает в nкачестве входных данных и показывает единичную матрицу.

Попробуйте это на rextester!для генерации матрицы 50 * 50

Прошедшее время для вычисления матрицы: 0.0844409 seconds.

Объяснение:

Рассмотрим функцию, f которая стабилизирует матрицу, задача поиска тождества просто

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

это ones(n)*6означает * n матрицу 6.

так для n=3:

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

Результат будет f(M-f(M))

Для стабилизации функции 2D используется свертка для ускорения задачи; В каждой итерации мы создаем двоичную матрицу bс одинаковым размером входной матрицы и устанавливаем ее равной 1, если соответствующий элемент входной матрицы равен> 3. Затем мы применяем двумерную свертку двоичной матрицы со следующей маской

0 1 0
1 0 1
0 1 0

представляющих четырех прямых соседей.
Результат свертки добавляется к матрице и 4 раза двоичная матрица вычитается из нее.

Цикл продолжается до тех пор, пока все элементы матрицы не будут <= 3

Безголовая версия :

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);
rahnema1
источник
8

Mathematica, 177 157 135 133 байта

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]]&

Занимает номер n. Результатом является идентификационная песочница. 0 черный, 1 светло-серый, 2 пурпурный и 3 сине-серый.

К сожалению, Mathematica не имеет встроенного для этого ...

Использует алгоритм, изложенный в статье Скотта Корри и Дэвида Перкинсона .

На моем 5-летнем ноутбуке уходит 91,7 секунды, чтобы вычислить идентификационную песочную кучу 50x50. Я уверен, что разумный современный настольный компьютер работает более чем на 50%. (У меня также есть более быстрый код в конце).

объяснение

f= ...

Определить функцию f(вход является матрицей песочницы): функция, которая ...

BlockMap[ ... ]~FixedPoint~#&

... повторяет BlockMapоперацию, пока выход не изменится. BlockMapоперация: ...


#~ArrayPad~1

... заполнить входной массив одним слоем 0 ...

{3,3},1

... разбить его на матрицы 3х3 со смещением 1 ...

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

... и для каждого раздела добавьте количество зерен песка, сброшенных на центральную ячейку, и значение центральной ячейки mod 4.

т.е. вывод fявляется стабилизированной версией ввода.


k=Table[6,#,#]

Определить kкак n на n массив из 6 с.

f[k-f@k]]

Вычислить f (k - f (k)).

Colorize[ ... ]

Примените цвета к результату.

Более быстрая версия (142 байта)

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]]&

Тот же код, но использует встроенную ротацию списка вместо BlockMap. Вычисляет n = 50 за 4,0 секунды на моем ноутбуке.

Юнг Хван Мин
источник
Учитывая, что вы следовали духу ограничения времени (реализуя реальный алгоритм, а не грубую силу), и очень вероятно, что мощный настольный компьютер работает на 50% быстрее, я позволю это. Поскольку он реализует фактический алгоритм без тривиализирующего встроенного, это дает бонус +100. Вам придется подождать, хотя я еще не могу начать щедрость.
orlp
Тем не менее, реализация этого довольно тривиально в Python (общеизвестно медленный язык), занимает всего ~ 2 с при n = 50. Может быть, вы можете немного ускорить его?
orlp
@orlp Готово, но оно длиннее исходного кода. Должен ли я сделать более быструю версию своим основным ответом или просто поставить ее в конце?
JungHwan Мин
Как это хорошо, я думаю.
orlp
0

Python 3 + Numpy + PIL, 385 370 364 байта

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')

Принимает участие в STDIN. Выводит изображение в оттенках серого на i.png. Черный соответствует 0, темно-серый - 1, светло-серый - 2, а белый - 0.

Использует формулу I = R(S - R(S)), где Iэлемент тождества, Sматрица, заполненная шестерками, и Rфункция сокращения.

Я мог бы, вероятно, сэкономить несколько байтов, переключившись на Python 2 и сделав это from numpy import*, но (1) у меня не установлен Numpy на Python 2 и (2) программа не заканчивалась from numpy import*.

Ungolfed:

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')
медь
источник
Вы можете сохранять байты, используя scipyили matplotlibотображая данные, а не генерируя изображение явно с помощью PIL.
orlp