如何编写分形程序?

82

我没有编写分形程序的经验。当然,我看过著名的Mandelbrot图像等。

你能提供一些简单的分形算法吗?

编程语言并不重要,但我最熟悉的是ActionScript、C#和Java。

我知道如果我在谷歌上搜索分形,会得到很多(复杂)信息,但我想从一个简单的算法开始,并进行尝试。

欢迎提出改进基本算法的建议,例如如何制作那些可爱的颜色等。


6
根据这个问题的答案,我在Github上创建了一个Gist,使用Canvas元素和JavaScript制作了动画曼德博集合。 https://gist.github.com/1853604 - Sander Versluys
12个回答

61

编写曼德博集合程序很容易。
以下是我的快速代码(不能保证无错误,但是是一个好的概述)。

这是概述: 曼德博集合完全位于半径为2的圆内的复数格中。

因此,从扫描该矩形区域中的每个点开始。 每个点代表一个复数(x + yi)。 迭代该复数:

[新值] = [旧值]² + [原始值] 同时跟踪两件事:

1.) 迭代次数

2.) [新值] 距离原点的距离。

如果达到最大迭代次数,则结束。 如果距离原点的距离大于2,则结束。

完成后,根据您完成的迭代次数对原始像素进行着色。 然后进入下一个像素。

public void MBrot()
{
    float epsilon = 0.0001; // The step size across the X and Y axis
    float x;
    float y;
    int maxIterations = 10; // increasing this will give you a more detailed fractal
    int maxColors = 256; // Change as appropriate for your display.

    Complex Z;
    Complex C;
    int iterations;
    for(x=-2; x<=2; x+= epsilon)
    {
        for(y=-2; y<=2; y+= epsilon)
        {
            iterations = 0;
            C = new Complex(x, y);
            Z = new Complex(0,0);
            while(Complex.Abs(Z) < 2 && iterations < maxIterations)
            {
                Z = Z*Z + C;
                iterations++;
            }
            Screen.Plot(x,y, iterations % maxColors); //depending on the number of iterations, color a pixel.
        }
    }
}

有一些细节没有提到:

1.) 学习复数的平方是什么以及如何计算。

2.) 找出如何将矩形区域 (-2,2) 转换为屏幕坐标。


30

你确实应该从曼德博集合开始,并理解它是什么。

它背后的思想相对简单。你从一个关于复变量的函数开始:

f(z) = z2 + C

其中 z 是一个复数 变量,C 是一个复数 常数。现在你从 z=0 开始迭代计算,也就是计算 z1 = f(0), z2 = f(z1), z3 = f(z2) 等等。对于那些序列 z1, z2, z3, ... 是 有界的,也就是不会无限增长的常数 C 组成的集合,称为曼德博集合(即维基百科页面上黑色的集合)。

