如何用乳胶制作兰顿蚁?

如何用乳胶制作兰顿蚁?

我想知道是否有可能创建不同的步骤兰顿蚁,一个细胞自动机,在 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 headingypart heading始终看起来像整数,这样我就可以安全地将它们添加到xy索引中。

但有了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翻译有点笨拙,为了提高速度,只需提取彩色单元格并传递到绘图部分。luatikz

\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 步模拟,以pbmascii 格式保存每 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);

相关内容