绘制不平衡的无限生长树

绘制不平衡的无限生长树

我想画一棵看起来像这样的树(忽略两个框、关于树高和节点颜色的信息,只有树本身):

在此处输入图片描述

我知道我可以手动绘制这棵树。有没有办法让这个过程自动化?我找到了一个类似问题接受的问题接近我想要的。但是,树是完全平衡的,我希望像上图那样,一些节点有子节点,而另一些没有子节点,这种“随机性”是存在的。此外,答案手动添加了带有 的节点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编写输入文件,然后对可执行文件执行系统调用以编译图表。您需要安装这两个开源程序。dotgraphvizdot

请注意,该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时需要该标志。systemasymptote

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");

在此处输入图片描述

相关内容