Побудуйте пісковик


59

Абелева купа пісок , для наших цілей, є нескінченною сіткою з цілими координатами, спочатку порожній пісок. Після кожної секунди кладеться зерно піску на (0,0). Кожного разу, коли сітка-клітина містить 4 і більше зерен піску, вона розсипає по одному зерну піску кожному з чотирьох своїх сусідів одночасно. Сусідами (x, y) є (x-1, y), (x + 1, y), (x, y-1) та (x, y + 1).

Коли клітина розливається, це може спричинити розплив сусідів. Деякі факти:

  • Цей каскад з часом зупиниться.
  • Порядок, в якому розливають клітини, не має значення; результат буде однаковим.

Приклад

Через 3 секунди сітка виглядає так

.....
.....
..3..
.....
.....

Через 4 секунди:

.....
..1..
.1.1.
..1..
.....

Через 15 секунд:

.....
..3..
.333.
..3..
.....

А через 16 секунд:

..1..
.212.
11.11
.212.
..1..

Змагання

У мінімально можливих байтах запишіть функцію, яка займає одне додатне ціле число t і виводить зображення пісочника через t секунд.

Вхідні дані

Єдине натуральне ціле t у будь-якому обраному форматі.

Вихід

Зображення пісковика через t секунд із використанням символів

 . 1 2 3

Редагувати: Використовуйте будь-які чотири різних символи, які вам подобаються, або намалюйте зображення. Якщо ви не використовуєте ".123" або "0123", вкажіть у своїй відповіді, що означають символи.

На відміну від прикладів, ваш вихід повинен містити мінімальну кількість рядків і стовпців, необхідних для відображення ненульової частини пісковика.

Тобто для введення 3 вихід повинен бути

 3

Для 4 вихід повинен бути

 .1.
 1.1
 .1.

Оцінка балів

Застосовується стандартне підрахунок гольфу.

Правила

Немає мовних функцій або бібліотек, які вже знають, що таке "пісочниця".

Редагувати: розділ виводу відредаговано, обмеження набору символів повністю знято. Використовуйте будь-які чотири чіткі символи чи кольори, які вам подобаються.


2
Чи може вхід t бути 0? Який вихід тоді?
Луїс Мендо

1
Чи правильно для заданого часового кроку кілька каскадів можуть відбуватися поспіль? Тож у цей часовий крок каскади продовжують діяти, поки кожна клітина знову не стане 3 чи меншою?
недолік

2
@flawr: Так, це було б правильно. Подивіться на різницю між t = 15 і t = 16.
El'endia Starman

@LuisMendo Вхід вказано як позитивне t , тому нуль не є коректним входом.
Ерік Треслер

1
ЧИ ДУЖЕ ПОВИННІ мати .порожні клітини? Чи можемо ми мати 0дійсну порожню клітинку?
Андрей Костирка

Відповіді:


56

R, 378 343 297 291 байт

Як зазвичай, користувач постачає свій вхід через scan()(я вже використовував змінну t, тому візьмемо zзамість неї), тому другий рядок слід запустити окремо, а потім решту:

e=numeric
a=1%*%scan()
x=1
o=a>3
n=1
while(any(o)){
v=which(o,T)
if(any(v==1)){a=rbind(e(n+2),cbind(e(n),a,e(n)),e(n+2));x=x+1;n=n+2;v=which(a>3,T)}
q=nrow(v)
u=cbind(e(q),1)
l=v-u[,1:2];r=v+u[,1:2];t=v-u[,2:1];b=v+u[,2:1]
a[l]=a[l]+1;a[r]=a[r]+1;a[t]=a[t]+1;a[b]=a[b]+1
a[v]=a[v]-4
o=a>3}
a

Виводить масив, який містить значення aв- tго покоління (0, 1, 2 або 3).

Тестові приклади:

z=3
     [,1]
[1,]    3
z=4
     [,1] [,2] [,3]
[1,]    0    1    0
[2,]    1    0    1
[3,]    0    1    0
z=16
     [,1] [,2] [,3] [,4] [,5]
[1,]    0    0    1    0    0
[2,]    0    2    1    2    0
[3,]    1    1    0    1    1
[4,]    0    2    1    2    0
[5,]    0    0    1    0    0

