サンドパイルを構築する


59

アーベル砂山は、我々の目的のために、砂の最初は空整数座標を持つ無限のグリッド、です。1秒ごとに、砂粒が(0,0)に配置されます。グリッドセルに4つ以上の砂粒があるときはいつでも、1つの砂粒をその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

編集:好きな4つの異なる文字を使用するか、絵を描きます。「.123」または「0123」を使用していない場合は、回答で文字の意味を指定してください。

例とは異なり、出力には、サンドパイルのゼロ以外の部分を表示するために必要な最小限の行と列が含まれている必要があります。

つまり、入力3の場合、出力は次のようになります。

 3

4の場合、出力は

 .1.
 1.1
 .1.

得点

標準的なゴルフのスコアリングが適用されます。

ルール

サンドパイルが何であるかを既に知っている言語関数またはライブラリは許可されていません。

編集:出力セクションが編集され、文字セットの制限が完全に解除されました。好きな4つの異なる文字または色を使用します。


2
入力tにでき0ますか?その時の出力は何ですか?
ルイスメンドー

1
特定のタイムステップで複数のカスケードが連続して発生する可能性があるのは正しいですか?では、そのタイムステップで、すべてのセルが再び3以下になるまでカスケードが継続しますか?
-flawr

2
@flawr:はい、それは正しいでしょう。t = 15とt = 16の違いを見てください。
エレンディアスターマン

@LuisMendo入力は正のtとして指定されているため、ゼロは有効な入力ではありません。
エリックトレスラー16

1
.空のセルに対して本当に必要なのでしょうか?0有効な空のセルとして使用できますか?
アンドレイKostyrka 16

回答:


56

R、378の 343 297 291バイト

通常、ユーザーはscan()(変数を既に使用しているため、代わりに使用しますt)を介して入力を提供するzため、2行目を個別に起動し、残りを実行する必要があります。

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

出力の値を含む配列at世代(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="")

この処理には10000回の反復で4秒かかりましたが、配列サイズが大きくなるとかなり遅くなります(たとえば、100000回の反復では数分)。これが非常に遅くなる理由です(成長速度をのように推定し、成長速度τ(i)≈689・i ^ 1.08を得たため、iステップ1 後に砂山全体が落ち着くまでの1粒あたりの平均時間は1 よりわずかに大きくなります) 、および粒子数の関数としての合計時間は、二次関数よりも少し遅くなります(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))が、値に大きな空のマトリックスを事前に割り当て、未使用のゼロを徐々に埋めるよりもずっと速く動作するのは、これが私の人生で初めてです。

更新。除去することによって、18のバイトをGolfed arr.indにおいてwhichによるBillywobと交換rep(0,n)e=numeric;e(n)のために5つの例ではJDL、そしてために17バイト以上JDL

更新2。サンドパイルはAbelianであるため、希望する高さのスタックで開始される可能性があるため、冗長ループを削除し、生産性を大幅に向上させました。


1
余分な列、出力する行インデックスについてはポイントを得ますが、出力を「答え」だけに制限したいと思います。ただし、写真を含めてくれてうれしいです。
エリックトレスラー16

1
いい答えアンドレイ!ただし、rep()6回使用すれば、たとえばpredefiningのように、数バイトは間違いなくゴルフできます。第二に、関数のarr.ind=Tオプションを記述する必要はないと思いますwhich()。単に使用しますwhich(...,T)
ビリーウォブ

1
であるよりも文字数が少ないn=numericため、代わりに定義して使用する方がゴルファーかもしれません。私は写真が好きです。n(k)r(0,k)
JDL

1
別の提案:1%*%0はより少ない文字ですarray(0,c(1,1))。また、の2番目の引数はu <- cbind1のみでcbind、デフォルトで最初の引数の長さに拡張されます。
JDL

