分形之曼德勃罗集-创新互联

分形的定义及介绍,知乎、百度上非常多。分形有很多, 其中曼德勃罗集是我上大学时老师讲的一个,当时感觉非常惊奇,一直记得。前几天又突发奇想,看看分形的图片,现在网上真是多。就捯饬了一个。觉得还是很有意思。

成都创新互联云计算的互联网服务提供商,拥有超过13年的服务器租用、资阳托管服务器、云服务器、虚拟空间、网站系统开发经验,已先后获得国家工业和信息化部颁发的互联网数据中心业务许可证。专业提供云主机、虚拟空间、域名注册、VPS主机、云服务器、香港云服务器、免备案服务器等。

下面以曼德勃罗集分形公式,利用C++builder XE8做一个小小的程序。系统是Windows10,64位。


一、曼德勃罗集公式

曼德勃罗特集是一个几何图形, 是曼德勃罗教授在上个世纪七十年代发现的。 这个点集出自迭代公式:

Z_{n+1}=Z^{2}_{n}+C

其中,ZC均为复数。

对于该非线性迭代公式,所有使得无限迭代后的结果能保持有限数值的复数z的集合(也称该迭代函数的Julia集)连通的C,构成曼德勃罗集。


二、曼德勃罗分形迭代公式 

设:Z_{n}=x_{n}+y_{n}i

 C_{n}=c_{n}x+c_{n}yi

可得到:

Z_{n+1}=(x_{n}+y_{n}i)^{2}+(cx_{n}+cy_{n}i) =(x_{n}^{2}-y_{n}^{2}+cx_{n})+(2x_{n}y_{n}+cy_{n})i

即:

x_{n+1}=x_{n}^{2}-y_{n}^{2}+cx_{n}

y_{n+1}=2x_{n}y_{n}+cy_{n}

对每一个连通点的C,利用上述公式进行迭代,并且满足一定的迭代次数或者收敛点。对一系列的连通点集进行迭代,即可得到曼德勃罗集。


三、利用C++builder XE8编程

下面是代码,有详细的说明。

程序文件:

//---------------------------------------------------------------------------

#include#pragma hdrstop