Це допомагає нам, що ця річ є симетричною як по вертикалі, так і по горизонталі, а це означає, що крайня ліва точка отримує висоту 4, це означає, що верхня, найправіша і нижня точки також 4.

О, і я сказав, що ви можете робити гарні візуалізації?

Після 1000 крапель:

Абелевий пісковик після 1000 кроків

Через 50000 крапель (≈4 секунди):

Абелевий пісковик після 50000 кроків

Після 333333 крапель (≈15 хвилин):

Абелевий пісковик після 100000 кроків

Ви також можете намалювати його!

image(1:n,1:n,a,col=colorRampPalette(c("#FFFFFF","#000000"))(4), axes=F, xlab="", ylab="")

Ця річ зайняла 4 секунди для 10000 ітерацій, але значно сповільнюється для великих розмірів масиву (наприклад, пару хвилин на 100000 ітерацій). Ось чому воно стає настільки повільним (я оцінив швидкість росту, як у Темп зростаннята отримано τ (i) ≈689 · i ^ 1,08, тому середній час на одне додаткове зерно, поки весь пісковик не осідає після того iкроку, трохи перевищує один) , а загальний час як функція кількості зерен росте трохи повільніше, ніж квадратично (T (i) ≈0.028 * i ^ 1.74):

Середня ітерація, поки ворс не осідає

Приблизний час обчислення

А тепер із повним поясненням:

e=numeric # Convenient abbreviation for further repeated use
a=1%*%scan() # Creates a 1×1 array with a user-supplied number
x=1 # The coordinate of the centre
o=a>3 # Remember which cells were overflown
n=1 # Array height that is going to change over time
while(any(o)){ # If there is still any overflow
  v=which(o,T) # Get overflown cells' indices
  if(any(v==1)){ # If overflow occurred at the border, grow the array
    a=rbind(e(n+2),cbind(e(n),a,e(n)),e(n+2)) # Growing
    x=x+1 # Move the centre
    n=n+2 # Change the height
    v=which(a>3,T) # Re-index the overflowed cells
    }
  q=nrow(v) # See how many indices are overflown
  u=cbind(e(q),1) # Building block for neighbours' indices
  l=v-u[,1:2];r=v+u[,1:2];t=v-u[,2:1];b=v+u[,2:1] # L, R, T, B neighbours
  a[l]=a[l]+1;a[r]=a[r]+1;a[t]=a[t]+1;a[b]=a[b]+1 # Increment neighbours
  a[v]=a[v]-4 # Remove 4 grains from the overflown indices
  o=a>3} # See if still overflown indices remain
a # Output the matrix

Це вперше в моєму житті, коли зростаючі об'єкти (на зразок a <- c(a, 1)) працюють набагато швидше, ніж попередньо виділити велику порожню матрицю для значень і поступово заповнювати її тоною нулів, які не використовуються.

Оновлення. Golfed 18 байт шляхом видалення arr.indв whichсилу Billywob і заміни rep(0,n)з e=numeric;e(n)в 5 випадках внаслідок JDL , і ще 17 байт внаслідок JDL .

Оновлення 2. Оскільки пісочник є абелевим, він може починатися зі стопки потрібної висоти, тому я зняв зайвий цикл і отримав величезний приріст продуктивності!


1
Я розумію ваш додатковий стовпець, індекси рядків, які ви виводите, але я думаю, що я хочу обмежити вихід лише "відповіддю", і більше нічого. Я радий, що ти включив фотографії.
Ерік Треслер

1
Гарна відповідь Андрей! Ви, безумовно, можете пограти в кілька байтів, хоча, наприклад, попередньо визначивши rep(), враховуючи, що ви використовуєте його 6 разів. По-друге, я не думаю, що вам потрібно виписати arr.ind=Tваріант для which()функції. Просто використовуйте which(...,T).
Billywob

1
Визначити n=numericі використовувати це можна лише гравцем , оскільки n(k)символів менше, ніж r(0,k). Мені подобаються фотографії, хоча.
JDL

