Mandelbrot 并行实现

浅析集及C++实现图形绘制 1.什么是分形? 2.什么是Mandelbrot-Set? 3.算法实现流程 4.实现代码 5.参考文献 阅读详情

最近要交并行计算的作业了,这周终于把作业写了个大概,这期间感觉学了不少东西,总结一下。

Mandelbrot Set 背景

前几天逛维基百科的时候看到了如下的消息:著名数学家、分形之父Benoît B. Mandelbrot(中文名本华·曼德博)美国时间10月15日辞世,享年85岁。

“1979年,在哈佛大学作为访问学者的期间,曼德博开始研究分形集之一——在复平面上一定变换下具有不变性的朱利亚集合。在加斯顿·朱利亚皮埃尔·法图学术成果的基础上,曼德博利用公式 fc(z) = z2 + c 反复迭代,在计算机上作出了朱利亚集合的图形。在研究朱利亚集合的拓扑结构是怎样依赖于复参数μ的同时,他还提出了后来一他的名字命名的曼德博集合(曼德博集合今天常常由 z2 + c 定义,因此曼德博早期以μ为参数所作的图像与后来以 c 为参数所得图像恰如镜面反射般左右对称)。”—From Wikipedia

Wikipedia上Mandelbrot的定义为:

曼德博集合可以用复二次多项式

f_ c(z) =z^{2}+ c /, 来定义

其中c是一个复参数。对于每一个c,从z = 0/,开始对fc(z)进行迭代

序列 (0, f_ c(0), f_c(f_ c(0)), f_ c(f_ c(f_ c(0))), /ldots) 的值或者延伸到无限大,或者只停留在有限半径的圆盘内。

曼德博集合就是使以上序列不延伸至无限大的所有c点的集合。

公式为:image where image

从数学上来讲,曼德博集合是一个复数的集合。一个给定的复数c或者属于曼德博集合M,或者不是。

要记住上面介绍的最后一句话:Mandelbrot Set是一个集合,谁的集合?是关于f_ c(z) =z^{2}+ c /,当中复数c的集合。刚开始我就老搞不明白Mandelbrot Set和Julia Set的区别,其实他们虽然迭代式大体相同,但是所表示的集合范围却是两码事,下面给出Julia Set的定义:

朱利亚集合可以由下式进行反复迭代得到:

fc(z) = z2 + c

对于固定的复数c,取某一z值(如z = z0),可以得到序列

z_0, f_c(z_0), f_c(f_c(z_0)), f_c(f_c(f_c(z_0))), /ldots .

这一序列可能反散于无穷大或始终处于某一范围之内并收敛于某一值。我们将使其不扩散的z值的集合称为朱利亚集合。

这里需要注意的是:这个迭代式当中,复数c的值是固定的,而Julia的集合的范围是关于z值的范围。

简单来说,对于迭代式:fc(z) = z2 + c ,Mandelbrot Set 迭代过程中的z值是固定的,是使上述迭代式始终在某一范围内而不发散于无穷大的c值的集合;而Julia Set的迭代过程中c是固定的,上述是使上述迭代式始终在某一范围内而不发散于无穷大的z值的集合。

理解了这些定义,就很容易通过编程来实现这样的集合。

最后就是关于绘图着色的问题,大部分都是通过让||z||>R的迭代次数来决定所绘制的颜色,我们定义如果在最大迭代次数N内没有超过R的集合的颜色为黑色,当最大的迭代次数N足够大时,可以认为这些点属于Mandelbrot Set,具体要多大?理论上来说当N为无穷大时,我们绘制的Mandelbrot Set才是真的符合定义,但是我们即使借助于计算机,也不可能处理这种无限的问题。所以说我们定义在最大迭代次数N之内z的模不超过R就认为就是属于Mandelbrot Set其实是不严格的,所幸的是如果z不属于Mandelbrot Set的时候,||z||的增长速度是非常的快,再加上我们一般电脑的屏幕分辨率的限制,当N值足够大的时候所显示的图像与N趋于无穷时显示的图像几乎没有区别。所以N值只要取足够大就可以了。还有一个问题就是R的大小的选择,我查阅了相关的资料,也没仔细看,相关的证明公式太多,懒得看,直接上结论:“If the absolute value of Z (that is, its distance from 0+0i) ever gets bigger than 2, it will never return to a place closer than 2, but it will actually rapidly escape to infinity.”也就是说如果z的模值大于2的时候,可以证明经过上述公式的迭代,z的模值会很快趋于无穷大。所以现在基本都是取R=2。

