我想知道是否有一种(简单的?)方法可以使用 LaTeX 绘制康托函数(魔鬼楼梯)。
使用 TikZ 手动进行绘制似乎有点疯狂,而且我对使用 TikZ 进行绘制的了解也非常有限。话虽如此,我在数学脚本中多次看到过绘制,那么它是如何完成的呢?有人知道吗?
以下是对康托函数。
答案1
我也建议使用外部解决方案。但当然,在 TeX 中也是可行的。只是需要一点时间。
由于这是一个递归解决方案,它可能比非递归解决方案花费更多时间,但递归解决方案相对容易实现。
如果定义了cantor 2 edge/.style={move to}
对角线部分,则不会绘制。(它不是edge
TikZ 路径运算符的一种方式。)
您可以像往常一样使用\draw
您想要的任何选项开始您的路径,然后插入另一个选项:
cantor start={<lower x>}{<upper x>}{<lower y>}{<upper y>}{<level>}
有价值键
/tikz/lower cantor
以及/tikz/upper cantor
/tikz/y cantor
。
我不知道这个y cantor
值有多大意义,所以我把它添加为一个“有趣”的定义。在正确的阶梯定义中y cantor
等于 0.5。(但是,我会使用这样标记的定义。)
代码
\documentclass[tikz]{standalone}
\tikzset{
if/.code n args=3{\pgfmathparse{#1}\ifnum\pgfmathresult=0
\pgfkeysalso{#3}\else\pgfkeysalso{#2}\fi},
lower cantor/.initial=.3333, upper cantor/.initial=.6667, y cantor/.initial=.5,
declare function={
cantor_l(\lowerBound,\upperBound)=
(\pgfkeysvalueof{/tikz/lower\space cantor})*(\upperBound-\lowerBound)+\lowerBound;
cantor_u(\lowerBound,\upperBound)=
(\pgfkeysvalueof{/tikz/upper\space cantor})*(\upperBound-\lowerBound)+\lowerBound;
cantor(\lowerBound,\upperBound)=% fun definition
(\pgfkeysvalueof{/tikz/y\space cantor})*(\upperBound-\lowerBound)+\lowerBound;},
cantor start/.style n args=5{%
insert path={(#1,#3)},
cantor={#1}{#2}{#3}{#4}{#5}{0},
insert path={to[every cantor edge/.try, cantor 1 edge/.try] (#2,#4)}},
cantor/.style n args=6{%
/utils/exec=%
\pgfmathsetmacro\lBx{cantor_l(#1,#2)}%
\pgfmathsetmacro\uBx{cantor_u(#1,#2)}%
% \pgfmathsetmacro\y{.5*(#3+#4)},% proper definition
\pgfmathsetmacro\y{cantor(#3,#4)},% fun
style/.expanded={
if={#6<#5}{cantor={#1}{\lBx}{#3}{\y}{#5}{#6+1}}{},
insert path={
to[every cantor edge/.try, cantor 1 edge/.try] (\lBx,\y)
to[every cantor edge/.try, cantor 2 edge/.try] (\uBx,\y)},
if={#6<#5}{cantor={\uBx}{#2}{\y}{#4}{#5}{#6+1}}{}}}}
\begin{document}
\foreach \level in {0,...,5}{
\begin{tikzpicture}[line join=round] % cantor 1 edge/.style={move to}
\useasboundingbox[draw, scale=6, help lines]
(0,0) grid[xstep=1/9, ystep=.25] (1,1);
\draw[thick, cantor start={0}{6}{0}{6}{\level}{0}];
\end{tikzpicture}}
\foreach \val[evaluate={\lc=1/\val;\uc=(\val-1)/\val;}] in {2,...,9}{
\begin{tikzpicture}[line join=round, lower cantor=\lc, upper cantor=\uc]
% \useasboundingbox[draw, scale=6, help lines]
% (0,0) grid[xstep=\lc*\lc, ystep=.25] (1,1);
\draw[thick, cantor start={0}{6}{0}{6}{6}{0}];
\node [anchor=north west] at (0,6) {$\frac1\val$};
\end{tikzpicture}}
\foreach \val in {1,...,9}{
\begin{tikzpicture}[line join=round, y cantor=.\val, cantor 1 edge/.style={move to}]
\draw[thick, cantor start={0}{6}{0}{6}{6}{0}];
\node[anchor=north west] at (0,6) {$.\val$};
\end{tikzpicture}}
\end{document}
输出
答案2
我很想看到有人直接在 LaTeX 中做到这一点,但与此同时,最简单的方法可能是使用其他方法生成数据并绘制生成的数据文件。
cantor
下面是一个使用以下函数的Python 脚本https://stackoverflow.com/a/17810389/1456857生成数据文件:
def cantor(n):
return [0.] + cant(0., 1., n) + [1.]
def cant(x, y, n):
if n == 0:
return []
new_pts = [2.*x/3. + y/3., x/3. + 2.*y/3.]
return cant(x, new_pts[0], n-1) + new_pts + cant(new_pts[1], y, n-1)
x = np.array(cantor(5))
y = np.cumsum( np.ones(len(x))/(len(x)-2) ) - 1./(len(x)-2)
y[-1] = 1
np.savetxt('cantor.dat', np.vstack([x,y]).T)
cantor.dat
然后可以使用 PGFPlots 绘制该文件:
\documentclass{standalone}
\usepackage{pgfplots}
\begin{document}
\begin{tikzpicture}
\begin{axis}
\addplot [const plot] table {cantor.dat};
\end{axis}
\end{tikzpicture}
\end{document}
答案3
n
这是首次使用 MetaPost 的尝试,可以随意选择迭代次数,本例中为 10 次。
它确实需要改进,但无论如何它都表明它可以完成。使用 LuaLaTeX 进行排版。
编辑第二个是递归实现,更加精致。
vardef cantor(expr A, B, n) =
if n = 0 : A -- B
else:
save C, D; pair C, D;
C = (1/3[xpart A, xpart B], .5[ypart A, ypart B]);
D = (2/3[xpart A, xpart B], ypart C);
cantor(A, C, n-1) -- cantor(D, B, n-1)
fi
enddef;
现在,在后续的应用中,n 等于 15,但我猜测此实现可能会有更多处理。
編輯之二为了更好地呈现,我添加了一个类似于 Qrrbrbirlbel 的网格。
\documentclass[border=2mm]{standalone}
\usepackage{amsmath, luamplib}
\mplibtextextlabel{enable}
\mplibnumbersystem{double}
\begin{document}
\begin{mplibcode}
u := 10cm;
vardef cantor(expr A, B, n) =
if n = 0 : A -- B
else:
save C, D; pair C, D;
C = (1/3[xpart A, xpart B], .5[ypart A, ypart B]);
D = (2/3[xpart A, xpart B], ypart C);
cantor(A, C, n-1) -- cantor(D, B, n-1)
fi
enddef;
beginfig(1);
% Grid and axes
for i = 1 upto 9: draw (i*u/9, 0) -- (i*u/9, u) withcolor .8white; endfor
for j = 1 upto 4: draw (0, j*u/4) -- (u, j*u/4) withcolor .8white; endfor
drawarrow origin -- (1.1u, 0); drawarrow origin -- (0, 1.1u);
% The function
draw cantor(origin, (1, 1), 15) scaled u;
% labels
label.llft("$O$", origin); label.bot("$1$", (u, 0)); label.lft("$1$", (0, u));
label.bot("$x$", (1.1u, 0)); label.lft("$y$", (0, 1.1u));
label.bot("$\dfrac{1}{3}$", (1/3u, 0)); label.bot("$\dfrac{2}{3}$", (2/3u, 0));
label.lft("$\dfrac{1}{2}$", (0, .5u));
endfig;
\end{mplibcode}
\end{document}
答案4
一个Asymptote
MWE
:
// Cantor.asy
settings.tex="pdflatex";
import graph;
real w=8cm,h=w; size(w,h);
import fontsize;defaultpen(fontsize(9pt));
texpreamble("\usepackage{lmodern}");
xaxis(0,1,RightTicks(Step=0.20,step=0.1));
yaxis(0,1,LeftTicks (Step=0.20,step=0.1));
real eps=1e-10;
real fn (real x,int n){
real u;
if(n>0){
if(0 <=x && x<=1/3) u=0.5fn(3x,n-1);
if(1/3 < x && x<=2/3) u=0.5;
if(2/3 < x && x<=1 ) u=0.5*(1+fn(3x-2,n-1));
}else u=x;
return u;
}
real f (real x){
real u, v; int n=1;
u=fn(x,0); v=fn(x,n);
while(abs(u-v)>eps){ ++n; u=v; v=fn(x,n);}
return u;
}
draw(graph(f,0,1,n=1000),darkblue+0.6bp+linejoin(0));
shipout(bbox(Fill(paleyellow)));
// To get Cantor.pdf, run
// asy Cantor.asy
//