#include "main.h"
//---------------------------------------------------------------------------
#pragma package(smart_init)
#pragma resource "*.dfm"
TMainForm *MainForm;
//---------------------------------------------------------------------------
__fastcall TMainForm::TMainForm(TComponent* Owner)
	: TForm(Owner)
{
}
//---------------------------------------------------------------------------
//创建总体视图时进行初始化。
void __fastcall TMainForm::FormCreate(TObject *Sender)   
{
	MainForm->Width = 560;
	MainForm->Height = 436;
	MainForm->BorderStyle = bsSingle;         //设置为不可调整大小
	MainForm->Color = clSkyBlue;              //设置背景为天蓝色

	FractalImage->Height = 400;               //设置图像高度
	FractalImage->Width = 400;                //设置图像宽度
	FractalImage->Canvas->Pen->Color=clBlack; //设置图像画笔为黑色

	ZoomShape->Brush->Style = bsClear;        //放大框只保留外框线条
	ZoomShape->Visible = false;               //刚创建时,先隐藏放大框

	for(int i=0;i<400;i++)                    //把图片置黑色
	{
		FractalImage->Canvas->MoveTo(i,0);    //移动至第i列
		FractalImage->Canvas->LineTo(i,400);  //竖着画黑色线
	}

	mousedownFlag=false;                      //鼠标标志关闭
	ZoomBut->Enabled=false;                   //没有图的时候,关闭放大功能
}
//---------------------------------------------------------------------------
//分形重置
//左下角为(-2.5,-2.0),宽高为4.0,因此右上角为(1.5,2.0)
void __fastcall TMainForm::ResetButClick(TObject *Sender)
{
	x1=-2.5;                            //最原始的两个坐标。使用这两个数字可以使图形居中。
	y1=-2.0;                            //
	width1=4.0;                         //整个宽度为4。这个是基数。
	ZoomButClick( ResetBut );
}
//---------------------------------------------------------------------------
//用于放大选择的区域
void __fastcall TMainForm::ZoomButClick(TObject *Sender)
{
	int i,j;                        //行,列
	int red,green,blue,times;       //设置各像素点的颜色及迭代次数;
	long double zx,zy,zxs,zys;
	bool inset;                     //置色标志
	FinishFlag=false;               //计算完成置否
	ZoomShape->Visible = false;     //隐藏放大框
	ResetBut->Enabled=false;        //关闭复位功能
	ZoomBut->Enabled=false;         //关闭放大功能
	ExitBut->Enabled=false;         //关闭退出功能

	if( width1 >0 )
	{
		width=width1;
		X0=x1;
		Y0=y1;
		IterationDetail=IterationEdit->Text.ToInt(); //得到迭代次数
		ScaleEdit->Text=4/width;  //放大位数。4为基数宽度。
		for(i=0;i<400;i++)        //核心代码。按列计算每一个像素点。同时将像素点转换的坐标作为迭代公式中的C
		{
			c_Real=X0+((double)i)*width/400.0; //计算公式中常数部分的实部
			for(j=0;j<400;j++)            //计算每一列的每一个像素。自上向下开始计算。
			{
				c_Image=Y0+((double)(400-j))*width/400.0;  //公式中常数部分的虚部
				zx=0;
				zy=0;
				inset=true;
				times=0;                  //对每个像素点的计算都要重新置0。
				while( inset && times< IterationDetail )    //当inset为真,且未达到循环次数时,继续计算
				{
					times++;
					zxs=zx*zx;
					zys=zy*zy;
					zy=2*zx*zy+c_Image;   //迭代后的y
					zx=zxs-zys+c_Real;    //迭代后的X
					if( zxs+zys >= 4.0 )  //如果此点的循环次数未到,有发散的情况,则置为false。
						inset = false;
				}
				if( inset )                   //如果不发散,则置黑色
				{
					FractalImage->Canvas->Pixels[i][j]=TColor RGB( 0,0,0 );   //这种方法能实现。下面两种方法都可以。
				}
				else                           //如果扩散,则用迭代次数times来设置像素点的颜色。
				{
					red=( times+100 )%200+50;  //这几种颜色的数字可以试着修改,会得到不同的颜色。这里的red、green、blue不是真正的红色、绿色和蓝色,而是后面的参数。
					green=( times+red )%200+50;
					blue=( red+green )%200+50;
					FractalImage->Canvas->Pixels[i][j]=TColor RGB(red,green,blue); //转换为TColor。
				}
			}
			Update();              //可以看到画线过程。不使用该方法,则是画完图后,一次性更新图片。
		}
	}
	ResetBut->Enabled=true;         //打开复位关闭功能
	ZoomBut->Enabled=true;          //打开放大功能
	ExitBut->Enabled=true;          //打开退出功能
	FinishFlag=true;
}
//---------------------------------------------------------------------------
//计算鼠标左键按下时的坐标值,并转换为相对图片原点的相对坐标
void __fastcall TMainForm::FractalImageMouseDown(TObject *Sender, TMouseButton Button, TShiftState Shift,
          int X, int Y)
{                                            //图像鼠标坐标值左上角为(0,0),右下角为(400,400)
	ZoomShape->Visible = true;               //显示放大选择框
	if( FinishFlag && Button==mbLeft)
	{
		ZoomShape->Left=FractalImage->Left+X;   //放大选择框的左边为图像的左边+鼠标的横坐标;
		ZoomShape->Top=FractalImage->Top+Y;     //放大选择框的上边为图像的上边+鼠标的纵坐标;
		LtX=X;                                  //记下当鼠标按下时相对图片左上角位置的坐标X
		mousedownFlag=true;                     //记录下按下鼠标的标志
		ltx=X0+((double)X)*width/400.0;         //计算左上角坐标ltx,转换为相对图片原点(中心点0,0)的坐标
		lty=Y0+((double)(400-Y))*width/400.0;   //计算左上角坐标lty,转换为相对原点坐标值。
	}
}
//---------------------------------------------------------------------------
//抬起鼠标左键后,记下鼠标位置。转换为相对图片中心(原点)坐标。
void __fastcall TMainForm::FractalImageMouseUp(TObject *Sender, TMouseButton Button, TShiftState Shift,
		  int X, int Y)                   //计算选中图框后的坐标,并且要保存下来。用于后续的计算。
{
	int Wide;
	if( FinishFlag && Button == mbLeft )  //计算完成并且是按下鼠标左键
	{
		mousedownFlag=false;
		rbx=X0+((double)X)*width/400.0;   //计算相对图片中原点(中心点(0,0)位置的右下角坐标。
		width1=rbx-ltx;                   //计算选框的宽度,这个宽度不是像素宽度,而是坐标宽度。
		rby=lty-width1;                   //选框的高度与宽度一样。
		x1=ltx;
		y1=rby;
		Wide=X-LtX;                     //用鼠标抬起时的坐标-按下鼠标左键的坐标,得到宽度
		ZoomShape->Height=Wide;            //选择框的大小。
		ZoomShape->Width=Wide;             //正方形
	}
}
//---------------------------------------------------------------------------
//没有这个移动的函数也可以,但有了可以画出鼠标移动时的选择框线。其代码与鼠标左键弹起相同。
void __fastcall TMainForm::FractalImageMouseMove(TObject *Sender, TShiftState Shift, int X,
		  int Y)
{
	int Wide;
	if( mousedownFlag )
	{
		rbx=X0+((double)X)*width/400.0;      //得到右下角坐标X。
		width1=rbx-ltx;
		rby=lty-width1;                      //得到右下角坐标y
		x1=ltx;
		y1=rby;
		Wide=X-LtX;                          //宽度:鼠标的坐标X-左上角x坐标
		ZoomShape->Height=Wide; //放大选择框的高度
		ZoomShape->Width=Wide;  //放大选择框的宽度。两个相等,为正文形。
//		ZoomShape->Update();
	}
}
//---------------------------------------------------------------------------
void __fastcall TMainForm::ExitButClick(TObject *Sender)
{
	Close();
}
//---------------------------------------------------------------------------