所以,经过上述的一番理论知识,我们用绘图的方法来展示Mandelbrot Set只是一种演示,并不完全符合Mandelbrot Set的定义,但是对于展示这个集合的相关特性,已经足够了。

刚开始我定义迭代次数n>N时,为黑色,属于Mandelbrot Set,对于不属于Mandelbrot Set的,我还是通过迭代次数n来决定其颜色,当n从N到N/2时,颜色从黑变红,当n从N/2到0时,颜色从红到白,结果出现如下后果,十分血腥。。。

然后我到网上搜索了相关的配色,最后在Rocky Mountain College的K. Stuart Smith 教授那里找到了一份配色的方案,他是将最大迭代次数设为256,应该是和256色有关,但是具体的色彩算法我也不是很清楚,不过他应该是用的一种叫HSB的配色方案,他写的函数就是将HSB的颜色转换为RGB的,具体代码如下:

   1: void setHSBColor (float hue, float saturate, float bright) {
   2:   // when I wrote this, openGL only liked colors specified by RGB values. The
   3:   // mandelbrot routine generated an HSB color, so I wrote this routine to do
   4:   // the conversion. It sure isn't perfect, but it does a respectable job.
   5:   //
   6:   // I expect that part of this work (but the final openGL call) could be
   7:   // pushed back with the mandelbrot color generator for more speedup.
   8:   //
   9:   float red, green, blue;    
  10:   float h = (hue * 256) / 60;
  11:   float p = bright * (1 - saturate);
  12:   float q = bright * (1 - saturate * (h - (int)h));
  13:   float t = bright * (1 - saturate * (1 - (h - (int)h)));
  14:  
  15:   switch ((int)h) {
  16:   case 0: 
  17:     red = bright,  green = t,  blue = p;
  18:     break;
  19:   case 1:
  20:     red = q,  green = bright,  blue = p;
  21:     break;
  22:   case 2:
  23:     red = p,  green = bright,  blue = t;
  24:     break;
  25:   case 3:
  26:     red = p,  green = q,  blue = bright;
  27:     break;
  28:   case 4:
  29:     red = t,  green = p,  blue = bright;
  30:     break;
  31:   case 5:
  32:   case 6:
  33:     red = bright,  green = p,  blue = q;
  34:     break;
  35:   }
  36:   glColor3f (red, green, blue);
  37: }
这样,我们连配色方案也有了,核心的算法通过上面的讲述也很清晰了,我的实现如下:
   1: double calcMandelbrotColor(double x,double y)
   2: {
   3:     complex<double> z(0,0);
   4:     complex<double> c(x,y);
   5:     int n=0;
   6:     while(n<256)
   7:     {
   8:         ++n;
   9:         z=z*z+c;
  10:         if(z.real()*z.real()+z.imag()*z.imag()>4) break;
  11:     }
  12:     return (double)n/256.0;
  13: }

这个函数计算出来的是(x,y)点的h值,通过上面的setHSBColor函数中的算法,就可以知道RGB的值,大体代码如下

   1: h=calcMandelbrotColor(x,y);
   2: setHSBColor(h,0.7,1.0-h*h/1.2);//为啥这么用,暂时不知道,应该是和色彩学相关的算法

当然,那个setHSBColor函数中的RGB的值依然是对应于OpenGL中的RGB,每个原色的取值范围为0~1.0,如果用微软的API实现,则要自己转换成0~255,并且自己实现相关的画笔设置功能。

最终的效果如下图:

image

如果放大后,则有惊人的效果:

image 

image

 

image

image

image

image