1
@GregMartinこれを修正しました。そのために残念; 私の母国語では、「自己」という言葉を使用し、問題の人の性別を気にしません(「男性にとっての小さな一歩」など)。それでも、時々、非常にまれに、私は犬を「彼女」または「彼」と呼びますが、あなたが飼い主であり、あなたが本当にあなたのアヌマルの性を強調したい場合を除き、それは「それ」でなければなりませ(男性と女性を区別するのはそれほど難しくないという事実にもかかわらず)。
アンドレイKostyrka

13

MATL55 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
私はofしてい1Y6ます。
-flawr

1
@flawrしかし、あなた~mod(spiral(3),2)はもっと賢いです:-)
ルイスメンドー

11

MATLAB、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数秒間処理されていました。私は何か間違っていますか?
アンドレイKostyrka 16

@AndreïKostyrkaそれは私のために働く(Matlab R2015b)
ルイスメンドー

1
@AndreïKostyrka nこのプログラムの入力では3*n x 3*n行列が生成されるため、約9*n^2数を保存する必要があります。また、1からnまでのまったく不要な長い反復があるため、完全に非効率的です。しかし、これもcode-golfであり、プログラムを効率的にすることは別のお茶です。
flawr

@AndreïKostyrkaスパース行列(2行目:)を使用z=sparse(zeros(2*n+1))し、forループをに変更することにより、メモリ効率を向上させることができますwhile any(z(:)>3)。畳み込みカーネルを一度だけ計算することもできますkern = 1-mod(spiral(3),2)
flawr

9

Python 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が1秒で完了し、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)1つの文字が追加され、い補間画像が出力されます。追加imshow(x,'gray',None,1,'nearest')すると、ぼやけた補間が削除され、出力が仕様に達します。

n = 100


コードを実行すると、次のエラーが表示されますImportError: No module named convolve2d。変更import scipy.signal.convolve2d as cfrom 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))

編集:@ Qwerp-Derpのおかげで6バイト節約

これは@AndreïKostyrkaの功績です。これは、RコードをPythonに直接変換したものだからです。


の割り当てをa,x,r関数の引数に移動できると思います。
Loovjo

1
私はあなたのコードを数バイト減らしました...それは大したことではありませんが、やらなければなりません。回答に編集を加えたり、PythonのバージョンをPython 2に変更したりしても構いませんか?
clismique

@ 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
警告:入力せずに「実行」を押すと、画面がクラッシュしました(無限ループ)。私ほど愚かではない。
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>

これについて説明することは多くないと思います。基本的には、中央に1粒の砂を追加するだけです。次に、それが4より大きいかどうかをチェックします。4より大きい場合、4より大きいすべての近隣をチェックします。これは非常に高速です。

性能:

  • t = 10000 72ms
  • t = 20000 167ms
  • t = 30000 419ms
  • t = 40000 659ms
  • t = 100000 3413ms
  • t = 1000000約6分

私のプログラムでは、(0,0)を中心として、t = 1552で半径15の砂山に最初に当たることを示唆しています。それには、格納するために31x31配列が必要になります(-15〜15を含む座標)。これはt = 5000までは正しいですか?
エリックトレスラー

これが正しいかどうかはわかりませんが、論理は正しいと思いますか?私は> 5593トンの限界例外のうち、配列のインデックスを取得
AmazingDreams

インクリメントし、すぐに流出をチェックすると、t = 1552で範囲外になります。それが正しい実装だと思います。コードを更新しました。
AmazingDreams

コンパイラーの最適化を使用してCまたはFortranで直接配列を操作することでのみ、パフォーマンスが低下します。私はあなたがうらやましい。
アンドレイKostyrka

@AndreïKostyrka、はい、これはscalaが輝くところです!私の出力は仕様に準拠していませんが、それに取り組む必要があります
-AmazingDreams

2

J、76バイト

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

p入力の周囲にゼロの境界を埋め込む動詞を定義します。メイン動詞は配列を入力として受け取ります。次に、最初の行で4つ以上の穀物を含む砂山がないかどうかを確認します。ある場合は、を使用してパディングされている以外は同じ配列を出力しますp。主動詞は、累乗演算子を使用して収束するまで繰り返され^:_ます。

使用法

   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 x 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ます。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.