其中,下面是关键代码。这一段,从左上角开始,对每一个像素点进行迭代计算。

//用于放大选择的区域
void __fastcall TMainForm::ZoomButClick(TObject *Sender)
{
	int i,j;                        //行,列
	int red,green,blue,times;       //设置各像素点的颜色及迭代次数;
	long double zx,zy,zxs,zys;
	bool inset;                     //置色标志
	FinishFlag=false;               //计算完成置否
	ZoomShape->Visible = false;     //隐藏放大框
	ResetBut->Enabled=false;        //关闭复位功能
	ZoomBut->Enabled=false;         //关闭放大功能
	ExitBut->Enabled=false;         //关闭退出功能

	if( width1 >0 )
	{
		width=width1;
		X0=x1;
		Y0=y1;
		IterationDetail=IterationEdit->Text.ToInt(); //得到迭代次数
		ScaleEdit->Text=4/width;  //放大位数。4为基数宽度。
		for(i=0;i<400;i++)        //核心代码。按列计算每一个像素点。同时将像素点转换的坐标作为迭代公式中的C
		{
			c_Real=X0+((double)i)*width/400.0; //计算公式中常数部分的实部
			for(j=0;j<400;j++)            //计算每一列的每一个像素。自上向下开始计算。
			{
				c_Image=Y0+((double)(400-j))*width/400.0;  //公式中常数部分的虚部
				zx=0;
				zy=0;
				inset=true;
				times=0;                  //对每个像素点的计算都要重新置0。
				while( inset && times< IterationDetail )    //当inset为真,且未达到循环次数时,继续计算
				{
					times++;
					zxs=zx*zx;
					zys=zy*zy;
					zy=2*zx*zy+c_Image;   //迭代后的y
					zx=zxs-zys+c_Real;    //迭代后的X
					if( zxs+zys >= 4.0 )  //如果此点的循环次数未到,有发散的情况,则置为false。
						inset = false;
				}
				if( inset )                   //如果不发散,则置黑色
				{
					FractalImage->Canvas->Pixels[i][j]=TColor RGB( 0,0,0 );   //这种方法能实现。下面两种方法都可以。
				}
				else                           //如果扩散,则用迭代次数times来设置像素点的颜色。
				{
					red=( times+100 )%200+50;  //这几种颜色的数字可以试着修改,会得到不同的颜色。这里的red、green、blue不是真正的红色、绿色和蓝色,而是后面的参数。
					green=( times+red )%200+50;
					blue=( red+green )%200+50;
					FractalImage->Canvas->Pixels[i][j]=TColor RGB(red,green,blue); //转换为TColor。
				}
			}
			Update();              //可以看到画线过程。不使用该方法,则是画完图后,一次性更新图片。
		}
	}
	ResetBut->Enabled=true;         //打开复位关闭功能
	ZoomBut->Enabled=true;          //打开放大功能
	ExitBut->Enabled=true;          //打开退出功能
	FinishFlag=true;
}
//---------------------------------------------------------------------------

把像素点的相对坐标(x,y),作为迭代公式中的C值进行迭代计算。

在迭代次数不到1000次(我的迭代次数)且向量值:>=4.0时,将该点置彩色,色彩值与迭代次数相关;

否则,置该点为黑色。具体就是下面这段代码:

		while( inset && times< IterationDetail )    //当inset为真,且未达到循环次数时,继续计算
				{
					times++;
					zxs=zx*zx;
					zys=zy*zy;
					zy=2*zx*zy+c_Image;   //迭代后的y
					zx=zxs-zys+c_Real;    //迭代后的X
					if( zxs+zys >= 4.0 )  //如果此点的循环次数未到,有发散的情况,则置为false。
						inset = false;
				}
				if( inset )                   //如果不发散,则置黑色
				{
					FractalImage->Canvas->Pixels[i][j]=TColor RGB( 0,0,0 );   //这种方法能实现。下面两种方法都可以。
				}
				else                           //如果扩散,则用迭代次数times来设置像素点的颜色。
				{
					red=( times+100 )%200+50;  //这几种颜色的数字可以试着修改,会得到不同的颜色。这里的red、green、blue不是真正的红色、绿色和蓝色,而是后面的参数。
					green=( times+red )%200+50;
					blue=( red+green )%200+50;
					FractalImage->Canvas->Pixels[i][j]=TColor RGB(red,green,blue); //转换为TColor。
				}