既然上面已经提到了Julia Set,那么也顺便实现了一下,其实代码和Mandelbrot Set很相似,区别就是在计算颜色的h值那个地方:

   1: double calcJuliaColor(double x,double y)
   2: {
   3:     complex<double> z(x,y);
   4:     complex<double> c(0.109,0.603);
   5:     int n=0;
   6:     while(n<256)
   7:     {
   8:         ++n;
   9:         z=z*z+c;
  10:         if(z.real()*z.real()+z.imag()*z.imag()>4) break;
  11:     }
  12:     return (double)n/256.0;
  13: }

效果如下,c取<0.109,0.603>

image

c取<0.314,0.628>

image

c取<-0.314.-0.628>

image

c取<-0.814,0.17>

image

数学之美啊~

Mandelbrot Set的并行实现:

实现并行的方法有很多,这里先说2种--MPI和pthreads,以后用其他方法实现再来补充。

首先说MPI,这个强大的工具已经被很多超级计算机所采用,我没有那么多电脑。。只好拿自己的电脑进行模拟。其实思路很简单,就是假设有n个处理器,拿出一个进行数据的分配工作,称为Master进程(如果真的有n台计算机的话,每个计算机上都会有这个进程,共n个,我是用MPI的mpiexec进行模拟的,也称其为进程吧,其实与真实的n个处理器还是有差别的),然后剩下的n-1个进程进行数据的操作,称为Slave进程。就用这1个Master进程和n-1个Slave进程进行Mandelbrot Set的计算以及绘图工作,涉及到的通信有:(1)Master将纵坐标切割成n-1个,然后将纵坐标row的信息发送到Slave;(2)每个Slave接受到各自的row的坐标后,然后进行Mandelbrot的颜色的计算,范围为row~row+Height/(n-1),并且将计算的每一个坐标点的颜色值发送给Master;(3)Master接受到Slave发送过来的颜色后,对这个坐标进行绘图工作。

主要代码如下:

   1: //主进程
   2: void calcMandelbrotMaster()
   3: {
   4:     int i,row;
   5:     double pre=0;
   6:     int step=windowY/(nprocs-1);
   7:     int remains=windowY-step*(nprocs-1);
   8:     row=0;
   9:     for(i=1;i<nprocs;++i)
  10:     {
  11:         MPI_Send(&row,1,MPI_INT,i,row_tag,MPI_COMM_WORLD);
  12:         row+=step;
  13:     }
  14:     //暂时认为 (nprocs-1)|windowY
  15:     //if(remains) MPI_Send(&row,1,MPI_INT,nprocs-1,row_tag,MPI_COMM_WORLD);
  16:     for(i=0;i<windowX*windowY;++i)
  17:     {
  18:         MPI_Recv(&currentColor,1,ccolor,MPI_ANY_SOURCE,color_tag,MPI_COMM_WORLD,&status);
  19:         //display(currentColor);
  20:         if(currentColor.c!=pre)
  21:         {
  22:             double b = 1.0 - currentColor.c * currentColor.c / 1.2;
  23:             setHSBColor (currentColor.c, 0.7, b);
  24:         }
  25:         glBegin(GL_POINTS);
  26:         glVertex2i (currentColor.x - windowX/2, windowY/2 - currentColor.y);
  27:         //if(myrank)printf("I am at %d rank/n",myrank);
  28:         glEnd();
  29:         glFlush();
  30:     }
  31: }
   1: //从进程
   2: void calcMandelbrotSlave()
   3: {
   4:     double h,b,pre=0;//pre是前一个颜色,一个小优化
   5:     int row;
   6:     MPI_Recv(&row,1,MPI_INT,0,row_tag,MPI_COMM_WORLD,&status);
   7:     int x,y;
   8:     for(x=0;x<windowX;++x)
   9:         for(y=row;y<min(row+windowY/(nprocs-1),windowY);++y)
  10:         {
  11:             h=calcColor(startX+zoomX*(x),startY+zoomY*(y));//将坐标点传入
  12:             b = 1.0 - h * h / 1.2;
  13:             setHSBColor (h, 0.7, b);
  14:             struct Node cur(x,y,h);
  15:             MPI_Send(&cur,1,ccolor,0,color_tag,MPI_COMM_WORLD);
  16:         }
  17: }

