绘制分形树
1.1题目描述
如下图所示,先垂直绘制一根线段,然后在线段长度的三分之一处和三分之二处分别以固定夹角绘制另外两根线段,长度分别为原线段的2/3和1/3. 如此反复,直至线段长度小于某个较小的值。其中,线条颜色以及长度,夹角都可以自行进行微调。
1.2 问题分析与解决思路
1.2.1 问题分析
问题需要解决如何绘制分形树?可以将绘制过程分解一下,然后去分析。
这是绘制的第一步:
可以发现,绘制这样的图,需要获取原始线段的长度,然后取线段的三分之一及三分之二长度,然后将线段的左右划分为左右部分,先绘制线段左边或右边的线段,再绘制另一边,就可以绘制出来了。
然后继续绘制分支:(可以用三角函数cos,sin来实现定位)
由图可以发现,树杈上的分叉跟上图的是一模一样的,这说明分叉的绘制可以调用绘制上图的绘制过程。不断地调用,直到满足一个什么条件时就停止绘制。
1.2.2解决思路
通过对问题的分析,采用递归思路解决该问题。(也可以用分形算法解决)
我们先考虑画主干,首先画一个垂直的线段,然后选取线段的三分之一位置绘制一根长度为原线段的三分之一的角度为多少的线段,然后再选取线段的三分之二位置绘制一根长度为原线段三分之二的角度为多少的线段(从上往下算的长度,在三分之一位置向左旋转角度,在三分之二位置向右旋转角度)。
然后分支,通过对主干绘制的过程的递归调用来实现。
1.3 模型建立与算法描述
为了简单解决问题,我采用一笔一画的方式,而没有通过起点与终点连接的方式。
记树的初始长度为len,旋转的角度为angle
我们将上述分析过程和解决思路进一步归纳为以下步骤:
(1)绘制主干线段,回到主干位置的1/3位置
(2)向左旋转角度,绘制左分支
(3)重复(1),(2)步,递归绘制长度为主线段的1/3,角度为angle的左分支,直到长度小于某个值,执行下一步
(4)回到初始长度的2/3位置
(5)向右旋转角度,绘制右分支
(6)重复以上步骤,递归绘制长度为主线段的2/3,角度为angle的分支,直到长度小于某个值,执行下一步
(7)转回正方向,回到起点。
算法伪代码描述:
算法 fractalTree(len,angle)
//使用递归算法实现分形树
//输入:分形树的初始长度len,旋转角度angle
//输出:动态实现分形树
int len;
Float angle;
if len < min://min为终止条件
绘制主干;
绘制左分支,位置在len的1/3,长度为len的1/3;
fractalTree(1/3*len,angle);
绘制右分支,位置在len的2/3,长度为len的2/3;
fractalTree(2/3*len,angle)
else:
return
//使用始终位置连接的算法伪代码:
Tree(x,y,L,angle)
//每次更新结点坐标
Int x1,y1 //初始位置坐标
Int x2,y2 //终止位置坐标
Int x2L,y2L//左子树坐标
Int x2R,y2R//右子树坐标
Int L // 初始主干长度
Float angle //旋转角度,可以通过math.pi函数设置,也可以自定义
if L>min://终止条件
x2=x1+(L*math.cos(angle))
y2=y2+(L*math.sin(angle))
Plot(x1,y1)-(x2,y2)//连接初始及终点位置,画主干
//画右子树
x2R=x1+(x2-x1)/3 //右子树起点
y2R=y1+(y2-y1)/3
Tree(x2R,y2R, L* 2 / , (angle - 30))
//画左子树
x2L = x1+ 2 * (x2 - x1) / 3 # 左树枝起点
y2L= y1 + 2 * (y2 - y1) / 3
Tree(x2L, y2L,L* 1 / 3, (angle + 30))
else:
return
1.4 算法实现与复杂度分析
1.4.1 算法实现
算法实现使用的数据结构为树
算法实现的步骤
- 画主干
- 画左子树
- 重复1,2步,直到长度小于某个值
- 画右子树
- 重复以上步骤,直到长度小于某个值
- 结束
1.4.2算法复杂度分析
设T(n)是分形树递归所需的时间,由此T(n)满足以下递推式:
解此递推式可以得出T(n) = O(nlogn)
因此此算法的时间复杂度为O(nlogn)
由于使用了递归法,程序会不断递归,直到满足终止条件为止,所以空间复杂度O(nlogn)。
1.5 程序实现及运行结果分析
角度为60度,长度为360时:
角度为30度,长度为270时:
分析:这是程序运行结果,由turtle的画笔一笔一笔画出来的,运行程序时可以看到绘制过程。通过结果图可以发现每个分支都含有主干结构,这其实就是分形结构的精美之处,局部与整体的相似性(即自相似性)。通过设置终止条件来实现程序递归的结束。