其中,

C++builder中,

FractalImage->Canvas->Pixels[i][j]=TColor RGB(red,green,blue);

在RGB(red,green,blue)前加TColor,强制转换为TColor,否则编译会出现warning ,但连接能够通过,程序能够正常运行。  


头文件

//---------------------------------------------------------------------------

#ifndef mainH
#define mainH
//---------------------------------------------------------------------------
#include#include#include#include#include//---------------------------------------------------------------------------
class TMainForm : public TForm
{
__published:	// IDE-managed Components
	TImage *FractalImage; //显示分形图像
	TShape *ZoomShape;    //放大选择框
	TButton *ResetBut;    //复位按钮
	TButton *ZoomBut;     //放大按钮
	TButton *ExitBut;       //显示放大选择框右上角y坐标
	TEdit *IterationEdit; //用于输入迭代次数。迭代次数越大,图像越精细,但计算越慢。太大会非常的慢;超过10亿次报错。
	TEdit *ScaleEdit;     //仅用于显示放大倍数。
	TLabel *IterationLabel;//迭代次数标签
	TLabel *ScaleEditLabel;//放大倍数标签

	void __fastcall FormCreate(TObject *Sender);      //程序初始化部分
	void __fastcall ResetButClick(TObject *Sender);   //分形重置
	void __fastcall ZoomButClick(TObject *Sender);    //放大
	void __fastcall ExitButClick(TObject *Sender);
	void __fastcall FractalImageMouseDown(TObject *Sender, TMouseButton Button, TShiftState Shift,
		  int X, int Y);
	void __fastcall FractalImageMouseUp(TObject *Sender, TMouseButton Button, TShiftState Shift,
		  int X, int Y);
	void __fastcall FractalImageMouseMove(TObject *Sender, TShiftState Shift, int X, int Y);

private:	// User declarations
	bool FinishFlag;            //完成画图标志
	double c_Real, c_Image;     //计算公式中,常数部分的实况与虚部
	double X0,Y0;               //最原始的两个坐标
	double x1,y1;               //选择待放大图像部分的坐标值
	double LtX, LtY;            //记录鼠标按下时的坐标值。实际代码中,未用到ltY
	double ltx,lty, rbx, rby;   //左上角:left top x,left top y,右下角:right bottom x, right bottom y
	double width;               //
	double width1;              //分形的坐标宽度(不是按照像素数量宽度)。
	int IterationDetail;        //迭代次数。越大,越精细,但也越慢。
	bool mousedownFlag;         //鼠标按下标志。
public:		// User declarations
	__fastcall TMainForm(TComponent* Owner);
};
//---------------------------------------------------------------------------
extern PACKAGE TMainForm *MainForm;
//---------------------------------------------------------------------------
#endif
四、部分图片

该程序对计算机的整体配置不是很高,但对于CPU的计算能力有较高的要求,对硬盘和内存要求不高。

我的笔记本是10年前的,Intel(R) Core(TM) i5-4200U CPU @ 1.60GHz   2.30 GHz,迭代次数在100000(十万)时,还勉强能算,再高,就非常慢了。

下面一张都是上面一张图片的局部放大图片。

图1 复位后的曼德勃罗分形图片

图2 图1中局部放大的图片

图3  图2局部放大图片

图4 图3局部放大分形

图5 图4局部放大

图6  图5 的局部放大

图7 图6的局部放大

图8  图7增加迭代次数为10万的迭代结果

图8 与图7 相比,迭代次数增加到10万次。可以看出,细节明显不一样。后面的计算都是按照10万次计算的。图片细节好,但我的笔记本比较慢。 

图9 图8 的局部放大

图10

图11

再放大,就开始马赛克了。 

程序本身不复杂,代码很少。

我已经将该程序源代码、可执行程序压缩为压缩包,上传到资源上。release版本可以拿出来单独运行。有兴趣的可以运行一下试试。 

你是否还在寻找稳定的海外服务器提供商?创新互联www.cdcxhl.cn海外机房具备T级流量清洗系统配攻击溯源,准确流量调度确保服务器高可用性,企业级服务器适合批量采购,新人活动首月15元起,快前往官网查看详情吧


网站题目:分形之曼德勃罗集-创新互联
本文网址:http://pwwzsj.com/article/dpijoh.html