有个问题就是这个程序的绘图使用OpenGL实现,但是绘图完成之后的CPU的使用率依然是100%,不知道怎么解决这个问题,我估计是glutMainLoop()函数的缘故,但是目前不知道怎么解决,暂时先记下来。运行结果如下图:

image

再来说pthreads,这个其实是用多线程来模拟多核,也是一种并行的方法。

其实用pthreads相对简单,关于其原理就不多说了,大体讲一下方法:

其实用pthreads实现关键在于

   1: pthread_create (pthread_t * tid,
   2:                             const pthread_attr_t * attr,
   3:                             void *(*start) (void *),
   4:                             void *arg);

这个函数,第一个参数是pthreads_t类型的数组,也就是你要建立几个线程,第二个参数是相关属性,一般为NULL,关键是第三个和第四个参数,第三个参数是个函数指针,第四个参数是函数指针指向的函数的参数。最重要的一点:要对绘图的操作进行上锁!主要代码如下:

   1: typedef struct Rect
   2: {
   3:     int xs;
   4:     int xe;
   5:     int ys;
   6:     int ye;
   7: }Rect;
   8: typedef struct Node
   9: {
  10:     HDC *hDC;
  11:     Rect *curRect;
  12: }Node;
  13: Rect myRect[9];//将屏幕分隔成9块
  14: Rect cursorRect;
  15:  
  16:  
  17: void *Draw(void *hDCRc)
  18: {
  19:     Rect *curRect=((Node*)hDCRc)->curRect;
  20:     HDC *hDC=((Node*)hDCRc)->hDC;
  21:     int x,y;
  22:     double h,b;
  23:     for(y=curRect->ye;y>=curRect->ys;--y)
  24:     {
  25:         for(x=curRect->xe;x>=curRect->xs;--x)
  26:         {
  27:             pthread_mutex_lock(&drawLock);
  28:             h=calcColor(startX+zoomX*(x),startY+zoomY*(y));//将坐标点传入
  29:             b = 1.0 - h * h / 1.2;
  30:             COLORREF crColor=getHSBColor (h, 0.7, b);
  31:  
  32:             SetPixel(*hDC,x,y,crColor);
  33:             pthread_mutex_unlock(&drawLock); 
  34:         }
  35:     }
  36:     return NULL;
  37: }
  38: void display()
  39: {
  40:     PAINTSTRUCT ps;
  41:     HDC hDC;
  42:     hDC=BeginPaint(hwnd,&ps);
  43:     pthread_t threads[9];
  44:     int i;
  45:     for(i=0;i<9;++i)
  46:     {
  47:         Node *myNode=new Node;
  48:         myNode->curRect=&myRect[i];
  49:         myNode->hDC=&hDC;
  50:         pthread_create(&threads[i],NULL,Draw,myNode);
  51:     }
  52:     for(i=0;i<9;++i)
  53:         pthread_join(threads[i],NULL);
  54:     EndPaint(hwnd,&ps);
  55: }

第一次利用Win32的API进行绘图,遇到了很多问题。比如响应WM_PAINT消息,BeginPaint后一定要EndPaint,要不然会长期占用cpu。暂时记这么多,运行效果如下:

image

比较有成就感,嘿嘿~

4、Rust实现Mandelbrot集绘制:从串行到并行 本文详细介绍了如何使用Rust语言实现Mandelbrot集的绘制,涵盖命令行参数解析、像素到复平面的映射、图像渲染与文件写入等关键步骤。同时,通过并行计算优化程序性能,利用多核处理器显著提升绘制速度,是学习Rust并发编程和数学可视化实践的优秀案例。 阅读详情

相关推荐

4、Rust实现Mandelbrot集绘制:从串行到并行的优化之旅

本文介绍了如何使用Rust实现Mandelbrot集的绘制,涵盖了解析命令行参数、像素坐标到复平面坐标的映射、图像渲染以及图像数据写入PNG文件等关键步骤。同时,通过将图像分割为多个水平带并利用crossbeam库实现多线程并行渲染,显著提升了程序性能。文章还对比了串行与并行版本的执行效率,并总结了Rust在并发编程中的优势和注意事项。

orange的博客 46

集合C语言串行并行代码.zip

