在一次随机化算法测试中我无意发现,通过粒子的随机移动和沉积可构造类似如下图形
首先初始化一个N*N的网格,其中每个格子有几率p1被放置一个粒子,状态设为活跃,在中心放置一个非活跃粒子,
随机选择一个活跃粒子和移动方向(上下左右),若粒子的这个方向上1格没有粒子或边界则移动,若粒子接触到非活跃粒子则有p2几率变为非活跃粒子(沉积),
直到所有粒子变为非活跃状态,算法结束。
通常p1取0.05~0.2,p2取0.5~1.0,也可以对不同的粒子设置不同的p1,p2
加入重力等其它规则有时可以得到意想不到的效果
测试表明此算法在p1=0.1,p2=1时期望时间复杂度大约是O(n4),然而我没有能力证明。
以下是一个简单的实现
c++版
#include<cstdio> #include<cstdlib> #include<ctime> const int N=200; const double p1=0.1; const double p2=1.0; int xs[]={-1,0,1,0}; int ys[]={0,-1,0,1}; int bmp[N+2][N+2]; struct pos{ int x,y; pos(){} pos(int a,int b):x(a),y(b){} }ps[N*N]; int p=0; int main(){ srand(time(0)); for(int i=0;i<=N+1;i++)//边界 bmp[0][i]=bmp[N+1][i]=bmp[i][0]=bmp[i][N+1]=-1; for(int i=1;i<=N;i++) for(int j=1;j<=N;j++) if(rand()<RAND_MAX*p1){//初始化 bmp[i][j]=1; ps[p++]=pos(i,j); } bmp[N/2+1][N/2+1]=2; while(p){ int w=rand()%p; int x=ps[w].x,y=ps[w].y; int f=rand()&3; bool d=0; if(bmp[x][y]==2)d=1; else for(int i=0;i<4;i++) if(bmp[x+xs[i]][y+ys[i]]==2) d=1; if(d&&rand()<p2*RAND_MAX){//沉积 bmp[x][y]=2; ps[w]=ps[--p]; continue; } if(bmp[x+xs[f]][y+ys[f]]==0){//移动 bmp[ps[w].x=x+xs[f]][ps[w].y=y+ys[f]]=1; bmp[x][y]=0; } }
//结果保存在数组bmp中 return 0; }
js+html5版,图形化界面
<html> <body> <p> 模拟沉积法生成器(测试版) <br/> 需js及html5支持 <br/> 边长较大时可能需要较长时间 </p> <br/> 边长(10~200) <input type="text" id="N" value="50"></input> <br/> p1(0.05~0.5) <input type="text" id="p1" value="0.12"></input> <br/> p2(0.5~1) <input type="text" id="p2" value="1"></input> <br/> <input type='button' onclick='onc()' value='生成'></input> <br/> <canvas id="canvas" width="400" height="400"></canvas> <script type="text/javascript"> var cv=document.getElementById("canvas") var i1=document.getElementById("N") var i2=document.getElementById("p1") var i3=document.getElementById("p2") var ctx=cv.getContext('2d') var n=50; var vs=new Array() var ps=new Array() var xs=new Array() var ys=new Array() xs[0]=-1;xs[1]=0;xs[2]=1;xs[3]=0 ys[0]=0;ys[1]=-1;ys[2]=0;ys[3]=1 var p=0,p1=0.12,p2=1 var doing=0 function draw(x,y){ ctx.fillRect(x*2,y*2,2,2) } function rnd(){ return Math.random() } function pre(){ ctx.fillStyle="white" ctx.fillRect(0,0,400,400) ctx.fillStyle="red" p=0 for(var i=0;i<=n+1;i++)vs[i]=new Array(n+2) for(var i=1;i<=n;i++) for(var j=1;j<=n;j++) if(rnd()<p1){ vs[i][j]=1 ps[p++]={x:i,y:j} }else vs[i][j]=0 for(var i=0;i<=n+1;i++)vs[i][0]=vs[i][n+1]=vs[0][i]=vs[n+1][i]=-1 vs[n/2+1][n/2+1]=2 draw(n/2+1,n/2+1) } function cal(){ doing=1 pre() while(p>0){ var w=Math.floor(rnd()*p) var x=ps[w].x,y=ps[w].y var f=Math.floor(rnd()*4) var d=0 if(vs[x][y]==2)d=1 else for(var i=0;i<4;i++){ if(vs[x+xs[i]][y+ys[i]]==2)d=1 } if(d>0&&rnd()<p2){ vs[x][y]=2 ps[w]=ps[--p] draw(x,y) }else if(vs[x+xs[f]][y+ys[f]]==0){ vs[x+xs[f]][y+ys[f]]=1 vs[x][y]=0 ps[w].x+=xs[f] ps[w].y+=ys[f] } } doing=0 } function onc(){ if(doing)return n=parseInt(i1.value) p1=parseFloat(i2.value) p2=parseFloat(i3.value) if(n<=200&&n>0&&p1>=0.05&&p1<=0.5&&p2>=0.5&&p2<=1)cal() else{ i1.value="" i2.value="" i3.value="" } } cal() </script> </body> </html>
暂时并没有什么实用价值。。