#edu019. 第 5 节 递归迭代绘图

第 5 节 递归迭代绘图

在前面的例子中“绘制 nn 个正 nn 边形”,如果有一个代码模块能够帮助我们绘制一个正 nn 边形,那么“绘制 nn 个正 nn 边形”的代码会很简洁,这个代码模块就是函数,一些常用的功能,前人已经编写了函数并封装到不同的库中(头文件),如果用户想自己做一个自己需要的函数,那可以自己定义函数,然后再调用。

定义函数基本格式如下:

函数返回值类型 函数名([形式参数列表])

{

功能语句;

...

[return 返回值]

}

注意:定义函数必须放到主函数外面,上述定义函数框架中 [...]为可以选项。

以“绘制 nn 个正 nn 边形”为例,利用自定义函数,参考代码如下:

void draw(int m)                //定义一个函数,函数名draw ,绘制一个正m边形
{
	for(int i=1;i<=m;i++)
		pen.fd(1000.0/m).rt(360.0/m);
}
int main()
{
    int n;
	cin>>n;
	for(int i=1;i<=n;i++)
	{
		draw(n);                //调用函数,draw为自定义函数,功能是绘制一个正n边形 
		pen.rt(360.0/n);
	}
    return 0;
}

程序运行时,每次调用 drawdraw 函数,会将实参 nn 单向复制给形参 mm,自定义的函数 drawdraw,就负责绘制一个正 mm 边形,从而使得代码逻辑更加清晰,大型程序要实现多种功能,都会将复杂任务分解成很多小任务,然后交给不同函数执行,便于维护,关于详细函数使用细节可以查看后面系统对应章节。

主函数可以调用子函数,子函数之间也可以相互调用,当一个子函数直接或间接调用自己,这就是递归,也被叫做迭代。如果一个问题可以分解成一个或多个子问题,子问题又与原问题具有相似的结构,那就可以用递归来处理(自己调用自己),当然不能无限递归下去,到达一定程度需要返回,否则时间和空间都可能爆炸(递归使用栈空间,每递归一层,就会扩大一层的空间)。

自然界会发现大量整体与部分具有相似性的形态,例如雪花、树冠、花菜、大脑皮层、粒子的布朗运动等,数学上称之为分形,是非线性科学前言和重要分支。瑞典数学家柯赫构造的 “Koch曲线”、波兰数学家提出的“谢尔宾斯基三角形”都是典型的分形图,分形图由于整体与部分具有相似性,这与递归在本质上是相通的,那么就可以利用递归绘制出各种分形图来。

瑞典数学家海里格·冯·科赫在 1904 构造的 “Koch曲线”,也叫雪花曲线。三次 Koch 曲线的构造过程主要分为三大步骤:

11 步:绘制一条线段;第 22 步:将这条线段中间的 1/31/3 处向外折起(内角为 6060 度);第 33 步:按照第 22 步的方法不断的把各段线段中间的 1/31/3 处向外折起。这样无限的进行下去,最终即可构造出 Koch 曲线。下图就是迭代了 44 次得到的“ Koch 曲线”:

Koch曲线

迭代 4 次“Koch曲线”

三角形三边按照上述方法迭代下去,就构成了"科赫雪花",Koch 曲线长度是无限的,"科赫雪花"面积有限,这就得到了数学上的一个悖论:"无穷大"的边界,包围着有限的面积。

例1:请利用计算机绘制"科赫雪花"。

分析:"科赫雪花"是由一个三角形构成,各边是 Koch 曲线,Koch 曲线可以用用递归生成,当长度小于 33,返回。

void draw(double length)  //绘制Koch曲线
{
	if(length<5)
	{
		pen.fd(length);
		return;
	}
	draw(length/3);
	pen.lt(60);
	draw(length/3);
	pen.rt(120);
	draw(length/3);
	pen.lt(60);
	draw(length/3);	
}
int main()
{
	pen.move(-100,-100);
	pen.rt(30); 
	for(int i=1;i<=3;i++)   //绘制三角形
	{
		draw(300);      //三角形各边为Koch曲线
		pen.rt(120);
	}
    return 0;
}

可以得到科赫雪花图像如下:

科赫雪花图像

科赫雪花图像

例2:绘制“谢尔宾斯基三角形” 分形

谢尔宾斯基三角形(英语:Sierpinski triangle)是一种分形,由波兰数学家谢尔宾斯基在 1915 年提出。其构造方法比较多,其中一种构造过程如下:

11 步.取一个三角形,使用等边三角形;第 22 步沿三边中点的连线,将它分成四个小三角形。第 33 步.去掉中间的那一个小三角形。

对其余三个小三角形重复上述 33 个过程,如下图:

谢尔宾斯基三角形

谢尔宾斯基三角形

分析:观察上图中前两步,找到一种简单的规律,先绘制一个小三角形(边长为大三角形边长的一半),画笔必然回到原位置,让画笔往前移动(大三角形边长),然后画笔再向右旋转 120 度,后面都是重复这个过程,重复 33 次,采用递归绘制每一个小三角形,当边长小于一定值(再绘制也看不到细节了),返回。

void draw(double length)
{
	if(length<3)return;
	for(int i=1;i<=3;i++)
	{
		draw(length/2);
		pen.fd(length);
		pen.lt(120);
	}
}

int main()
{
    pen.move(-100,-100);
	pen.rt(90);
	draw(300);	
    return 0;
}

可以得到谢尔宾斯基三角形:

谢尔宾斯基三角形

谢尔宾斯基三角形

拓展:绘制其他分形图。