2012年2月3日,星期五

迟到总比没有好:Mandelbrot Set


尽管我有数学和计算机科学背景,但这(上面)是我第一次尝试画出 曼德布罗集。由于绘制该集合是一个行之有效的计算机数学项目,因此几乎是陈词滥调了(关于它的在线信息很多,但是 这篇数学蒙克文章 有一些不错的指针)。但是,如果我终于可以解决这个问题,那么对您来说还为时不晚。

因此,如果您还没有编写任何程序来绘制集合,则强烈建议您使用。进行探索时,有很多美好的事情要考虑,而且与分形几乎没有关系-尽管 那奇怪的墨迹样图像 最后出现的是胡萝卜,或者 菜花,这将激励您前进。

我将在下面分享我的小程序和说明。如果尝试的话,您可以找到或创建更流畅,更高效的代码。例如,您实际上并不需要知道复杂点的大小(大小的平方就足够了),而且我确定我的点生成不是最好的。

你在画什么
我猜想大多数参加预计算的学生倾向于考虑仅根据实数的单变量函数来绘制点。这不是那种情节之一。代替 (x,y)表示 y = (x),我们正在绘制属于 C,如果它们属于特定组,则将它们制成一种颜色 M,如果没有,则使用其他颜色。

弄清楚一个点是否在 M,对于您考虑的每个点,您都需要构建一个特定的序列-如果该序列保持有界,则它位于 M,但是如果序列是无界的,则不在 M。您越计算序列,就越能确定自己的分数确实在集合中。

一些复数的东西
您不需要编写太多代码来实现所需的复数运算,但是我倾向于将这种事情封装在这样的通用类中 加工 example:

CPoint类{
 float xvalue;
 float yvalue;
 浮点数= 0.2;
 CPoint(float x,float y){
  xvalue = x;
  yvalue = y;
 } 
 CPoint sum(CPoint p){ 
 返回新的CPoint(xvalue + p.xvalue,yvalue + p.yvalue);
 }
 CPoint产品(CPoint p){
 float newx = xvalue *(p.xvalue)-yvalue *(p.yvalue);
 float newy = xvalue *(p.yvalue)+ yvalue *(p.xvalue); 
 返回新的CPoint(newx,newy);
 }
 CPoint squared(){
   返回this.prod(this);
 } 
 float magnit(){
   浮动部分= pow(xvalue,2)+ pow(yvalue,2);
   return sqrt(part);
 }
 无效显示(浮动xshift,浮动yshift,浮动缩放){
    fill(255,255);
   椭圆(xvalue * zoom + xshift,yvalue * zoom + yshift,点大小,点大小);
  }  
}

关键是复数乘法-这就是区别点 C from one in R^2.

我与处理有喜欢/不喜欢的关系-我喜欢事物可以多快地创建并且它使 无需付出太多努力即可获得漂亮的图片,但是我不喜欢使用它似乎无法帮助打破基本的编程规则(我最终使用了全局变量,并且总是打破了模型与视图的分离)-可能是个人问题,而​​不是问题与处理本身。

确定点是否在集合M中
您将根据特殊规则找到一系列点-如果反复应用特殊规则会导致这些点变得太大,那么它们将超出范围。除了了解特殊规则外,您还需要知道“大小大于太大”和“我将应用该规则多少次”。

该计算封装在此其他Processing类中 地图。您能找出规则是什么,以及如何指定计算的“多少”和“多少”部分吗?

类图{
 CPoint c;
 CPoint first;
 CPoint current;

 地图(CPoint初始值,CPoint cValue){
   first = initial;
   current = initial;
   c = cValue;
  }
    
  void iterate(){
   当前= current.squared()。sum(c);
  }
  
 布尔迭代(int迭代,int绑定){   
    for(int i=0; i< iterations; i++){
      this.iterate();    
      if(current.magnit()>bound) break;
    }
   返回(current.magnit()< bound);
  }
  
 无效显示(浮动xshift,浮动yshift,浮动缩放){
   c.display(xshift,yshift,zoom);
  }    
}

如果您仅计算几次迭代,那么一些不应该包含在集合中的点将被包括在内-经过5次迭代后,您将获得类似于 滑冰.


与通过多次迭代获得的更清晰的图像相比,具有稍微不太精确的图可以使您的图片看起来更时髦。由于这个原因,人们经常在他们的Mandelbrot集的图片中包含不完全集中的点,并且经常使用颜色来显示给定点在哪个迭代中未通过隶属度测试。这些模糊的图片确实是很好的例子 模糊集 -在其中不是值的组成员是true或false的值范围,它指示该点在实际集中失败的级别。下图是使用20次迭代生成的。



绘制集合
您需要做的最后一件事是设置窗口并生成点。这是我用于此操作的主要处理文件(此操作中没有缩放或平移-留给读者练习):

//各种魔术数字
int windowX = 600;
int windowY = 500;
int迭代= 200;
int zoom = 250;
int磁盘= 2;
int numberPoints = 100000;
int randomRange = 100;
浮动中心X = -0.5;
浮点中心Y = 0;

//在里面
void setup(){
 大小(windowX,windowY);
  noStroke();
  smooth();
  background(0);
}

无效draw() 
{
  loop();
 CPoint零=新的CPoint(0,0);
  float x;
  float y;
  int signx = -1;
  int signy = -1;
  Map newMap;
  CPoint cPoint;  
  for( int i=0; i < numberPoints; i++){
   cPoint = randomCPoint(1.5,randomRange);
   newMap =新地图(零,cPoint);
   if(newMap.iterate(迭代次数,磁盘)){
       newMap.display(windowX / 2-centerX * zoom,windowY / 2-centerY * zoom,zoom);
    }  
  }  
}

CPoint randomCPoint(浮动界限,浮动深度){
  float x;
  float y;
  int signx = -1;
  int signy = -1;
 x =边界*随机(深度)/深度;
 y =边界*随机(深度)/深度;
  if(random(100)<50) signx *= (-1);
  if(random(100)<50) signy *= (-1);
 返回新的CPoint(signx * x,signy * y); 
 }

如果以这种方式实现,则随着测试的点越来越多,该集合将慢慢出现。下面的更清晰的版本使用了200次迭代(可能有点过多)。



更新:此内容的Javascript版本已发布 这里.