集合C语言串行并行代码,使用OpenMP和MPI来应用并行性。

MATLAB矢量化编程:快速生成Mandelbrot

本文还有配套的精品资源,点击获取 简介:MATLAB开发——Mandelbrot集合的矢量化实现 1. Mandelbrot集合简介 1.1 探索复数的奥秘 Mandelbrot集合是一个在复平面上定义的点集,它以数学家Benoit Mandelbrot的名字命名。这个集合通过一个简单却深奥的迭代过程展现复杂性,向我们展示了一个数学上的奇迹。要开始理解...

weixin_35815766的博客 1723

分形理论之集合Mandelbrot set)

(Benoit B. Mandelbrot),数学家、经济学家,分形理论的创始人。1924年生于波兰华沙;1936年随全家移居法国巴黎,在那里经历了动荡的二战时期;1948年在帕萨迪纳获得航空硕士学位;1952年在巴黎大学获得数学博士学位;曾经是普林斯顿、日内瓦、巴黎的访问教授,哈大学的“数学实践讲座”的教授,IBM公司的研究成员和会员。

西京刀客 4110

MATLAB | 分形的艺术——(Mandelbrot)集合

PART.0 Mandelbrot 介绍 “无规则的碎片” “魔鬼的聚合物” “上帝的指纹” Mandelbrot集合有着多种称谓,那么什么是特集?Mandelbrot集合可以用复二次多项式: fc(z)=z2+c f_c(z)=z^2+c fc​(z)=z2+c 来表示,其中c是一个复数。对于每一个c,从z=0,开始对fc(z)f_c(z)fc​(z)进行迭代。序列: (0,fc(0),fc(fc(0)),fc(fc(fc(0))),… ) (0,f_c(0),f_c(f_c(0)),f_c(f_

slandarer的博客 6650

Mandelbrot

一,先堆一下概念: Mandelbrot集合是在复平面上组成分形的点的集合,它正是以数学家Mandelbrot命名。 Mandelbrot集合可以用复二次多项式。从数学上来讲,Mandelbrot集合是一个复数的集合。一个给定的复数c或者属于Mandelbrot集合M,或者不属于。比如,取c = 1,那么这个序列就是(0, 1, 2, 5, 26, …),显然它的值会

wuyongpeng的专栏 2262

并行程序设计报告(MPI并行计算π,实现mandelbrot集)

一.熟悉MPI并行程序设计环境 1.硬件 电脑:HP暗夜精灵 内存:4G 处理器:ntel® Core™ i5-6300HQ CPU @ 2.30GHz × 4 显卡:NVIDIA 960M 2.软件 系统:Ubuntu 16.04LTS MPI版本:MPICH2 二.计算ππ\pi 1.问题描述 已知ππ\pi计算公式:π=∫1041+x2dxπ=∫0141+x2dx ...

silence401的博客 1万+

并行程序设计绘制mandelbrot图片

mandelbrot绘制,使用openmp,mpi来进行绘制和使用不同进程和线程数量进行判断

个人博客地址为http://elikhb.top/ 625

基于OpenMP与OpenCV的Mandelbrot图像并行生成与显示实战

想象你在复平面上随机选一个点 $ c $,然后不断执行这个操作:$$$$初始值是 $ z_0 = 0 $。如果这个序列一直不会“跑掉”(即模长始终有界),那这个点 $ c $ 就属于Mandelbrot 集合;否则就被归为“逃逸区”。听起来很简单对吧?但正是这样一个极简规则,造就了自然界最复杂的图案之一 —— 自相似、无限嵌套、边界永不重复。📌 关键洞察:我们不需要判断是否“收敛”,只需要看它会不会发散。

weixin_29885875的博客 1040

并行计算和OpenGL渲染的Mandelbrot集可视化项目

Mandelbrot集是复动力学中的一个经典分形,由迭代公式z = z^2 + c生成,其中c是复数,z从零开始迭代。集合的边界由那些使得序列不发散到无穷的c组成,具有无限复杂的结构,对数学和计算机图形学均有重要影响。

weixin_26854475的博客 514

【opencv/core module】(九)How to use the OpenCV parallel_for_ to parallelize your code

目录说在前面并行执行的前提测试用例-Mandelbrot setTheory算法流程伪代码串行实现并行实现Result附 说在前面 opencv版本:4.0.1 操作系统:win10 vs版本:2017 官方文档:How to use the OpenCV parallel_for_ to parallelize your code 其他说明:自学,记录,demo 并行执行的前提 支持...

o0o 499

基于MPI实现Mandelbrot Set和Julia Set

文章目录1. Mandelbrot & Julia Set 介绍2. 初步分析2.1 Mandelbrot 集的理论并行性2.2 Mandelbrot 集和 Julia 集的运行逻辑2.3 探究参数C对 Julia 集的形状的影响3. 分区方案设计3.1 基于轮询行分段的分区方案3.2 基于 MPI 的并行计算4. 图形美化设计4.1 着色方案4.2 其他方案5. 结果和讨论6. 总结7. 附录7.1 其他结果展示7.2 源代码附录7.2.1 mandelbrot源码7.2.2 Julia源码7.3

zyw2002的博客 1729

RiverTrail Mandelbrot分形示例深度解析:并行计算的魅力

RiverTrail是一个专注于JavaScript数据并行处理的API,它通过简洁的编程模型让开发者能够轻松利用多核处理器的计算能力。本文将以Mandelbrot分形示例为切入点,带你探索并行计算在复杂图形生成中的强大魅力,展示如何通过RiverTrail实现高效的并行计算任务。 ## 什么是Mandelbrot分形? Mandelbrot分形是数学领域中最著名的分形图形之一,以其无限复杂的

gitblog_00456的博客 853

OpenMP并行化实例----Mandelbrot集合并行化计算

在理想情况下,编译器使用自动并行化能够管理一切事务,使用OpenMP指令的一个优点是将并行性和算法分离,阅读代码时候无需考虑并行化是如何实现的。当然for循环是可以并行化处理的天然材料,满足一些约束的for循环可以方便的使用OpenMP进行傻瓜化的并行。 为了使用自动并行化对Mandelbrot集合进行计算,必须对代码进行内联:书中首次使用自动并...

weixin_34405557的博客 856

独家|OpenCV1.9 如何利用OpenCV的parallel_for_并行化代码(附代码)

翻译:陈之炎 校对:顾伟嵩 本文约3200字,建议阅读7分钟本教程的目标是展示如何使用OpenCV的parallel_for_框架轻松实现代码并行化。 目标本教程的目标是展示如何使用Op...

数据派THU 711

WasmEdge 多线程渲染 Mandelbrot 分形图案实战指南

WasmEdge 多线程渲染 Mandelbrot 分形图案实战指南 【免费下载链接】WasmEdge WasmEdge is a lightweight, high-performance, and extensible WebAssembly runtime for cloud native, edge, and d...

gitblog_00329的博客 475

推荐开源项目:并行Mandelbrot集绘制器

推荐开源项目:并行Mandelbrot集绘制器 该项目是一个巧妙地利用Rust并发特性的应用,用于绘制和导出Mandelbrot集合的PNG图像。通过多线程技术,它将工作负载分布在多个线程中,从而实现更高效的计算。 1、项目介绍 这个程序在single-threaded分支上以单线程模式运行,然后逐渐演变为在多个线程上操作。其中,bands分支将绘图区域划分为八条带,每条由一个独立的线程处理。进一...

gitblog_00005的博客 363

c++伪代码_【2】OpenCV核心模块(9)使用OpenCV parallel_for_并行运行代码

本文是OpenCV核心模块(core module)的最后一节内容。下一部分会更新OpenCV的图像处理模块,基本的图像处理算法都在这个模块中。本文的目标是展示如何用parallel_for_框架快速实现代码的并行运行。下面例程中,并行方法使用几乎100%的CPU资源,绘制一个Mandelbrot集合图像。与单线程相比,速度提升约7倍。如果想了解更多的多线程编程信息,需要阅读相关的参考书或课程,本...

weixin_39668470的博客 670
上一篇: SRM 481 div2
chaoswork
博客等级 码龄17年 9粉丝 4原创
评论 2
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值