我想知道是否有可能创建不同的步骤兰顿蚁,一个细胞自动机,在 Latex 中使用 TikZ 或其他包。例如像这样:
答案1
这是一个简单的方法元帖子包裹在luamplib
库中-用编译lualatex
。
\RequirePackage{luatex85}
\documentclass[border=5mm]{standalone}
\usepackage{luamplib}
\begin{document}
\mplibtextextlabel{enable}
\mplibnumbersystem{double}
\begin{mplibcode}
beginfig(1);
numeric x,y,N;
boolean s[][];
pair heading;
N = 1000;
x = y = 0;
heading = up;
for i=0 upto N:
if not known s[x][y]: s[x][y] := false; fi
if s[x][y]:
s[x][y] := false;
heading := round(heading rotated -90);
else:
s[x][y] := true;
heading := round(heading rotated 90);
fi
x := x + xpart heading;
y := y + ypart heading;
endfor
z0 = (x,y);
numeric n;
n = 42;
for x=-n upto n:
for y=-n upto n:
if known s[x][y]:
if s[x][y]:
drawdot 3(x,y) withpen pencircle scaled 2;
fi
fi
endfor
endfor
drawdot 3z0 withpen pencircle scaled 2.2 withcolor 2/3 red;
%label.urt(decimal N, 3(-n,-n));
endfig;
\end{mplibcode}
\end{document}
使用N=1000
(如上所述),你应该得到类似这样的结果:
如果你把它调大一点并进行设置N=10000
,那么你会得到这样的结果:
然后大约N=10200
“高速公路”开始出现,因此N=11000
你会得到:
关于数字大小和精度的说明
在普通的 Metapost 中(即使包裹在内luamplib
),默认的数字系统是 Knuth 的“缩放”系统,其中允许的最大绝对值略小于 4096。因此,我添加了\mplibnumbersystem{double}
以便能够设置N
为大于 4096 的值,这样我们就可以看到蚂蚁有趣的行进。
这会产生令人恼火的副作用,使简单操作的结果不如默认系统准确。因此,在默认数字系统中,当我设置
heading = up;
heading
获取值(0,1)
,以便当我设置
heading := heading rotated 90;
heading
变成(-1,0)
,并且xpart heading
和ypart heading
始终看起来像整数,这样我就可以安全地将它们添加到x
和y
索引中。
但有了double
数字系统
heading := heading rotated 90;
设置heading
为(-1,6.123233995736766e-17)
不太一样,意味着这些部分不能用于添加到索引中。
我对这个烦恼的解决方案是添加,round()
以便根据需要将微小的数字四舍五入为零。 round()
定义为,如果您将它应用于pair
变量,它会对两个部分进行四舍五入。
答案2
这是一个使用 tikz 绘制世界的 LaTeX 解决方案。根据 Langton 蚂蚁的规则维基百科:
在白色方块处,向右旋转 90°,翻转方块颜色,向前移动一个单位。
在黑色方块处,向左旋转 90°,翻转方块颜色,向前移动一个单位。
蚂蚁的位置和方向用红色箭头表示。
% Langton's ant
\documentclass[tikz]{standalone}
\usepackage{tikz}
% boundaries of the known universe
% The boundaries expand automatically as the ant runs.
% If you want to have a stable map that does not expand,
% set the boundaries to some values large enough before letting the ant run.
\newcounter{N}
\newcounter{E}
\newcounter{S}
\newcounter{W}
% position and direction of ant
\newcounter{antx}
\newcounter{anty}
\newcounter{antdir}% 0=N, 1=E, 2=S, 3=W
% \ifIsWhiteSquare{x}{y}{code for white}{code for black}
% white = "\square:x:y is undefined", black = "\square:x:y is defined"
\newcommand\ifIsWhiteSquare[4]{\ifcsname square:#1:#2\endcsname#4\else#3\fi}
\newcommand\setwhite% equate \square:x:y with an undefined macro
{\expandafter\let\csname square:\arabic{antx}:\arabic{anty}\endcsname\undefined}
\newcommand\setblack% define \square:x:y as a macro expanding to nothing
{\expandafter\def\csname square:\arabic{antx}:\arabic{anty}\endcsname{}}
\newcommand\turnright% +1 mod 4
{\stepcounter{antdir}\ifnum\value{antdir}>3\addtocounter{antdir}{-4}\fi}
\newcommand\turnleft% -1 mod 4
{\addtocounter{antdir}{3}\ifnum\value{antdir}>3\addtocounter{antdir}{-4}\fi}
\newcommand\stepforward% add +1/-1 to antx/anty and expand known universe
{\ifcase\value{antdir}%
\stepcounter{anty}%
\ifnum\value{anty}>\value{N}\stepcounter{N}\fi
\or
\stepcounter{antx}%
\ifnum\value{antx}>\value{E}\stepcounter{E}\fi
\or
\addtocounter{anty}{-1}%
\ifnum\value{anty}<\value{S}\addtocounter{S}{-1}\fi
\else
\addtocounter{antx}{-1}%
\ifnum\value{antx}<\value{W}\addtocounter{W}{-1}\fi
\fi
}
% Rules for Langton's ant according to Wikipedia
% - At a white square, turn 90° right, flip the color of the square, move forward one unit.
% - At a black square, turn 90° left, flip the color of the square, move forward one unit.
\newcommand\antstep
{\ifIsWhiteSquare{\arabic{antx}}{\arabic{anty}}%
{\setblack\turnright}%
{\setwhite\turnleft}%
\stepforward
}
% \run does "timer" steps in a row
\newcounter{timer}
\newcommand\run
{\addtocounter{timer}{-1}%
\ifnum\value{timer}<0%
\else
% \expandafter\theUniverse % uncomment for snapshots between steps
\expandafter\antstep
\expandafter\run
\fi
}
\newcommand\ant% mark position and direction of ant with an arrow
{{\boldmath$\ifcase\value{antdir}\uparrow\or\rightarrow\or\downarrow\or\leftarrow\fi$}}
\newcommand\theUniverse
{\begin{tikzpicture}
\draw (\value{W},\value{S}) rectangle (\value{E}+1,\value{N}+1);
\foreach \i in {\arabic{W},...,\arabic{E}}
\foreach \j in {\arabic{S},...,\arabic{N}}
{\ifIsWhiteSquare{\i}{\j}%
{}%
{\draw[fill] (\i,\j) rectangle +(1,1);}%
}
\node[red] at (\arabic{antx}+0.5,\arabic{anty}+0.5) {\ant};
\end{tikzpicture}%
}
\begin{document}
% 11 x 11 grid with ant in the middle heading north
\setcounter{N}{5}%
\setcounter{E}{5}%
\setcounter{S}{-5}%
\setcounter{W}{-5}%
\setcounter{timer}{11000}%
\run
\theUniverse
\end{document}
该 gif 图像由命令生成
convert -density 300 -delay 30 -loop 0 -background white -alpha remove ant.pdf ant.gif
纯 LaTeX 解决方案比 Lualatex 版本慢,但仍然足够快,可以在几分之一秒内完成 12000 个步骤(比绘制最终图像所需的速度更快)。
答案3
需要。像往常一样,从到的lualatex
翻译有点笨拙,为了提高速度,只需提取彩色单元格并传递到绘图部分。lua
tikz
\RequirePackage{luatex85}
\documentclass[tikz,border=5]{standalone}
\begin{document}
\directlua{
minx = 1; miny = 1; maxx = -1; maxy = -1
x = 0; y = 0; a = 0; t = {}; p = {}; n = 0
for i = 0,10000 do
if x > maxx then maxx = x end; if x < minx then minx = x end
if y > maxy then maxy = y end; if y < miny then miny = y end
t[x] = t[x] or {}
t[x][y] = 1 - (t[x][y] or 0)
a = a - (t[x][y] * 2 - 1) * 90
if a < 0 then a = a + 360 end; if a >= 360 then a = a - 360 end
x = x + ((a == 0) and 1 or 0) - ((a == 180) and 1 or 0)
y = y + ((a == 90) and 1 or 0) - ((a == 270) and 1 or 0)
end
for j = minx,maxx do; for i = miny,maxy do
if (t[j] or {})[i] == 1 then
n = n + 1
p[n] = '(' .. j .. ',' .. i .. ')'
end
end; end
ant = '(' .. x .. ',' .. y .. ')'
}
\edef\n{\directlua{tex.print(n)}}
\begin{tikzpicture}[fill1/.style={fill=black}, x=1pt, y=-1pt]
\foreach \i in {1,...,\n}\fill \directlua{tex.print(p[\i])} circle [radius=0.5];
\fill[red] \directlua{tex.print(ant)} circle [radius=0.375];
\end{tikzpicture}
\end{document}
这里有一个更加丰富多彩的变体,其中的色调由对单元格的访问次数模 20 来确定:
\RequirePackage{luatex85}
\documentclass[tikz,border=5]{standalone}
\usepackage{luacode}
\begin{document}
\begin{luacode*}
minx = 1; miny = 1; maxx = -1; maxy = -1
x = 0; y = 0; a = 0; t = {}; p = {}; b = {}; n = 0
for i = 0,10500 do
if x > maxx then maxx = x end; if x < minx then minx = x end
if y > maxy then maxy = y end; if y < miny then miny = y end
t[x] = t[x] or {}
t[x][y] = ((t[x][y] or 0) + 1) % 20
a = a - ((((t[x][y] % 2) == 0) and 1 or 0)* 2 - 1) * 90
if a < 0 then a = a + 360 end; if a >= 360 then a = a - 360 end
x = x + ((a == 0) and 1 or 0) - ((a == 180) and 1 or 0)
y = y + ((a == 90) and 1 or 0) - ((a == 270) and 1 or 0)
end
for j = minx,maxx do; for i = miny,maxy do
c = (t[j] or {})[i] or 0
if c > 0 then
n = n + 1
p[n] = '[fill' .. c .. '/.try] (' .. j .. ',' .. i .. ')'
b[n] = c
end; end
end
ant = '(' .. x .. ',' .. y .. ')'
\end{luacode*}
\edef\n{\directlua{tex.print(n)}}
\begin{tikzpicture}[x=-5pt,y=5pt]
\foreach \i [evaluate={\o=\directlua{tex.print{b[\i]}}*5;}] in {1,...,\n}
\fill \directlua{tex.print(p[\i])} [blue!\o] circle [radius=0.5];
\fill[red] \directlua{tex.print(ant)} circle [radius=0.375];
\end{tikzpicture}
\end{document}
答案4
这是渐近线MWE 草案。它使用一个langtonant
类来运行具有不同数量蚂蚁的模拟。例如,代码
// ant.asy
//
import langtonants;
AntField field=AntField(nAnts=42);
field.go(steps=40000,saveEvery=400,prefix="a",maxFrameNo=40000);
(运行asy ant.asy
) 将在默认的 200x200 区域内的随机位置分配 42 只蚂蚁,然后运行 40000 步模拟,以pbm
ascii 格式保存每 400 帧。
类文件langtonant.asy
:
// langtonant.asy
struct Ant{
int posInd;
int antDir; // direction 0:W, 1:E, 2:S, 3:N
void operator init(int posInd, int antDir=3){
assert(posInd>0);
this.posInd=posInd;
this.antDir=antDir;
}
};
struct AntField{
int minDim=10;
int nRows, mCols, nAnts, fieldSize;
int numSteps, stepNo;
Ant[] ants;
int[] field;
int[] occupiedCells;
//
int[] leftDir= {2,3,1,0}; // direction after left turn
int[] rightDir={3,2,0,1}; // direction after right turn
int[][] nextDir={rightDir, leftDir};
int[] dRow={0,0,-1,1};
int[] dCol={-1,1,0,0};
void placeAnts(){
for(int i=0;i<nAnts;++i){
ants.push(Ant(floor(unitrand()*nRows*mCols),floor(unitrand()*4)));
}
}
void step(){
occupiedCells=new int[];
int k;
for(int i=0;i<ants.length;++i){
k=ants[i].posInd;
occupiedCells[k]=1;
ants[i].posInd=(k%mCols+dCol[ants[i].antDir])%mCols + (((k#mCols)+dRow[ants[i].antDir])%nRows)*nRows;
ants[i].antDir=nextDir[field[ants[i].posInd]] [ants[i].antDir]; // next direction
}
for(int i=0;i<occupiedCells.keys.length;++i){
k=occupiedCells.keys[i];
field[k]=1-field[k];
}
}
void initSim(){
field=array(nRows*mCols,0); // 0 = white
ants=new Ant[];
placeAnts();
stepNo=0;
}
void savePBMascii(string prefix="antFrame", int maxFrameNo=100000){
string s='P1\n'+string(mCols)+'\n'+string(nRows)+'\n';
for(int i=0;i<fieldSize;++i) s=s+" "+string(field[i]);
string fname=prefix+format("%0"+string(length(string(maxFrameNo)))+"i", stepNo)+".pbm";
write(fname);
file out=output(name=fname);
write(out,s); flush(out); close(out);
}
void go(int steps=1, int saveEvery=1, string prefix="antFrame", int maxFrameNo=100000){
assert(steps>0);
int n=stepNo+steps;
for(int i=stepNo;i<n;++i){
step();
++stepNo;
if(stepNo%saveEvery==0) savePBMascii(prefix, maxFrameNo);
}
}
void operator init(int nRows=200,int mCols=200, int nAnts=1, int seed=-42){
assert(nRows>=minDim && mCols>=minDim && nAnts>0);
this.nRows=nRows; this.mCols=mCols; this.nAnts=nAnts;
if(seed>0) srand(seed);
fieldSize=nRows*mCols;
initSim();
}
}
// // example:
// import langtonants;
// AntField field=AntField(nAnts=42);
// field.go(steps=40000,saveEvery=200,prefix="a",maxFrameNo=40000);
* 编辑 *
第 1000 帧,其中 42 只蚂蚁精美打印(此处为位图片段):
// ant.asy
//
// run
// asy ant.asy
// to get asy.pdf
//
real w=12cm,h=0.618w;
size(w,h);
import langtonants;
void print(AntField field
, guide cellShape=circle(0,1)
, guide antShape=((0,0)--(2,-1)--(2,1)--cycle)
, pen bgPen=white, pen cellPen=black
, pen antPen=red, real cellSize=2){
real[] dirAngle={0,180,90,-90};
pair fieldIdToPair(int posInd){
return ((posInd%field.mCols), field.nRows-1-(posInd#field.mCols))*cellSize;
// return ((posInd%field.mCols),(field.nRows-1-(posInd#field.mCols)) )*cellSize;
}
fill(box((0,0),((field.mCols,field.nRows)*cellSize)),bgPen);
for(int i=0;i<field.nRows;++i){
for(int j=0;j<field.mCols;++j){
if(field.field[(field.nRows-1-i)*field.mCols+j]==1) draw(shift((j,i)*cellSize)*cellShape,cellPen);
}
}
for(int i=0;i<field.ants.length;++i){
fill(shift(fieldIdToPair(field.ants[i].posInd))*rotate(dirAngle[field.ants[i].antDir])*antShape, antPen);
}
}
AntField field=AntField(nAnts=42);
field.go(steps=1000,saveEvery=1000,prefix="x1",maxFrameNo=40000);
print(field,paleyellow,blue+0.1bp,deepred);
* 编辑 * 环面上的简单 3D 图像:
// ant3d.asy
//
real w=12cm,h=0.618w;
size(w,h);
import graph3;
currentprojection=orthographic(
camera=(6.,7.5,6.), up=three.Z, target=(0,0,0), zoom=0.7);
import langtonants;
void print3d(AntField field
, pen bgPen=green+opacity(0.8)
, pen cellPen=blue+2bp
, pen antPen=red+5bp){
real[] dirAngle={0,180,90,-90};
real R=3, r=1;
triple ftorus(pair t) {
return ((R+r*cos(t.y))*cos(t.x),(R+r*cos(t.y))*sin(t.x),r*sin(t.y));
}
triple fieldIdToTriple(int posInd){
return ftorus(
( (field.nRows-1-(posInd#field.mCols))/(field.nRows-1)*2*pi
, (posInd%field.mCols)/(field.mCols-1)*2*pi
)
);
}
surface s=surface(ftorus,(0,0),(2pi,2pi),8,8,Spline);
draw(s,bgPen,render(compression=Low,merge=true));
for(int i=0;i<field.nRows;++i){
for(int j=0;j<field.mCols;++j){
if(field.field[(field.nRows-1-i)*field.mCols+j]==1){
dot(
fieldIdToTriple( (field.nRows-1-i)*field.mCols + j )
, cellPen);
}
}
}
for(int i=0;i<field.ants.length;++i){
dot(fieldIdToTriple(field.ants[i].posInd),antPen);
}
}
AntField field=AntField(nAnts=42);
field.go(steps=1000,saveEvery=10000,prefix="x1",maxFrameNo=40000);
print3d(field,green+opacity(0.8),blue+2bp,red+5bp);