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


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.