1
Ще одна пропозиція: 1%*%0менше символів, ніж array(0,c(1,1)). Також другий аргумент, який u <- cbindможе бути просто 1, cbindпоширює його до довжини першого аргументу за замовчуванням.
JDL

1
@GregMartin Виправлено це. Вибачте за це; у моїй першій мові ми вживаємо слово «я» і ніколи не переймаємось статтю відповідної людини (наприклад, «один маленький крок для чоловіка»); все ж іноді, дуже рідко, я називаю собаку "вона" або "він", тоді як це має бути "воно", якщо, наприклад, ви не є власником, і ви дійсно не хочете підкреслити стать свого анума ( незважаючи на те, що розповісти чоловіка від самки не так вже й важко).
Андрей Костирка

13

MATL , 55 53 48 43 42 байт

Натхненний відповіддю @ flawr .

Графічний вихід :

0i:"Gto~+XytP*+t"t4=t1Y6Z+b+w~*]]tat3$)1YG

Спробуйте в MATL Online! . На введення потрібно близько 10 секунд 30. Можливо, вам доведеться оновити сторінку і знову натиснути «Виконати», якщо вона не працює.

Ось приклад результату для введення даних 100:

введіть тут опис зображення

Вихід ASCII (43 байти) :

0i:"Gto~+XytP*+t"t4=t1Y6Z+b+w~*]]tat3$)48+c

Спробуйте в Інтернеті!

Пояснення

0          % Push a 0. This is the initial array. Will be resized in first iteration
i:         % Take input n. Generate range [1 2 ... n]
"          % For each, i.e. repeat n times
  Gto~+    %   Push input and add negate parity. This "rounds up" n to odd number
           %   m = n or n+1
  Xy       %   Identity matrix with that size
  tP*      %   Multiply element-wise by vertically flipped copy. This produces a
           %   matrix with a 1 in the center and the rest entries equal to 0
  +        %   Add to previous array. This updates the sandpile array
  t        %   Duplicate
  "        %   For each column (i.e. repeat m times)
    t4=    %     Duplicate. Compare with 4 element-wise. This gives a 2D mask that
           %     contains 1 for entries of the sandpile array that equal 4, and 0
           %     for the rest
    t      %     Duplicate
    1Y6    %     Predefined literal: [0 1 0; 1 0 1; 0 1 0]
    Z+     %     2D convolution, maintaining size
    b      %     Bubble up to bring sandpile array to top
    +      %     Element-wise addition. This adds 1 to the neighbours of a 4
    w      %     Swap to bring copy of mask to top
    ~*     %     Multiply bu negated mask. This removes all previous 4
  ]        %  End
]          % End
t          % Duplicate the updated sandpile array
a          % 1D mask that contains 1 for columns that contain a 1. This will be
           % used as a logical index to select columns
t          % Duplicate. This will be used as logical index to select rows (this
           % can be done because of symmetry)
3$)        % Keep only those rows and columns. This trims the outer zeros in the
           % sandpile array
1YG        % Display as scaled image

3
Я ревную 1Y6.
недолік

1
@flawr Але ваш ~mod(spiral(3),2)набагато розумніший :-)
Луїс Мендо

11

Матлаб, 160 156 148 байт

n=input('');z=zeros(3*n);z(n+1,n+1)=n;for k=1:n;x=z>3;z=z+conv2(+x,1-mod(spiral(3),2),'s');z(x)=z(x)-4;end;v=find(sum(z));z=z(v,v);[z+48-(z<1)*2,'']

Спочатку створюється занадто велика матриця, де nзнаходиться посередині десь. Потім каскад обчислюється за дуже зручною 2d згорткою. В кінці надлишок обрізається і вся справа перетворюється на рядок.

Приклад виводу для t=100

...121...
..32.23..
.3.323.3.
123.3.321
2.23.32.2
123.3.321
.3.323.3.
..32.23..
...121...

Як завжди:

Конволюція - запорука успіху.


v=any(z)замість v=find(sum(z))(я використовую це у своїй відповіді). Також 2*~zзамість(z<1)*2
Луїс Мендо

Мій комп'ютер замерз на вході n=500... Оброблявся n=400протягом декількох секунд. Я щось роблю не так?
Андрей Костирка

