使用 tikz 递归方法构建树

使用 tikz 递归方法构建树

我想用 tikz 翻译用 pst-tree 构建的伯努利树的下一个示例,但我不知道是否可行:

\documentclass{article}
\usepackage{pstricks,pst-tree}

\makeatletter

\newcount\@Bernoudepth
\newcount\@Bernoumaxdepth

\newcommand\Bernoutree[8][treemode=R,nodesep=1ex,levelsep=12ex]{%
  % #2 = depth of tree
  % #3 = name for success
  % #4 = name for miss
  % #5 = probability of a success
  % #6 = position of  #5
  % #7 = probability of a miss 
  % #8 = position of #7
  \begingroup
  % initialize  parameters
  \psset{treemode=R,nodesep=1ex,levelsep=12ex}%
  \psset{#1}%
  \@Bernoumaxdepth #2\relax
  \def\@Reussite{#3}%
  \def\@Echec{#4}%
  \def\@probareussite{#5}%
  \def\@Argreussite{#6}%
  % if no parameter of position
  % center position 
  \ifx\empty\@Argreussite
    \def\@Argreussite{0.5}%
  \fi
  \def\@probaechec{#7}%
  \def\@Argechec{#8}
  \ifx\empty\@Argechec
    \def\@Argechec{0.5}%
  \fi
  % First call  (empty root, level 1)
  \pstree{\TR{}}{\@Bernoutree{1}}
  \endgroup
}
\newcommand\@Bernoutree[1]{%
  % #1 = recursive depth 
  % initialize the depth
  \@Bernoudepth #1\relax
  \ifnum\@Bernoudepth=\@Bernoumaxdepth
    % if depth  max is reached
    % we place the two final nodes
    \TR{\@Reussite}\taput[tpos=\@Argreussite]{\@probareussite}
    \TR{\@Echec}\tbput[tpos=\@Argechec]{\@probaechec}
    % it's finished
  \else
    % else we build with a recursive method
    % the two branches of higher level
    \advance\@Bernoudepth \@ne
    \pstree{\TR{\@Reussite}\taput[tpos=\@Argreussite]
           {\@probareussite}}{\@Bernoutree{\the\@Bernoudepth}}
    \pstree{\TR{\@Echec}\tbput[tpos=\@Argechec]
           {\@probaechec}}{\@Bernoutree{\the\@Bernoudepth}}
  \fi
}
\makeatother

\pagestyle{empty}

\begin{document}

\Bernoutree[levelsep=18ex,treenodesize=0pt]{4}{$R$}{$E$}{$p$}{}{$q$}{}

\end{document}  

实际上,我使用下一个方法得到了树,但它不是递归方法。

\documentclass{scrartcl}
\usepackage{pgf,tikz} 
\usetikzlibrary{trees,arrows} 

\makeatletter
\newcount\tkz@Berndepth
\newdimen\tkz@BernLEN
\tkz@BernLEN=24em 
\def\tkzBernTreeSet#{\pgfqkeys{/berntree}} 

\pgfqkeys{/berntree}{%
  success/.code      = \def\tkz@bern@success{#1},
  miss/.code         = \def\tkz@bern@miss{#1},
  p/.code            = \def\tkz@bern@pbsuccess{#1},
  q/.code            = \def\tkz@bern@pbmiss{#1},
  node success style/.style  = {inner sep=2pt,outer sep=3pt},
  node miss style/.style     = {inner sep=2pt,outer sep=3pt},
  edge style/.style  = {->,>=latex',shorten <= 6pt},
  root style/.style  = {draw,circle},
  success/.initial   = S,
  miss/.initial      = E, 
  p/.initial         = $p$,
  q/.initial         = $1-p$,
  gap/.code          = \def\tkz@bern@gap{#1},
  length/.code       = \def\tkz@bern@length{#1} 
} 

\def\tkz@brntree#1#2{%
  \node[/berntree/root style] {};
\global\advance\tkz@Berndepth 1\relax
\begin{scope}[level distance=\tkz@bern@length,
              level 1/.style={sibling distance=#2}]   
  \node[] (root) at (#1) {}     
  [grow=right]
  child[/berntree/edge style] {%
    node[/berntree/node miss style](tkz@E\the\tkz@Berndepth) {\tkz@bern@miss} 
            edge from parent node[fill=white] {\tkz@bern@pbmiss}}
  child [/berntree/edge style] {%
    node[/berntree/node success style] (tkz@S\the\tkz@Berndepth) {\tkz@bern@success} 
            edge from parent node[fill=white] {\tkz@bern@pbsuccess}
             };
\end{scope}}% 

\def\tkzBernTree{\pgfutil@ifnextchar[{\tkz@BernTree}{\tkz@BernTree[]}}
\def\tkz@BernTree[#1]#2{%
\begingroup  
\pgfqkeys{/berntree}{%
success = S,
miss= E,
node success style/.style  = {inner sep=2pt,outer sep=3pt,draw,minimum width=1.5em,minimum height=1.5em},
node miss style/.style  = {inner sep=2pt,outer sep=3pt,circle,draw,minimum width=1.5em},
p=$p$,
q=$q$,
gap=8cm,
length=3cm} 
\pgfqkeys{/berntree}{#1}
  \tkz@BernLEN=\tkz@bern@gap\relax    
  \tkz@Berndepth 0\relax 
  \node (tkz@S0) at (0,0){}; 
\def\tkz@bn@level{#2} 
 \ifcase\tkz@bn@level%
 \or% 
  \tkz@brntree{tkz@S0}{\tkz@BernLEN} 
 \or% 
   \tkz@brntree{tkz@S0}{\tkz@BernLEN} 
  \divide \tkz@BernLEN by 2 %
 \foreach \nd in {1}{   
   \tkz@brntree{tkz@S\nd}{\tkz@BernLEN}
   \tkz@brntree{tkz@E\nd}{\tkz@BernLEN}}
 \or%
   \tkz@brntree{tkz@S0}{\tkz@BernLEN}  
   \divide \tkz@BernLEN by 2 %
 \foreach \nd in {1}{   
   \tkz@brntree{tkz@S\nd}{\tkz@BernLEN}
   \tkz@brntree{tkz@E\nd}{\tkz@BernLEN}}   
  \divide \tkz@BernLEN by 2 %
 \foreach \nd in {2,3}{   
   \tkz@brntree{tkz@S\nd}{\tkz@BernLEN}
   \tkz@brntree{tkz@E\nd}{\tkz@BernLEN}} 
 \or% 
   \tkz@brntree{tkz@S0}{\tkz@BernLEN}  
   \divide \tkz@BernLEN by 2 %
 \foreach \nd in {1}{   
   \tkz@brntree{tkz@S\nd}{\tkz@BernLEN}
   \tkz@brntree{tkz@E\nd}{\tkz@BernLEN}}   
  \divide \tkz@BernLEN by 2 %
 \foreach \nd in {2,3}{   
   \tkz@brntree{tkz@S\nd}{\tkz@BernLEN}
   \tkz@brntree{tkz@E\nd}{\tkz@BernLEN}} 
  \divide \tkz@BernLEN by 2 %
 \foreach \nd in {4,5,6,7}{   
   \tkz@brntree{tkz@S\nd}{\tkz@BernLEN}
   \tkz@brntree{tkz@E\nd}{\tkz@BernLEN}}    
  \or% 
   \tkz@brntree{tkz@S0}{\tkz@BernLEN}  
   \divide \tkz@BernLEN by 2 %
 \foreach \nd in {1}{   
   \tkz@brntree{tkz@S\nd}{\tkz@BernLEN}
   \tkz@brntree{tkz@E\nd}{\tkz@BernLEN}}   
  \divide \tkz@BernLEN by 2 %
 \foreach \nd in {2,3}{   
   \tkz@brntree{tkz@S\nd}{\tkz@BernLEN}
   \tkz@brntree{tkz@E\nd}{\tkz@BernLEN}} 
  \divide \tkz@BernLEN by 2 %
 \foreach \nd in {4,...,7}{   
   \tkz@brntree{tkz@S\nd}{\tkz@BernLEN}
   \tkz@brntree{tkz@E\nd}{\tkz@BernLEN}} 
   \divide \tkz@BernLEN by 2 %
 \foreach \nd in {8,...,15}{   
   \tkz@brntree{tkz@S\nd}{\tkz@BernLEN}
   \tkz@brntree{tkz@E\nd}{\tkz@BernLEN}}   
 \fi   
 \endgroup
 }
\makeatother
\begin{document}

 \begin{tikzpicture}[yscale=1.2]
 \tkzBernTree[root style/.style  = {fill,circle,outer sep =1pt,inner sep=2pt}]{4}

 \end{tikzpicture} 

\end{document} 

答案1

以下是一个可能的解决方案。我必须将节点创建和边缘标记分开。我也一直在考虑使用 lindenmayer 系统来做到这一点,但失败了。尽情享受吧。(我尽量减少样式设置,专注于问题的核心。)

树的创建并不那么简单,我不得不费尽心机增加和减少 TeX 计数器 \depth。最后,我将其作为节点样式的一部分来完成。它还需要放置一个虚拟坐标,该坐标在从递归返回时增加级别。

标记利用了这样一个事实: tikz 使用标签 (parent-1), (parent-2), ... 来标记父级的子级,其中 (parent) 是父级的标签。

其余部分非常简单。

\documentclass{article}
\usepackage{tikz}      
\usetikzlibrary{calc}  

\newcount\depth

\makeatletter
\newcommand*\bernoulliTree[1]{%
    \depth=#1\relax            
    \draw node(root)[bernoulli/root] {}[grow=right] \draw@bernoulli@tree;
    \draw \label@bernoulli@tree{root};                                   
}                                                                        

\def\draw@bernoulli@tree{%
    \ifnum\depth>0        
      child foreach \type/\label in {left child/$S$,right child/$F$} {%
          node[bernoulli/\type]{\label} \draw@bernoulli@tree
      }
      coordinate[bernoulli/increment] (dummy)
   \fi%
}

\def\label@bernoulli@tree#1{%
    \ifnum\depth>0
      ($(#1)!0.5!(#1-1)$) node[fill=white,bernoulli/decrement] {$p$}
      \label@bernoulli@tree{#1-1}
      ($(#1)!0.5!(#1-2)$) node[fill=white] {$1-p$}
      \label@bernoulli@tree{#1-2}
      coordinate[bernoulli/increment] (dummy)
   \fi%
}

\makeatother

\tikzset{bernoulli/.cd,
         root/.style={circle,fill,black},
         decrement/.code=\global\advance\depth by-1\relax,
         increment/.code=\global\advance\depth by 1\relax,
         left child/.style={draw,bernoulli/decrement},
         right child/.style={draw,circle}}

\begin{document}

\begin{tikzpicture}[level distance=2cm,
                    level 1/.style={sibling distance=4cm},
                    level 2/.style={sibling distance=2cm},
                    level 3/.style={sibling distance=1cm}]
\bernoulliTree{3}
\end{tikzpicture}

\end{document}

示例输出

答案2

Forest 很容易实现自动化。下面提供了一个 Forest 样式,Bernoulli以及一个包装器宏,\bernoullitree[<options>]它接受一个可选参数。现在更常见,并且在 Ti 中无处不在Z 世界,配置采用键值接口。如果采用宏形式,<options>可能包含Bernoulli-style-specific 键和/或 Forest 键和/或 TiZ 键。

给出了一个在树中直接使用该样式的示例和三个使用宏包装器的示例。

\begin{forest}
  Bernoulli,
  tree depth'=3,
  success name=R,
  miss name=E,
  miss probability=q
  []
\end{forest}

这显示了将默认设置(未指定)与某些键的自定义值混合的效果。

\bernoullitree

生成一棵具有默认设置的树(当然,如果需要,可以通过编辑我的默认值或稍后使用 进行更改来全局更改\forestset)。

第三棵树展示了将一系列自定义值传递给宏的效果\bernoullitree

\bernoullitree[tree depth'=3,success name=R, miss name=E, miss probability=q, root style'=draw, miss style=red, success style=blue, miss edge style={red, densely dashed}, success edge style=blue]

最后,最后一个例子表明,如果你需要一棵向南生长的树,而不是一棵向西生长的树,那么生长方向可以很容易地改变。

\bernoullitree[before typesetting nodes={for tree={grow'=-90, tier/.option=level}},miss label=above]

伯努利树的样本选择

完整代码:

\documentclass[border=10pt]{standalone}
\usepackage{forest}
\forestset{
  declare toks register={success name},
  declare toks register={miss name},
  declare toks register={success probability},
  declare toks register={miss probability},
  declare count register={tree depth},
  declare keylist register={success style},
  declare keylist register={miss style},
  declare keylist register={root style},
  declare keylist register={miss edge style},
  declare keylist register={success edge style},
  declare toks register={success label},
  declare toks register={miss label},
  tree depth'=4,
  success name=S,
  miss name=M,
  success probability=p,
  miss probability=1-p,
  success style={circle, draw},
  miss style={circle, draw},
  success edge style={},
  miss edge style={},
  root style={circle, fill, draw},
  success label=above,
  miss label=below,
  bernoulli label/.style={
    edge label/.process={RRw2{#1 probability}{#1 label}{node [midway, sloped, ##2] {$##1$}}},
  },
  Bernoulli/.style={
    delay={
      \forestregister{root style},
      tempcounta'=0,
      do while={
        >RR<{tempcounta}{tree depth}
      }{
        where n children=0{append={[,content/.register=miss name,\forestregister{miss style},edge+/.register=miss edge style,bernoulli label=miss,]},prepend={[,content/.register=success name,\forestregister{success style},edge+/.register=success edge style,bernoulli label=success,]}}{},
        do dynamics,
        tempcounta/.max={>O{level}}{tree},
      }
    },
    before typesetting nodes={
      for tree={
        grow'=0,
        math content,
        l sep'+=15pt,
        s sep'+=5pt,
      },
    },
  },
}
\newcommand\bernoullitree[1][]{\begin{forest}Bernoulli,#1[]\end{forest}}

\begin{document}

\begin{forest}
  Bernoulli,
  tree depth'=3,
  success name=R,
  miss name=E,
  miss probability=q
  []
\end{forest}
\bernoullitree
\bernoullitree[tree depth'=3,success name=R, miss name=E, miss probability=q, root style'=draw, miss style=red, success style=blue, miss edge style={red, densely dashed}, success edge style=blue]
\bernoullitree[before typesetting nodes={for tree={grow'=-90, tier/.option=level}},miss label=above]

\end{document}

答案3

您还可以通过稍微修改 user10274 所做的工作来改进它,这样它也能给出您想要的分支概率:我必须这样做才能在同一个文档中使用它,但使用不同的配置。这给出的输出与 user10274 获得的输出相同:

%command :          \bernoulliTree{#attempts}{p}{1-p} 

%  Bernoulli tree - Schéma de Bernoulli - arbre pondéré

\documentclass{article}
\usepackage{tikz}      
\usetikzlibrary{calc}  

\newcount\depth

\makeatletter
\newcommand*\bernoulliTree[3]{%
\depth=#1\relax            
\draw node(root)[bernoulli/root] {}[grow=right] \draw@bernoulli@tree;
\draw \label@bernoulli@tree{root}{#2}{#3};                           
}                                                                        

\def\draw@bernoulli@tree{%
\ifnum\depth>0        
  child foreach \type/\label in {left child/$S$,right child/$F$} {%
      node[bernoulli/\type]{\label} \draw@bernoulli@tree
  }
  coordinate[bernoulli/increment] (dummy)
   \fi%
}

\def\label@bernoulli@tree#1#2#3{%
\ifnum\depth>0
  ($(#1)!0.5!(#1-1)$) node[fill=white,bernoulli/decrement] {{\small $#2$}}
  \label@bernoulli@tree{#1-1}{#2}{#3}
  ($(#1)!0.5!(#1-2)$) node[fill=white] {{\small $#3$}}
  \label@bernoulli@tree{#1-2}{#2}{#3}
  coordinate[bernoulli/increment] (dummy)
   \fi%
}

\makeatother

\tikzset{bernoulli/.cd,
     root/.style={circle,fill,black},
     decrement/.code=\global\advance\depth by-1\relax,
     increment/.code=\global\advance\depth by 1\relax,
     left child/.style={draw,bernoulli/decrement},
     right child/.style={draw,circle}}

\begin{document}

\begin{tikzpicture}[level distance=2cm,
                level 1/.style={sibling distance=4cm},
                level 2/.style={sibling distance=2cm},
                level 3/.style={sibling distance=1cm}]
\bernoulliTree{3}{p}{1-p}
\end{tikzpicture}

\end{document}

相关内容