Question

The Mandelbrot set has been a favorite of mine for many years. I've successfully created it in Pascal years ago and more recently on a TI-83 graphing calculator (Java renders it juuuuust a bit faster).

Since complex numbers are involved, I took a version from a text that extends RecursiveAction using a BufferedImage and ForkJoinPool (without understanding the those concepts and the overall implementation) and, using routines I developed a few months ago, modified (the heck out of) the code that does the point-plotting so that it looks more like complex numbers are involved.

Original:

public class MandelbrotTask extends RecursiveAction  {

...

public void render() {

...

for (int x = xStart; x <= xEnd; x++) {
  for (int y = yStart; y <= yEnd; y++) {
    double r = x * zoomFactor / image.getWidth() - zoomFactor / 2 + offsetX;
    double i = y * zoomFactor / image.getHeight() - zoomFactor / 2 + offsetY;
    double zr = 0, zi = 0;
    int iter;
    for (iter = 0; iter < maxIter; iter++) {
      double nzr = zr * zr - zi * zi + r;
      double nzi = 2 * zr * zi + i;
      if (nzr * nzr + nzi * nzi > escapeRadius * escapeRadius)
        break;
      zr = nzr;
      zi = nzi;
    }
    image.setRGB(x, y, Color.HSBtoRGB(0.5f * iter / maxIter, 1.0f, 1.0f));
  }
}

My revised, somewhat-cleaner code:

for (int x = xStart; x <= xEnd; x++) {
  for (int y = yStart; y <= yEnd; y++) {
    z1 = new ComplexNumber(x * dx - zoomFactor / 2 + offsetX,
                           y * dy - zoomFactor / 2 + offsetY);
    z0 = new ComplexNumber(0,0);
    int iter;
    for (iter = 0; iter < maxIter; iter++) {
      nz = cAdd(cMult(z0,z0),z1);
      if (cAbs(nz) > escapeRadius )
        break;
      z0 = nz;
    }
    image.setRGB(x, y, Color.HSBtoRGB(0.5f * iter / maxIter, 1.0f, 1.0f));
  }
}

My only question is how to get rid of "new" on the two lines defining z1 and z0. It seems like I'm wasting a ton of memory since the two objects get "newed" a total of 1,000,000+ times during the almost 25,000 executions of the above block of code, though there's no problem as is.

I know I need new at least once inside the method, but if I put the statements (shown below) outside the loop (and either inside or outside render()), if I omit new from those two lines defining z1 and z0 in the block of code above, I get the error "cannot find symbol: method ComplexNumber(double,double) location: class MandelbrotTask."

z1 = new ComplexNumber();
z0 = new ComplexNumber();

---- edit 10:21 12/26/13

Here is the part of the ComplexNumber class that is invovled. The constructor call ComplexNumber() sets real and imag-inary parts to 0.

class ComplexNumber {
  public double real;       
  public double imag;       

  public ComplexNumber() {
    real = 0.0;
    imag = 0.0;
  }

  public ComplexNumber(double r, double i) {
    this.real = r;
    this.imag = i;
  }

    public static ComplexNumber cAdd(ComplexNumber a, ComplexNumber b) {
    return new ComplexNumber(a.real + b.real, a.imag + b.imag);
  }

  public static ComplexNumber cMult(ComplexNumber a, ComplexNumber b) {
   return new ComplexNumber(a.real * b.real - a.imag * b.imag, a.real * b.imag + a.imag * b.real);
  }

  public static double sqr(double x) {
    return x * x;
  }

  public static double cAbs(ComplexNumber z) {
    return Math.sqrt(sqr(z.real) + sqr(z.imag));
  }

}
Was it helpful?

Solution

Got a few upvotes, so I am converting my comment to an answer. If you want to avoid reinstantiating over and over again inside the loop, your only way out is to create setter methods for your ComplexNumber class:

public void setReal(double real) { this.real = real; }
public void setImaginary(double im) { this.im = im; }
public void setTo(double real, double im) { setReal(real); setImaginary(im); }

I am assuming your class has fields called real and im.

Moreover, if you can't modify the class itself, you should extend it by creating a wrapper class of the form class MyComplexNumber extends ComplexNumber, and then implement the setter methods for MyComplexNumber.

OTHER TIPS

Types are good but I don't see a need for them here if your problem is memory.

Try creating methods inside ComplexNumber such as

public void init(){
  //clear and reset all variables inside ComplexNumber
  ...
}

and

public void set(param1, param2...){
  //set required variables
  ...
}

Then use set() and init() respectively inside your loop instead of creating a new instance each time. This works if you don't need references to the objects created inside the loop.

I should have pointed out in advance that the following code--with no new, no setters--accomplishes the task:

    z1.real = x * dx - zoomFactor / 2 + offsetX;
    z1.imag = y * dy - zoomFactor / 2 + offsetY;
    z0.real = 0;
    z0.imag = 0;

I was just hoping for something along the lines of my original revised code, one line per complex number.

====================

Here's me extending ComplexNumber class into a RealNumber class (which seems a bit backward, but...):

class RealNumber extends ComplexNumber 
{  // RealNumber IS-A subclass ...

  RealNumber() {}

  RealNumber(double r) {
    super.imag = 0;                         // ... THIS kind of subclass ... 
    super.real = r;                         // ... of ComplexNumber!           
  }
}

============================

Hey, @Chthonic, this worked:

public void setReal(double real) { this.real = real; }
public void setImaginary(double imag) { this.imag = imag; }
public void makeComplex(double real, double imag) { setReal(real); setImaginary(imag); }  

...

    z1 = new ComplexNumber();
    z0 = new ComplexNumber(); // before loop

...

    z1.makeComplex(x * dx - zoomFactor / 2 + offsetX,y * dy - zoomFactor / 2 + offsetY);
    z0.makeComplex(0, 0);

THANKS for the idea. I'm not certain I got exactly why my original z1 = new Complex... didn't work, but I'll think about the replies.

(I just realized that I "learned" something that I already "knew". Happens all the time.)

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top