@ AndreïKostyrka Це працює для мене (Matlab R2015b)
Луїс Мендо

1
@ AndreïKostyrka Для введення nцієї програми генерується 3*n x 3*nматриця, тому їй потрібно зберігати 9*n^2номери. Крім того, це абсолютно неефективно, тому що ми також маємо абсолютно непотрібну довгу ітерацію від 1 до n. Але знову-таки це код-гольф , що робить програмою ефективною іншу чашку чаю.
недолік

@ AndreïKostyrka Ви можете зробити це більш ефективною пам'яттю, використовуючи розріджені матриці (другий рядок:) z=sparse(zeros(2*n+1))та змінивши цикл for на while any(z(:)>3). Тоді ви можете також можливо обчислити ядро згортки тільки один раз: kern = 1-mod(spiral(3),2).
недолік

9

Пітон 2, 195 +1 +24 = 220 217

from pylab import*
ifrom scipy.signal import convolve2d as c
k=(arange(9)%2).reshape(3,3)
def f(n):g=zeros((n,n),int);g[n/2,n/2]=n;exec"g=c(g/4,k,'same')+g%4;"*n;return g[any(g,0)].T[any(g,0)]

вихід для n = 16

array([[0, 0, 1, 0, 0],
       [0, 2, 1, 2, 0],
       [1, 1, 0, 1, 1],
       [0, 2, 1, 2, 0],
       [0, 0, 1, 0, 0]])

є багато непотрібних прокладки та ітерацій, використовуючи nверхню межу як "достатньо хорошої", але n = 200 все-таки завершено за секунду і n = 500 приблизно за 12 секунд

неозорий

from pylab import*
from scipy.signal import convolve2d as c
k=array([0,1,0],
        [1,0,1],
        [0,1,0])
def f(n):
  g=zeros((n,n))                 # big grid of zeros, way bigger than necessary
  g[n/2,n/2]=n                   # put n grains in the middle
  exec"g=c(g/4,k,'same')+g%4;"*n # leave places with <4 grains as is, convolve the rest with the kernel k, repeat until convergence (and then some more)
  return g[any(g,0)].T[any(g,0)] # removes surrounding 0-rows and columns

заміна з return xдопомогою imshow(x)додає один символ і виводить негарне интерполированное зображення, додавши , imshow(x,'gray',None,1,'nearest')видаляє розмиті інтерполяції в результаті чого вихідного сигналу до специфікації

n = 100


Я отримую наступне повідомлення про помилку , коли я запускаю свій код ImportError: No module named convolve2d. Зміна import scipy.signal.convolve2d as cдо from scipy.signal import convolve2d as cвирішує проблему. Я використовую scipy версії 0.16.1, чи потрібна мені старіша чи новіша версія? Або питання щось інше?
Ендрю Епштейн

дивно, тепер, коли ви згадуєте, що це теж не працює для мене. Я, мабуть, зробив це правильно вперше в інтерактивному режимі, потім скоротив його і проігнорував помилку, але функція залишилася в пам’яті
DenDenDo

6

Perl, 157 147 байт

Включає +1 для -p

Запустити з підрахунком на STDIN, друкує карту за допомогою 0123STDOUT:

sandpile.pl <<< 16

sandpile.pl:

