我想画一棵看起来像这样的树(忽略两个框、关于树高和节点颜色的信息,只有树本身):
我知道我可以手动绘制这棵树。有没有办法让这个过程自动化?我找到了一个类似问题接受的问题接近我想要的。但是,树是完全平衡的,我希望像上图那样,一些节点有子节点,而另一些没有子节点,这种“随机性”是存在的。此外,答案手动添加了带有 的节点for
。
这是从答案中获取的玩具代码:
\documentclass{article}
\usepackage[utf8]{inputenc}
\usepackage[linguistics]{forest}
\usepackage{graphicx}
\begin{document}
\resizebox{\linewidth}{!}{%
\begin{forest}
symbol/.style={
draw=black,
text height=1.5ex,
text depth=.25ex,
},
operation/.style={
symbol,
rounded rectangle,
},
[{}, operation,
repeat=2{
append={
[{},operation,repeat=2{
append={[{}, operation]}
}]
},
},
before typesetting nodes={
for children={
for children={
repeat=2{
append={
[{}, operation,repeat=2{
append={[\dots]}
}]
},
},
}
}
}
]
\end{forest}
}
\end{document}
由此得出这棵树:
第一张图片中的树可以在 Latex 中自动生成吗?我并不是在寻找它的精确复制品,而是在不平衡、随机的外观上与它相似。
答案1
这是一个有趣的问题。我决定看看我能用什么渐近线和图形可视化程式。
以下代码将用语言( 的一部分)asymptote
编写输入文件,然后对可执行文件执行系统调用以编译图表。您需要安装这两个开源程序。dot
graphviz
dot
请注意,该defineChildren
函数会递归调用,直到达到所需的图形级别。
//srand(seconds());
int levels = 15;
int getWeightedRandom(int thisLevel) {
int i = floor(0.5 + unitrand() * 25 - thisLevel/2.0);
if (i < 4) return 0;
if (i < 9) return 1;
if (i < 14) return 2;
if (i < 18) return 3;
if (i < 22) return 4;
return 5;
}
file fout=output("sample.dot");
void defineChildren(string parentName) {
int thisLevel = length(parentName);
if (thisLevel >= levels) { return; }
for (int i = 1; i <= getWeightedRandom(thisLevel); ++i) {
string childName = parentName + string(i);
write(fout, parentName + " -> " + childName + ";", endl);
defineChildren(childName);
}
}
write(fout, "digraph G {", endl);
write(fout, "node [shape=circle, label=\"\"];", endl);
write(fout, "edge [arrowhead=none];", endl);
defineChildren("0");
write(fout, "}", endl);
system("dot -Tpdf sample.dot -o sample.pdf");
您可以在 Windows 批处理文件中使用以下命令调用此渐近线代码。使用代码末尾的调用-nosafe
时需要该标志。system
asymptote
asy -nosafe sample.asy
如果取消注释第一行,则随机数生成器将使用计算机时钟进行播种,并且每次处理代码时图表都会有所不同。有时这会导致空白输出,因为第一个节点没有子节点。
我尝试使用这个getWeightedRandom
函数来制作一个吸引人的图表。我将图表的级别(行)合并到随机数函数中,因为否则图表的底部会变得非常宽。
编辑根据 OP 评论在底部添加虚线:
修改以下代码,使树的最低层具有虚线边缘。此外,底部一行节点具有白色边框,以使其不可见。
//srand(seconds());
int levels = 15;
int getWeightedRandom(int thisLevel)
{
int i = floor(0.5 + unitrand() * 25 - thisLevel/2.0);
if (i < 4) return 0;
if (i < 9) return 1;
if (i < 14) return 2;
if (i < 18) return 3;
if (i < 22) return 4;
return 5;
}
file fout=output("sample.dot");
void defineChildren(string parentName)
{
int thisLevel = length(parentName);
if (thisLevel >= levels) { return; }
for (int i = 1; i <= getWeightedRandom(thisLevel); ++i)
{
if (thisLevel == levels - 1)
{
write(fout, "edge [style=dashed];", endl);
write(fout, "node [color=white];", endl);
}
else
{
write(fout, "edge [style=solid];", endl);
write(fout, "node [color=black];", endl);
}
string childName = parentName + string(i);
write(fout, parentName + " -> " + childName + ";", endl);
defineChildren(childName);
}
}
write(fout, "digraph G {", endl);
write(fout, "node [shape=circle, label=\"\"];", endl);
write(fout, "edge [arrowhead=none];", endl);
defineChildren("0");
write(fout, "}", endl);
system("dot -Tpdf sample.dot -o sample.pdf");