在实践中,要画出曼德博集合,你需要:

  • 选择复平面上的一个矩形(比如说,从-2-2i到2+2i的点)。
  • 用适当的矩形网格(比如说400x400的点)覆盖这个矩形,网格上的每个点会被映射到你屏幕上的像素。
  • 对于每个点/像素,让 C 等于该点,计算相应的迭代序列 z1, z2, z3,... 的20个术语,并检查它是否“趋向于无穷”。在实践中,你可以检查,在迭代过程中,如果一个术语的绝对值大于2,则表示“趋向于无穷”。如果某个 z_k 趋向于无穷,则该序列“趋向于无穷”;否则,你可以将其视为有界。
  • 如果与某一点C相应的序列是有界的,则在图像上用黑色绘制相应的像素(因为它属于曼德博集合)。否则,用另一种颜色绘制。如果您想玩得开心并且绘制漂亮的图形,请根据abs(第20项)的大小使用不同的颜色进行绘制。
  • 分形的惊人之处在于我们如何从简单而表面无害的要求中获得一个极其复杂的集合(特别是曼德博集合的“边缘”)。

    享受吧!


    10
    如果复数让你头疼,那么有很多分形可以使用L系统进行公式化。这需要几个交互层,但每个层都是有趣的。

    首先,你需要一只海龟。前进、后退、左转、右转、笔起、笔落。即使没有L系统的驱动,使用海龟图形和海龟几何学也可以制作出许多有趣的形状。搜索“LOGO图形”或“海龟图形”。完整的LOGO系统实际上是一个使用未括号化的Cambridge Polish语法的Lisp编程环境。但你不必走得那么远,就可以使用海龟概念获得一些漂亮的图片。

    然后,您需要一个层来执行L系统。 L系统与后缀系统Semi-Thue系统相关,并且像病毒一样跨越图灵完备性的边界。这个概念是字符串重写。它可以作为宏展开或过程集实现,具有额外的控件以限制递归。如果使用宏扩展(如下面的示例),仍需要一个过程集将符号映射到海龟命令,并且需要一个过程来迭代字符串或数组以运行编码的海龟程序。对于有限递归过程集(例如),您可以在过程中嵌入海龟命令,并向每个过程添加递归级别检查,或将其分解为处理程序函数。
    这是一个使用宏展开和非常简略的海龟命令绘制的Postscript版勾股树示例。有关Python和Mathematica的示例,请参见我的code golf challenge

    ps l-system pythagoras tree luser-droog


    该图像是使用此处描述的技术创建的,将一小段代码与一个小插图结合在一起。 - luser droog

    8

    有一本名为《混沌与分形》的好书,每章末尾都有简单的示例代码,实现了某些分形或其他示例。很久以前我读这本书时,将每个示例程序(用某种Basic方言编写)转换成了在网页上运行的Java小程序。这些小程序在此处:http://hewgill.com/chaos-and-fractals/

    其中一个示例是一个简单的Mandelbrot实现。


    6
    另一个很好的分形图案是Sierpinski三角形分形图案。基本上,先画出三个角(等边三角形最好,但任何三角形都可以),然后在其中一个角上开始一个点P。将P随机移动到任意3个角的中心点,绘制一个点。再次将P向任意随机角度移动一半距离,绘制并重复此过程。你可能会认为随机运动会产生随机结果,但实际上并不是这样。参考:http://en.wikipedia.org/wiki/Sierpinski_triangle

    2
    这实际上是IFS的基础,可以被视为您所描述方法的概括。 - Algorias

    6
    Sierpinski三角形和Koch曲线是一类特殊的火焰分形图案。火焰分形是迭代函数系统的一种非常泛化的类型,因为它使用非线性函数。
    IFS的算法如下:
    从一个随机点开始。
    重复以下步骤很多次(至少一百万次,取决于最终图像的大小):
    对该点应用N个预定义变换之一(矩阵变换或相似变换)。例如,乘以0.5可以是一个例子。 在屏幕上绘制新点。
    如果该点在屏幕外,则随机选择一个屏幕内的新点。
    如果您想要漂亮的颜色,让颜色取决于最后使用的变换。

    5
    我认为你可能不把分形看作算法或编程的一部分。分形是一个概念!它是一种重复出现的详细图案的数学概念。
    因此,您可以使用不同的方法创建分形,如下图所示。

    enter image description here

    选择一种实现方法,然后研究如何实现。这四个示例使用 Marvin Framework 实现。源代码可在 此处 获取。

    5
    我建议从一些简单的内容开始,比如科赫雪花。这是一个简单的过程,只需要对一条线进行变换,然后递归重复该过程,直到它看起来很漂亮。
    例如,可以先取两个点(一条线),再添加第三个点(形成一个角),然后在每个新创建的部分上重复该过程,非常简单易懂。
    fractal(p0, p1){
        Pmid = midpoint(p0,p1) + moved some distance perpendicular to p0 or p1;
        fractal(p0,Pmid);
        fractal(Pmid, p1);
    }
    

    4
    这是一个使用普通的JavaScript和HTML编写的Mandelbrot分形的CodePen示例
    希望这份代码容易理解。
    最复杂的部分是缩放和平移坐标系。另外,制作彩虹调色板也很复杂。
    function mandel(x,y) {
      var a=0; var b=0;
      for (i = 0; i<250; ++i) {
        // Complex z = z^2 + c
        var t = a*a - b*b;
        b = 2*a*b;
        a = t;
        a = a + x;
        b = b + y;
        var m = a*a + b*b;
        if (m > 10)  return i;
      }
      return 250;
    }
    

    enter image description here


    3
    曼德布洛集合是通过重复计算函数直到它溢出(达到某个定义的极限),然后检查溢出所需时间来生成的。
    伪代码:
    MAX_COUNT = 64 // if we haven't escaped to infinity after 64 iterations, 
                   // then we're inside the mandelbrot set!!!
    
    foreach (x-pixel)
      foreach (y-pixel)
        calculate x,y as mathematical coordinates from your pixel coordinates
        value = (x, y)
        count = 0
        while value.absolutevalue < 1 billion and count < MAX_COUNT
            value = value * value + (x, y)
            count = count + 1
    
        // the following should really be one statement, but I split it for clarity
        if count == MAX_COUNT 
            pixel_at (x-pixel, y-pixel) = BLACK
        else 
            pixel_at (x-pixel, y-pixel) = colors[count] // some color map. 
    

    注意:

    value是一个复数。复数(a+bi)的平方是(aa-b*b+2*abi)。你需要使用复数类型,或者在循环中包含该计算。


    1
    您确定 value.absolutevalue 小于 10 亿吗?一旦 value.absolutevalue 超过2,它将“逃逸”并趋向无穷大。因此,您只需要测试到2就可以了,而不是10 亿。 - abelenky

    网页内容由stack overflow 提供, 点击上面的
    可以查看英文原文,
    原文链接