计算曼德博集合迭代有困难

4

我阅读了这篇文章: http://www.wikihow.com/Plot-the-Mandelbrot-Set-By-Hand

但是我在第七步卡住了。我正在使用javascript画布绘制集合。

我只需要基本的C值就可以了。

for (var y = 0; y < ImageHeight; y++) {
    for (var x = 0; x < ImageWidth; x++) {

        // Pixel-Position for ImageObject
        var xy = (x + y * image.width) * 4;

        // Convert Image-Dimension to a radius of 2
        var xi = ((x / ImageWidth) * 4) - 2;
        var yi = ((y / ImageHeight) * 4) - 2;

        for (var n = 0; n < MaxIterations; n++) {

            // Complex number stuff..?
            z = (xi*xi) + (yi*yi) + c;
            c = 0; // Somethig with z ..?

            if (z < 4) {

                image.data[xy] = inner_color[0];
                image.data[xy+1] = inner_color[1];
                image.data[xy+2] = inner_color[2];
                image.data[xy+3] = Math.round(n * cdiff);

            } else {

                image.data[xy] = outer_color[0];
                image.data[xy+1] = outer_color[1];
                image.data[xy+2] = outer_color[2];
                image.data[xy+3] = Math.round(n * cdiff);

                break;
            }
        }
    }
}

我也阅读了很多关于虚数等内容,但我并不太理解如何进行计算。并且它们似乎对我来说毫无用处,因为你还是必须将它们转换回实数才能在javascript中执行逻辑操作。

这是它的样子:[已删除]
如果您移除url末尾的2,您会看到另一种版本,其中我只是重写了一个小的c++代码片段。 但缩放有些奇怪,这就是为什么我想自己编写它的原因。

我理解迭代函数集合创建的基本概念,但正如我所说,复数部分令我困扰。或许还有更简单的解释呢?


不行,每个 Mandelbrot 的实现都使用复数,因为它是复平面上的一个图形。 - duffymo
但是对我来说,复平面看起来就像普通的坐标平面,为什么要称之为复数? - user625860
(叹气)你好像需要了解一些关于复数的知识。如果你了解了,答案就很明显了。一个复数的实部是它的x坐标,虚部是它的y坐标。 - duffymo
是的,我知道。我也知道如何添加/乘以复数。我只是不知道如何在编程语言中使用它们,因为你不能用像“2i”这样的字符串计算东西。我阅读/观看的所有教程都没有解释那一部分。 - user625860
编写自己的Complex类,看看它能带给你什么。从这里开始。 - duffymo
Duffymo的页面非常棒(在他的回答中),但我清楚地记得当我是一个Mandelbrot新手时,看代码并没有帮助太多,因为我对分形的理解很低。我刚刚检查了你提到的链接,你应该回到第一步 :-) 你没有很好地掌握它,我会尝试在回答中涵盖一些内容。 - karatedog
1个回答

16

首先你需要理解这个:

z = z^2 + c

让我们来分解一下。
变量 z 和 c 都是复数(最近有一个问题教给我要强调这一点,它们具有小数位,并且可能看起来像这样:c=-0.70176-0.3842i)。复数可以有一个“非实数”的部分,正确的术语是虚部,并且你可以用以下形式表示一个 单一 复数:
(a + bi),也可以写成:(a + b*i)
如果 b 是 0,则你得到一个 a + 0i,它就是简单的 a,所以没有 部分的时候你得到一个实数。
你的链接没有提到一个复数最重要的属性,尤其是它的虚部的一个属性,即 i == sqrt(-1)。在实数领域中,不存在负数的平方根,这就是复数的作用,让你可以有-1的平方根。让我们把i平方: i^2 == -1,魔术!
虚部(i)必须由你(进行特殊平方)处理,或者你使用的编程语言将为你提供一个处理它的复数类型。
现在回到扩展z^2z == (a+bi),因此z^2 == (a+bi)^2,所以z^2 == (a^2 + bi^2 + 2*a*bi)
让我们也将其分解:
  • a^2 => 这是简单的,它是一个实数
  • bi^2 => 棘手的部分。这实际上是b^2*i^2。我们得到了一个i^2,它是-1,这使它成为b^2*-1或:-b^2。所以这也是一个实数
  • 2*a*b*i => 这将是虚数部分

结果:z^2 = (a^2-b^2+2*a*bi)

例子(有点过于详细了。您可以将其视为循环中的第一次迭代):

z = (5 + 3i)
z^2 = (5 + 3i)^2
    = (5^2 + 3^2*i^2 + 2*5*3i)
    = (25 + 9i^2 + 30i)
    = (25 + 9*-1 + 30i)
    = (25 - 9 + 30i)
    = (16 + 30i)

现在如果你理解了复数的迭代和乘法,那么关于曼德博集合(以及 c 值)的一些说明:当你想创建曼德博集合时,你实际上是在寻找复平面上的点,如果使用上述讨论的迭代进行迭代-比如50次-它们永远不会趋近于无穷大。曼德博集合是通常看到的“曼德博”图片中的黑色部分,而不是闪亮、彩色的部分。

Mandelbort set, taken from Wikipedia

通常的工作流程如下:

  • 在复平面上选择一个点,比如 (1.01312 + 0.8324i) => 这将是 c 的值!
  • 在第一次迭代之前,将 (0, 0i) 放入 z 中,然后按照之前的次数进行迭代 => z = z^2 + c。是的,你正在对一个点进行平方,并将该点加到它自己上,这是 曼德博集合 的一个非常重要的属性。首先进行 50 次迭代。这将给你一个复数作为结果。
  • 如果结果复数的任何部分(实部或虚部)等于或大于 2,则我们假设此点会趋向于无穷大,并认为此点不属于曼德博集合*。这是需要对该点进行着色的情况(这是曼德博集合的多彩部分)。如果复数的两个部分都小于 2,则我们假设该点永远不会趋向于无穷大(即使进行了无数次迭代),并认为该点属于曼德博集合,其颜色将为黑色。
  • 重复(选择下一个点,将其值放入 c 中,将零放入 z 中并计算)

*实际上,验证一个点是否属于集合有些复杂,但对于原型来说这很有效


1
嘿,感谢您抽出时间回答这个问题,尽管它已经过去一个月了。我已经理解了迭代背后的基本方案,但我仍然很难意识到复数是什么(如果这有任何意义的话)。无论如何,我现在放弃了,但我相信我几个月后会再次回到这个问题上。 - user625860
1
曼德博集合看久了会变得无聊,下一步是朱利亚集合 :-) - karatedog
1
呵呵,两年后我终于理解了复数。 :D https://www.shadertoy.com/view/MsXXWN - user625860
@EliasSchütt 很好,恭喜啊 :-) - karatedog

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