#!/usr/bin/perl -p
map{++substr$_,y///c/2-1,1;/4
/?$.+=s%^|\z%0 x$..$/%eg+!s/\b/0/g:s^.^$&%4+grep{3<substr$\,0|$_+"@+",1}-$.-2,-2,0,$.^eg while/[4-7]/}($\="0
")x$_}{

5

Python 3 2, 418 385 362 342 330 байт

w='[(i,j)for i in r(n)for j in r(n)if a[i][j]>3]'
def f(z):
 a,x,r=[[z]],0,range
 for _ in[0]*z:
  n=len(a);v=eval(w)
  if[1for b,c in v if(b==0)+(c==0)]:n+=2;a=[[0]*n]+[[0]+a[i]+[0]for i in r(n-2)]+[[0]*n];x+=1;v=eval(w)
  for c,d in v:exec'a[c+%s][d+%s]+=1;'*4%(-1,0,1,0,0,-1,0,1);a[c][d]-=4
 for i in a:print''.join(map(str,i))

Редагувати: збережено 6 байт завдяки @ Qwerp-Derp

Вдячний @ Andreï Kostyrka, оскільки це прямий переклад його R-коду на Python.


Я думаю, ви можете перенести призначення a,x,rв аргументи функції.
Loovjo

1
Я переграв ваш код на кілька байт ... це не так багато, але це доведеться робити. Ви не заперечуєте, якщо я поміщую вашу відповідь і якщо я зміню версію Python на Python 2?
клісмік

@ Qwerp-Derp: Відчувайте себе вільно! Я хотів би побачити, що ти зробив.
Ендрю Епштейн

3

JavaScript, 418 416 406 400 393 байт

Створює анонімну функцію, яка відображає вихід на консолі.

var f =
    t=>{a=(q,w)=>Math.max(q,w);c=_=>{x=a(p[0],x);y=a(p[1],y);m[p]=(g(p)+1)%4;if(!m[p]){s.push([p[0],p[1]]);}};x=y=0,m={};g=k=>{v=m[k];return!v?0:v;};m[o=[0,0]]=1;s=[];while(--t){m[o]=(m[o]+1)%4;if(!m[o]){s.push(o);}while(s.length){p=s.pop();p[0]++;c();p[0]-=2;c();p[0]++;p[1]++;c();p[1]-=2;c();p[1]++;}}s='';for(i=-x;i<=x;i++){for(j=-y;j<=y;j++){v=g([i,j]);s+=v==0?'.':v;}s+='\n';}console.log(s);}
<input id="i" type="number"><input type="button" value="Run" onclick="var v = +document.getElementById('i').value; if (v>0) f(v)">


1
Попередження: Я натиснув 'run' без введення, і екран вийшов з ладу (нескінченний цикл). Не будь таким дурним, як я.
roberrrt-s

1
@Roberrrt Я оновив свою відповідь, щоб запобігти цьому.
hetzi

3

Нім, 294 символи

import os,math,sequtils,strutils
var
 z=parseFloat paramStr 1
 y=z.sqrt.toInt+1
 w=y/%2
 b=y.newSeqWith newSeq[int] y
 x=0
proc d(r,c:int)=
 b[r][c]+=1;if b[r][c]>3:b[r][c]=0;d r-1,c;d r,c+1;d r+1,c;d r,c-1
for i in 1..z.toInt:d w,w
while b[w][x]<1:x+=1
for r in b[x..< ^x]:echo join r[x..< ^x]

Компілювати та запустити:

nim c -r sandbox.nim 1000

Примітки:

  1. Мені вдалося створити більш коротку версію, яка використовує фіксований розмір таблиці, але я відредагував її на користь динамічної.
  2. Після обчислення пісочниці обчислюється xяк кількість нульових стовпців на початку середнього ряду.
  3. Для відображення таблицю розбивають, виключаючи xрядки та стовпці з кожного кінця.

Продуктивність

nim c --stackTrace:off --lineTrace:off --threads:off \ 
      --checks:off --opt:speed sandbox.nim

time ./sandbox   10000       0.053s
time ./sandbox   20000       0.172s
time ./sandbox   30000       0.392s
time ./sandbox   40000       0.670s
time ./sandbox  100000       4.421s
time ./sandbox 1000000    6m59.047s

3

Scala, 274 байт

val t=args(0).toInt
val s=(Math.sqrt(t)+1).toInt
val (a,c)=(Array.ofDim[Int](s,s),s/2)
(1 to t).map{_=> ?(c,c)}
println(a.map{_.mkString}.mkString("\n"))
def?(b:Int,c:Int):Unit={
a(b)(c)+=1
if(a(b)(c)<4)return
a(b)(c)=0
?(b+1,c)
?(b-1,c)
?(b,c+1)
?(b,c-1)
}

Використання:

scala sandpile.scala <iterations>

Я не думаю, що є багато пояснення щодо цього. В основному, це лише додає один центр піску в центр. Потім перевіряє, чи не більший він від 4, якщо так, то він проллється і перевіряє, чи всі сусіди перевищують 4, переливаються тощо. Це досить швидко.

Продуктивність:

  • t = 10000 72мс
  • t = 20000 167 мс
  • t = 30000 419 мс
  • t = 40000 659мс
  • t = 100000 3413мс
  • t = 1000000 приблизно 6 хвилин

Моя програма передбачає, що в центрі (0,0) пісковик спочатку потрапляє в радіус 15 при t = 1552. Для цього знадобиться масив розміром 31x31 (координати від -15 до 15 включно). Ви впевнені, що це правильно через t = 5000?
Ерік Треслер

Я не впевнений, що це правильно, хоча я думаю, що я правильно зрозумів логіку? Я отримую індекс масиву поза межами винятків на t> 5593
AmazingDreams

Коли я зростаю, а потім негайно перевіряю, чи є розлив, він не виходить за межі t = 1552. Я б сказав, що це правильна реалізація. Я оновив код.
AmazingDreams

Ваша продуктивність може бути побита лише шляхом маніпулювання прямим масивом на C або Fortran з оптимізацією компілятора. Я заздрю ​​тобі.
Андрей Костирка

@ AndreïKostyrka, так, тут сяє скала! Мій вихід не відповідає специфікаціям, хоча тому мені доведеться попрацювати над цим
AmazingDreams

2

J, 76 байт

p=:0,.~0,.0,~0,]
p`(4&|+3 3([:+/@,*&(_3]\2|i.9));._3[:<.4%~p)@.([:*/4>{.)^:_

Я визначаю дієслово, pяке прокладає межу нулів навколо введення. Основне дієслово приймає масив як вхідний. Потім він перевіряє перший рядок на наявність пісочника, який містить 4 і більше зерен. Якщо це так, він виводить той же масив, за винятком прокладеного p, і в іншому випадку він виконує 2d згортку для імітації падаючих зерен. Основне дієслово повторюється до зближення за допомогою енергооператора ^:_.

Використання

   p =: 0,.~0,.0,~0,]
   f =: p`(4&|+3 3([:+/@,*&(_3]\2|i.9));._3[:<.4%~p)@.([:*/4>{.)^:_
   f 15
0 3 0
3 3 3
0 3 0
   f 50
0 0 0 1 0 0 0
0 0 3 1 3 0 0
0 3 2 2 2 3 0
1 1 2 2 2 1 1
0 3 2 2 2 3 0
0 0 3 1 3 0 0
0 0 0 1 0 0 0
   timex 'r =: f 50000'
46.3472
   load 'viewmat'
   ((256#.3&#)"0<.255*4%~i._4) viewmat r

Для обчислення результату для n = 50000 потрібно близько 46 секунд , і результат може бути відображений за допомогою viewmatаддону з однотонною кольоровою гамою.

фігура


2

C 229 (з великою кількістю попереджень)

G[99][99],x,y,a=99,b=99,c,d;S(x,y){if(++G[y][x]>3)G[y][x]=0,S(x+1,y),S(x-1,y),S(x,y+1),S(x,y-1);a=x<a?x:a;b=y<b?y:b;c=x>c?x:c;d=y>d?y:d;}F(t){for(;t--;)S(49,49);for(y=b;y<=d;y++){for(x=a;x<=c;x++)printf("%d ",G[y][x]);puts("");}}

/* call it like this */
main(_,v)char**v;{F(atoi(v[1]));}

Гаразд, я здаюсь: чому ваш масив 99 на 98?
Ерік Треслер

@EricTressler Як я цього не знайшов на тестуванні ?!
Джеррі Єремія


1

PHP, 213 байт

function d($x,$y){global$p,$m;$m=max($m,$x);$q=&$p[$y][$x];if(++$q>3){$q=0;d($x+1,$y);d($x-1,$y);d($x,$y+1);d($x,$y-1);}}while($argv[1]--)d(0,0);for($y=-$m-1;$y++<$m;print"\n")for($x=-$m;$x<=$m;)echo+$p[$y][$x++];

рекурсивно створює ворс $p, запам'ятовуючи розмір у $m; потім друкується з вкладеними петлями.
Бігайте з -r.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.