分形的定义及介绍,知乎、百度上非常多。分形有很多, 其中曼德勃罗集是我上大学时老师讲的一个,当时感觉非常惊奇,一直记得。前几天又突发奇想,看看分形的图片,现在网上真是多。就捯饬了一个。觉得还是很有意思。
成都创新互联云计算的互联网服务提供商,拥有超过13年的服务器租用、资阳托管服务器、云服务器、虚拟空间、网站系统开发经验,已先后获得国家工业和信息化部颁发的互联网数据中心业务许可证。专业提供云主机、虚拟空间、域名注册、VPS主机、云服务器、香港云服务器、免备案服务器等。下面以曼德勃罗集分形公式,利用C++builder XE8做一个小小的程序。系统是Windows10,64位。
曼德勃罗特集是一个几何图形, 是曼德勃罗教授在上个世纪七十年代发现的。 这个点集出自迭代公式:
其中,、均为复数。
对于该非线性迭代公式,所有使得无限迭代后的结果能保持有限数值的复数z的集合(也称该迭代函数的Julia集)连通的,构成曼德勃罗集。
设:
可得到:
即:
对每一个连通点的C,利用上述公式进行迭代,并且满足一定的迭代次数或者收敛点。对一系列的连通点集进行迭代,即可得到曼德勃罗集。
下面是代码,有详细的说明。
程序文件:
//---------------------------------------------------------------------------
#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(十万)时,还勉强能算,再高,就非常慢了。
下面一张都是上面一张图片的局部放大图片。
图8 与图7 相比,迭代次数增加到10万次。可以看出,细节明显不一样。后面的计算都是按照10万次计算的。图片细节好,但我的笔记本比较慢。
再放大,就开始马赛克了。
程序本身不复杂,代码很少。
我已经将该程序源代码、可执行程序压缩为压缩包,上传到资源上。release版本可以拿出来单独运行。有兴趣的可以运行一下试试。
你是否还在寻找稳定的海外服务器提供商?创新互联www.cdcxhl.cn海外机房具备T级流量清洗系统配攻击溯源,准确流量调度确保服务器高可用性,企业级服务器适合批量采购,新人活动首月15元起,快前往官网查看详情吧