C++ 优化Mandelbrot分形

C++ 优化Mandelbrot分形,c++,C++,这是一个在.ppm文件中输出mandelbrot分形的代码。我如何优化它 #include<bits/stdc++.h> using namespace std; int findMandelbrot(double cr, double ci, int max_iterations) { int i = 0; double zr = 0.0, zi = 0.0; while (i < max_iterations && zr * zr

这是一个在.ppm文件中输出mandelbrot分形的代码。我如何优化它

#include<bits/stdc++.h>
using namespace std;

int findMandelbrot(double cr, double ci, int max_iterations)
{
    int i = 0;
    double zr = 0.0, zi = 0.0;
    while (i < max_iterations && zr * zr + zi * zi < 4.0)
    {
        double temp = zr * zr - zi * zi + cr;
        zi = 2.0 * zr * zi + ci;
        zr = temp;
        ++i;
    }
    return i;
}

double mapToReal(int x, int imageWidth, double minR, double maxR)
{
    double range = maxR - minR;
    return x * (range / imageWidth) + minR;
}

double mapToImaginary(int y, int imageHeight, double minI, double maxI)
{
    double range = maxI - minI;
    return y * (range / imageHeight) + minI;
}

int main()
{
    ifstream f("input.txt");
    int imageWidth, imageHeight, maxN;
    double minR, maxR, minI, maxI;

    if (!f)
    {
        cout << "Could not open file!" << endl;
        return 1;
    }

    f >> imageWidth >> imageHeight >> maxN;
    f >> minR >> maxR >> minI >> maxI;

    ofstream g("output_image.ppm");
    g << "P3" << endl;
    g << imageWidth << " " << imageHeight << endl;
    g << "255" << endl;


    double start = clock();

    for (int i = 0; i < imageHeight; i++)
    {
        for (int j = 0; j < imageWidth; j++)
        {
            double cr = mapToReal(j, imageWidth, minR, maxR);
            double ci = mapToImaginary(i, imageHeight, minI, maxI);

            int n = findMandelbrot(cr, ci, maxN);

            int r = ((int)sqrt(n) % 256);
            int gr = (2*n % 256);
            int b = (n % 256);

            g << r << " " << gr << " " << b << " ";
        }
        g << endl;

        if(i == imageHeight / 2) break;
    }

    cout << "Finished!" << endl;

    double stop = clock();

    cout << (stop-start)/CLOCKS_PER_SEC;
    return 0;
}
#包括
使用名称空间std;
int findMandelbrot(双cr、双ci、int max_迭代)
{
int i=0;
双zr=0.0,zi=0.0;
而(i>imageHeight>>最大值;
f>>最小值>>最大值>>最小值>>最大值;
流g(“output_image.ppm”);

g这就是热循环:

int i = 0;
double zr = 0.0, zi = 0.0;
while (i < max_iterations && zr * zr + zi * zi < 4.0)
{
    double temp = zr * zr - zi * zi + cr;
    zi = 2.0 * zr * zi + ci;
    zr = temp;
    ++i;
}
return i;
inti=0;
双zr=0.0,zi=0.0;
而(i
我知道如何在快速CPU指令中实现非整数幂运算,但它不会让您摆脱束缚,因为它根本不适用于复数。使用std::complex也不会有任何帮助。您不会为非内联支付任何费用,当然也无法在找到它们时应用优化。因此,我能做的最好的事情是:

int i = max_iterations;
double zr = 0.0, zi = 0.0;
do {
    double temp = zr * zr - zi * zi + cr;
    zi = 2.0 * zr * zi + ci;
    zr = temp;
} while (--i && zr * zr + zi * zi < 4.0)
return max_iterations - i;
int i=最大迭代次数;
双zr=0.0,zi=0.0;
做{
双温=zr*zr-zi*zi+cr;
zi=2.0*zr*zi+ci;
zr=温度;
}而(-i&&zr*zr+zi*zi<4.0)
返回最大迭代次数-i;
是的,我知道从循环中取出一个整数测试并不能解决所有这些问题。我只找到了另一个优化器,您必须检查它是否真的更快:

int i = max_iterations;
double zr = 0.0, zi = 0.0;
do {
    double tempr = zr * zr - zi * zi + cr;
    double tempi = zr * zi;
    zi = tempi + tempi + ci;
    zr = tempr;
} while (--i && zr * zr + zi * zi < 4.0);
return max_iterations - i;
int i=最大迭代次数;
双zr=0.0,zi=0.0;
做{
双温度=zr*zr-zi*zi+cr;
双温度=zr*zi;
zi=tempi+tempi+ci;
zr=温度;
}而(-i&&zr*zr+zi*zi<4.0);
返回最大迭代次数-i;

这就是所有的人。

findMandelbrot
中,您在循环测试中使用表达式
zr*zr
zi*zi
,但如果测试成功,则重新计算相同的两个表达式。因此,一个明显的改进可能是使用类似于

int findMandelbrot (double cr, double ci, int max_iterations)
{
  int i = 0;
  double zr = 0.0, zi = 0.0;
  double zr2 = 0.0, zi2 = 0.0;
  while (i < max_iterations && zr2 + zi2 < 4.0) {
    double temp = zr2 - zi2 + cr;
    zi = 2.0 * zr * zi + ci;
    zr = temp;
    zr2 = zr * zr;
    zi2 = zi * zi;
    ++i;
  }
  return(i - 1);
}
int findMandelbrot(双cr、双ci、int max\u迭代)
{
int i=0;
双zr=0.0,zi=0.0;
双zr2=0.0,zi2=0.0;
而(i
有许多方法可以优化曼德布罗特分形

一种方法是优化CPU甚至GPU的代码。令人印象深刻的加速如:。所示,它将内部循环优化了近1000倍。这比以前快了3个数量级

但还有其他优化方法。您已经提到的第一种方法是:mandelbrot集合在y=0时镜像。因此,您只需要它的一半。还有一些更简单的方法可以避免运行内部循环。如果向下滚动到优化,您会看到“心形/灯泡检查”。这是一个简单的检查点在苹果形的主要部分或直接左边的圆圈。对于一个海报,可以覆盖很多点

我看到的另一个加速使用了距离估计(同样在维基百科上)方法生成预览或仅生成黑白mandelbrot集的轮廓。计算图像中的随机点,如果在mandelbrot集之外,则使用距离估计方法给定的半径绘制一个圆。该圆中的任何内容都不是mandelbrot集的一部分。这可以快速覆盖mand集之外的许多点埃尔布罗特集

还有一些近似结果的方法,可能不会得到完美的图像,但通常已经足够好了。例如:

  • 边界追踪
计算边界后的点,其中像素需要N次和N+1次迭代才能转义。对于N+1和N+2也是如此。这两个边界之间的所有内容都需要N次迭代

  • 分而治之的盒子
计算矩形的边框(从整个图像开始)。如果边框都需要N次迭代,则矩形的内部需要N次迭代并填充。否则,将矩形拆分为4个,并对每个矩形重复。计算2或3像素宽的边框可能会改善结果,但会保存较少的边框

  • 猜测
以低分辨率计算图像,然后将分辨率加倍,保留计算的点。检查图像,如果5x5个原始点具有相同的N,则在内部3x3点周围填充一个矩形(也适用于3x3、7x7、9x9等点).计算未填写的分数。然后重复整个过程,直到获得最终解决方案

  • 单轨道迭代
这是最难做到的,我只见过它的一个实现。其思想是,在迭代中,靠近的点表现相同。如果你迭代3x3点的网格(首先覆盖整个图像)你可以用牛顿插值法来插值中间点的值,效果很好,直到不行为止

因此,除了3x3点网格外,您还迭代了4个测试点,即每个网格单元的中间。对这13个点进行8次迭代,然后从网格点插值4个测试点。如果计算和插值结果偏移过多(这是困难的部分)扔掉最后8次迭代,将网格细分为4个大小为一半的网格