分形的定義及介紹,知乎、百度上非常多。分形有很多, 其中曼德勃羅集是我上大學時老師講的一個,當時感覺非常驚奇,一直記得。前幾天又突發(fā)奇想,看看分形的圖片,現(xiàn)在網(wǎng)上真是多。就捯飭了一個。覺得還是很有意思。
成都創(chuàng)新互聯(lián)云計算的互聯(lián)網(wǎng)服務提供商,擁有超過13年的服務器租用、資陽托管服務器、云服務器、虛擬空間、網(wǎng)站系統(tǒng)開發(fā)經(jīng)驗,已先后獲得國家工業(yè)和信息化部頒發(fā)的互聯(lián)網(wǎng)數(shù)據(jù)中心業(yè)務許可證。專業(yè)提供云主機、虛擬空間、域名注冊、VPS主機、云服務器、香港云服務器、免備案服務器等。下面以曼德勃羅集分形公式,利用C++builder XE8做一個小小的程序。系統(tǒng)是Windows10,64位。
曼德勃羅特集是一個幾何圖形,?是曼德勃羅教授在上個世紀七十年代發(fā)現(xiàn)的。?這個點集出自迭代公式:
其中,、均為復數(shù)。
對于該非線性迭代公式,所有使得無限迭代后的結(jié)果能保持有限數(shù)值的復數(shù)z的集合(也稱該迭代函數(shù)的Julia集)連通的,構(gòu)成曼德勃羅集。
設(shè):
?
可得到:
即:
對每一個連通點的C,利用上述公式進行迭代,并且滿足一定的迭代次數(shù)或者收斂點。對一系列的連通點集進行迭代,即可得到曼德勃羅集。
下面是代碼,有詳細的說明。
程序文件:
//---------------------------------------------------------------------------
#include#pragma hdrstop
#include "main.h"
//---------------------------------------------------------------------------
#pragma package(smart_init)
#pragma resource "*.dfm"
TMainForm *MainForm;
//---------------------------------------------------------------------------
__fastcall TMainForm::TMainForm(TComponent* Owner)
: TForm(Owner)
{
}
//---------------------------------------------------------------------------
//創(chuàng)建總體視圖時進行初始化。
void __fastcall TMainForm::FormCreate(TObject *Sender)
{
MainForm->Width = 560;
MainForm->Height = 436;
MainForm->BorderStyle = bsSingle; //設(shè)置為不可調(diào)整大小
MainForm->Color = clSkyBlue; //設(shè)置背景為天藍色
FractalImage->Height = 400; //設(shè)置圖像高度
FractalImage->Width = 400; //設(shè)置圖像寬度
FractalImage->Canvas->Pen->Color=clBlack; //設(shè)置圖像畫筆為黑色
ZoomShape->Brush->Style = bsClear; //放大框只保留外框線條
ZoomShape->Visible = false; //剛創(chuàng)建時,先隱藏放大框
for(int i=0;i<400;i++) //把圖片置黑色
{
FractalImage->Canvas->MoveTo(i,0); //移動至第i列
FractalImage->Canvas->LineTo(i,400); //豎著畫黑色線
}
mousedownFlag=false; //鼠標標志關(guān)閉
ZoomBut->Enabled=false; //沒有圖的時候,關(guān)閉放大功能
}
//---------------------------------------------------------------------------
//分形重置
//左下角為(-2.5,-2.0),寬高為4.0,因此右上角為(1.5,2.0)
void __fastcall TMainForm::ResetButClick(TObject *Sender)
{
x1=-2.5; //最原始的兩個坐標。使用這兩個數(shù)字可以使圖形居中。
y1=-2.0; //
width1=4.0; //整個寬度為4。這個是基數(shù)。
ZoomButClick( ResetBut );
}
//---------------------------------------------------------------------------
//用于放大選擇的區(qū)域
void __fastcall TMainForm::ZoomButClick(TObject *Sender)
{
int i,j; //行,列
int red,green,blue,times; //設(shè)置各像素點的顏色及迭代次數(shù);
long double zx,zy,zxs,zys;
bool inset; //置色標志
FinishFlag=false; //計算完成置否
ZoomShape->Visible = false; //隱藏放大框
ResetBut->Enabled=false; //關(guān)閉復位功能
ZoomBut->Enabled=false; //關(guān)閉放大功能
ExitBut->Enabled=false; //關(guān)閉退出功能
if( width1 >0 )
{
width=width1;
X0=x1;
Y0=y1;
IterationDetail=IterationEdit->Text.ToInt(); //得到迭代次數(shù)
ScaleEdit->Text=4/width; //放大位數(shù)。4為基數(shù)寬度。
for(i=0;i<400;i++) //核心代碼。按列計算每一個像素點。同時將像素點轉(zhuǎn)換的坐標作為迭代公式中的C
{
c_Real=X0+((double)i)*width/400.0; //計算公式中常數(shù)部分的實部
for(j=0;j<400;j++) //計算每一列的每一個像素。自上向下開始計算。
{
c_Image=Y0+((double)(400-j))*width/400.0; //公式中常數(shù)部分的虛部
zx=0;
zy=0;
inset=true;
times=0; //對每個像素點的計算都要重新置0。
while( inset && times< IterationDetail ) //當inset為真,且未達到循環(huán)次數(shù)時,繼續(xù)計算
{
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 ) //如果此點的循環(huán)次數(shù)未到,有發(fā)散的情況,則置為false。
inset = false;
}
if( inset ) //如果不發(fā)散,則置黑色
{
FractalImage->Canvas->Pixels[i][j]=TColor RGB( 0,0,0 ); //這種方法能實現(xiàn)。下面兩種方法都可以。
}
else //如果擴散,則用迭代次數(shù)times來設(shè)置像素點的顏色。
{
red=( times+100 )%200+50; //這幾種顏色的數(shù)字可以試著修改,會得到不同的顏色。這里的red、green、blue不是真正的紅色、綠色和藍色,而是后面的參數(shù)。
green=( times+red )%200+50;
blue=( red+green )%200+50;
FractalImage->Canvas->Pixels[i][j]=TColor RGB(red,green,blue); //轉(zhuǎn)換為TColor。
}
}
Update(); //可以看到畫線過程。不使用該方法,則是畫完圖后,一次性更新圖片。
}
}
ResetBut->Enabled=true; //打開復位關(guān)閉功能
ZoomBut->Enabled=true; //打開放大功能
ExitBut->Enabled=true; //打開退出功能
FinishFlag=true;
}
//---------------------------------------------------------------------------
//計算鼠標左鍵按下時的坐標值,并轉(zhuǎn)換為相對圖片原點的相對坐標
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,轉(zhuǎn)換為相對圖片原點(中心點0,0)的坐標
lty=Y0+((double)(400-Y))*width/400.0; //計算左上角坐標lty,轉(zhuǎn)換為相對原點坐標值。
}
}
//---------------------------------------------------------------------------
//抬起鼠標左鍵后,記下鼠標位置。轉(zhuǎn)換為相對圖片中心(原點)坐標。
void __fastcall TMainForm::FractalImageMouseUp(TObject *Sender, TMouseButton Button, TShiftState Shift,
int X, int Y) //計算選中圖框后的坐標,并且要保存下來。用于后續(xù)的計算。
{
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; //正方形
}
}
//---------------------------------------------------------------------------
//沒有這個移動的函數(shù)也可以,但有了可以畫出鼠標移動時的選擇框線。其代碼與鼠標左鍵彈起相同。
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();
}
//---------------------------------------------------------------------------
其中,下面是關(guān)鍵代碼。這一段,從左上角開始,對每一個像素點進行迭代計算。
//用于放大選擇的區(qū)域
void __fastcall TMainForm::ZoomButClick(TObject *Sender)
{
int i,j; //行,列
int red,green,blue,times; //設(shè)置各像素點的顏色及迭代次數(shù);
long double zx,zy,zxs,zys;
bool inset; //置色標志
FinishFlag=false; //計算完成置否
ZoomShape->Visible = false; //隱藏放大框
ResetBut->Enabled=false; //關(guān)閉復位功能
ZoomBut->Enabled=false; //關(guān)閉放大功能
ExitBut->Enabled=false; //關(guān)閉退出功能
if( width1 >0 )
{
width=width1;
X0=x1;
Y0=y1;
IterationDetail=IterationEdit->Text.ToInt(); //得到迭代次數(shù)
ScaleEdit->Text=4/width; //放大位數(shù)。4為基數(shù)寬度。
for(i=0;i<400;i++) //核心代碼。按列計算每一個像素點。同時將像素點轉(zhuǎn)換的坐標作為迭代公式中的C
{
c_Real=X0+((double)i)*width/400.0; //計算公式中常數(shù)部分的實部
for(j=0;j<400;j++) //計算每一列的每一個像素。自上向下開始計算。
{
c_Image=Y0+((double)(400-j))*width/400.0; //公式中常數(shù)部分的虛部
zx=0;
zy=0;
inset=true;
times=0; //對每個像素點的計算都要重新置0。
while( inset && times< IterationDetail ) //當inset為真,且未達到循環(huán)次數(shù)時,繼續(xù)計算
{
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 ) //如果此點的循環(huán)次數(shù)未到,有發(fā)散的情況,則置為false。
inset = false;
}
if( inset ) //如果不發(fā)散,則置黑色
{
FractalImage->Canvas->Pixels[i][j]=TColor RGB( 0,0,0 ); //這種方法能實現(xiàn)。下面兩種方法都可以。
}
else //如果擴散,則用迭代次數(shù)times來設(shè)置像素點的顏色。
{
red=( times+100 )%200+50; //這幾種顏色的數(shù)字可以試著修改,會得到不同的顏色。這里的red、green、blue不是真正的紅色、綠色和藍色,而是后面的參數(shù)。
green=( times+red )%200+50;
blue=( red+green )%200+50;
FractalImage->Canvas->Pixels[i][j]=TColor RGB(red,green,blue); //轉(zhuǎn)換為TColor。
}
}
Update(); //可以看到畫線過程。不使用該方法,則是畫完圖后,一次性更新圖片。
}
}
ResetBut->Enabled=true; //打開復位關(guān)閉功能
ZoomBut->Enabled=true; //打開放大功能
ExitBut->Enabled=true; //打開退出功能
FinishFlag=true;
}
//---------------------------------------------------------------------------
把像素點的相對坐標(x,y),作為迭代公式中的C值進行迭代計算。
在迭代次數(shù)不到1000次(我的迭代次數(shù))且向量值:>=4.0時,將該點置彩色,色彩值與迭代次數(shù)相關(guān);
否則,置該點為黑色。具體就是下面這段代碼:
while( inset && times< IterationDetail ) //當inset為真,且未達到循環(huán)次數(shù)時,繼續(xù)計算
{
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 ) //如果此點的循環(huán)次數(shù)未到,有發(fā)散的情況,則置為false。
inset = false;
}
if( inset ) //如果不發(fā)散,則置黑色
{
FractalImage->Canvas->Pixels[i][j]=TColor RGB( 0,0,0 ); //這種方法能實現(xiàn)。下面兩種方法都可以。
}
else //如果擴散,則用迭代次數(shù)times來設(shè)置像素點的顏色。
{
red=( times+100 )%200+50; //這幾種顏色的數(shù)字可以試著修改,會得到不同的顏色。這里的red、green、blue不是真正的紅色、綠色和藍色,而是后面的參數(shù)。
green=( times+red )%200+50;
blue=( red+green )%200+50;
FractalImage->Canvas->Pixels[i][j]=TColor RGB(red,green,blue); //轉(zhuǎn)換為TColor。
}
其中,
C++builder中,
FractalImage->Canvas->Pixels[i][j]=TColor RGB(red,green,blue);
在RGB(red,green,blue)前加TColor,強制轉(zhuǎn)換為TColor,否則編譯會出現(xiàn)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; //用于輸入迭代次數(shù)。迭代次數(shù)越大,圖像越精細,但計算越慢。太大會非常的慢;超過10億次報錯。
TEdit *ScaleEdit; //僅用于顯示放大倍數(shù)。
TLabel *IterationLabel;//迭代次數(shù)標簽
TLabel *ScaleEditLabel;//放大倍數(shù)標簽
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; //計算公式中,常數(shù)部分的實況與虛部
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; //分形的坐標寬度(不是按照像素數(shù)量寬度)。
int IterationDetail; //迭代次數(shù)。越大,越精細,但也越慢。
bool mousedownFlag; //鼠標按下標志。
public: // User declarations
__fastcall TMainForm(TComponent* Owner);
};
//---------------------------------------------------------------------------
extern PACKAGE TMainForm *MainForm;
//---------------------------------------------------------------------------
#endif
四、部分圖片該程序?qū)τ嬎銠C的整體配置不是很高,但對于CPU的計算能力有較高的要求,對硬盤和內(nèi)存要求不高。
我的筆記本是10年前的,Intel(R) Core(TM) i5-4200U CPU @ 1.60GHz ? 2.30 GHz,迭代次數(shù)在100000(十萬)時,還勉強能算,再高,就非常慢了。
下面一張都是上面一張圖片的局部放大圖片。
圖8 與圖7 相比,迭代次數(shù)增加到10萬次??梢钥闯觯毠?jié)明顯不一樣。后面的計算都是按照10萬次計算的。圖片細節(jié)好,但我的筆記本比較慢。?
再放大,就開始馬賽克了。?
程序本身不復雜,代碼很少。
我已經(jīng)將該程序源代碼、可執(zhí)行程序壓縮為壓縮包,上傳到資源上。release版本可以拿出來單獨運行。有興趣的可以運行一下試試。?
你是否還在尋找穩(wěn)定的海外服務器提供商?創(chuàng)新互聯(lián)www.cdcxhl.cn海外機房具備T級流量清洗系統(tǒng)配攻擊溯源,準確流量調(diào)度確保服務器高可用性,企業(yè)級服務器適合批量采購,新人活動首月15元起,快前往官網(wǎng)查看詳情吧