0

Previously posted a question on a C#.Net Mandelbrot set a while back which was helpfully answered, however I have had to go back to this Mandelbrot set and implement a struct on it's double variables that define the (imaginary and real) coordinates.

As someone that is just new to structs and a little rusty with them, I would like a little advice on what I am doing wrong and how to improve the said code as just by looking at it, I'm sure it can be optimized slightly. Is this the correct way to utilize a struct? If not, what are the alternatives or best techniques? Thanks.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace wmcMandelbrot
{
struct doubleVar
{

    public double realCoord, imagCoord;
    public double realTemp, imagTemp, realTemp2, arg;
    public double newIcoord, newIcoord1, newIcoord2;
    public double rCoord, rCoord1, rCoord2;
 }


class Program
{
    static void Main(string[] args)
    {

        doubleVar doubleNum;
        int iterations;

        Console.WriteLine("Enter first imaginary coord: ");
        doubleNum.newIcoord = Convert.ToDouble(Console.ReadLine());

        Console.WriteLine("Enter second imaginary coord: ");
        doubleNum.newIcoord1 = Convert.ToDouble(Console.ReadLine());

        Console.WriteLine("Enter third imaginary coord: ");
        doubleNum.newIcoord2 = Convert.ToDouble(Console.ReadLine());

        Console.WriteLine("Enter first real coord: ");
        doubleNum.rCoord = Convert.ToDouble(Console.ReadLine());

        Console.WriteLine("Enter second real coord: ");
        doubleNum.rCoord1 = Convert.ToDouble(Console.ReadLine());

        Console.WriteLine("Enter third real coord: ");
        doubleNum.rCoord2 = Convert.ToDouble(Console.ReadLine());

        for (doubleNum.imagCoord = doubleNum.newIcoord; doubleNum.imagCoord >= -doubleNum.newIcoord1; doubleNum.imagCoord -= doubleNum.newIcoord2)
        {
            for (doubleNum.realCoord = -doubleNum.rCoord; doubleNum.realCoord <= doubleNum.rCoord1; doubleNum.realCoord += doubleNum.rCoord2)
            {
                iterations = 0;
                doubleNum.realTemp = doubleNum.realCoord;
                doubleNum.imagTemp = doubleNum.imagCoord;
                doubleNum.arg = (doubleNum.realCoord * doubleNum.realCoord) + (doubleNum.imagCoord * doubleNum.imagCoord);
                while ((doubleNum.arg < 4) && (iterations < 40))
                {
                    doubleNum.realTemp2 = (doubleNum.realTemp * doubleNum.realTemp) - (doubleNum.imagTemp * doubleNum.imagTemp) - doubleNum.realCoord;
                    doubleNum.imagTemp = (2 * doubleNum.realTemp * doubleNum.imagTemp) - doubleNum.imagCoord;
                    doubleNum.realTemp = doubleNum.realTemp2;
                    doubleNum.arg = (doubleNum.realTemp * doubleNum.realTemp) + (doubleNum.imagTemp * doubleNum.imagTemp);
                    iterations += 1;
                }
                switch (iterations % 4)
                {
                    case 0:
                        Console.Write(".");
                        break;
                    case 1:
                        Console.Write("o");
                        break;
                    case 2:
                        Console.Write("O");
                        break;
                    case 3:
                        Console.Write("@");
                        break;
                }
            }
            Console.Write("\n");
        }
        Console.ReadKey();




    }
  }
}
Tim
  • 35,413
  • 11
  • 95
  • 121
wilbomc
  • 183
  • 3
  • 13
  • You are getting no value out of that struct as the moment. Ordinarily you'd use one to pass a number of related values. Or as a light weight class, e.g. values and methods. PS If you are on .net 4 onwards, you might want to have a peek at the System.Numerics.Complex struct – Tony Hopkinson Sep 06 '12 at 22:33
  • Get a value like that of a function? – wilbomc Sep 06 '12 at 22:43

2 Answers2

3

I think you should use a struct to represent each complex number. That struct can then contain the functions to add, subtract, multiply those complex numbers together:

eg.

struct Complex
{
    public double real;
    public double imag;

    public Complex(double r, double i)
    {
        real = r;
        imag = i;
    }

    public Complex add(Complex operand)
    {
        return new Complex(real + operand.real, imag + operand.imag);
    }

    public Complex multiply(Complex operand)
    {
        return new Complex(real * operand.real - imag * operand.imag, real * operand.imag + operand.real * imag);
    }
}

Then to use the struct you could do something like

Complex c = new Complex( ...coordinate goes here... );
Complex z = new Complex(0,0);

// z -> z^2 + c
Complex result = z.multiply(z).add(c);

etc.

You could also add a "sq" (square the number) or "pow" (raise to a given power) or "arg" (in your above code) to the complex number struct. The sky's the limit! Note that these functions don't have to return another complex number, "arg" for example would return a double.

Also if you are always grouping together two complex numbers (the map point and the z value) you could create a struct to hold two complex numbers:

struct MapPoint
{
    Complex c;
    Complex z;
}

And so on. For me it makes sense to group things in structs which are logically indivisible or things which are always worked on as a group.

Wayne Uroda
  • 5,025
  • 4
  • 30
  • 35
  • 2
    There would be no benefit gained from switching to a class in this scenario. – James Sep 07 '12 at 07:55
  • Thanks for the answer but have to agree with James in that a class isn't really necessary as this mandelbrot set is just a simple console app. – wilbomc Sep 07 '12 at 17:23
  • @james Ahh I didn't realise that structs could have member functions. Having said that, is there a benefit using a struct over a class? – Wayne Uroda Sep 09 '12 at 23:38
  • @wilbomc, I agree your app is small now, but what if (hypothetically) the requirements were to change and you had to also include say the Julia set - in this case, you could re-use the implementation for the complex number (whether it is class based or struct based, it doesn't make any difference - replace the word "class" with "struct" in the code above and it behaves the same. Re-usability is one of the core goals of object oriented programming, as well as grouping data together with the operations which work on that data. – Wayne Uroda Sep 09 '12 at 23:43
  • 1
    @WayneUroda "*is there a benefit using a struct over a class*" - if you are dealing with a lot of instances there would be some performance gain using Structs. Structs should be used for lightweight objects, which in this scenario is makes sense. In fairness, switching to a class wouldn't be necessarily a *bad* thing, however, it probably isn't a good design to use a class for simple data like this (unless you intend to extend the functionality later). – James Sep 10 '12 at 07:44
  • Will I go back to the console window version? Probably not unless to tweak minor issues. However with a bit more experience with OOP and classes, I will most likely re-write the Mandelbrot set from scratch using Visual Studio's GUI objects and at this point I will most certainly consider using a class rather than a struct. – wilbomc Sep 11 '12 at 17:32
  • 1
    Classes are very bad in situations where one will frequently be making new things from old ones, and where there is no meaningful concept of referential identity. If one were to create a `ComplexClass` instance to hold the product of two complex numbers, that result of that computation would have to be held in memory until the next garbage collection, even if nothing else needed it. By contrast, if one `ComplexStruct`, the results of old computations may be directly overwritten with the results of newer ones. – supercat Sep 19 '12 at 15:13
  • Thanks @supercat that makes it crystal clear what the advantages are. Very interesting. – Wayne Uroda Sep 20 '12 at 07:29
1

It would make sense to store only the value you need to define the coordinates (read and imaginary parts) and none of the temporary values. Also calculated values can be properties, like this:

public struct MapPoint
{
    public double realCoord, imagCoord;
    public double realConst, imagConst;

    public MapPoint(double realConst, double imagConst)
        : this(realConst, imagConst, realConst, imagConst) { }

    public MapPoint(double realCoord, double imagCoord, double realConst, double imagConst)
    {
        this.realCoord = realCoord;
        this.imagCoord = imagCoord;
        this.realConst = realConst;
        this.imagConst = imagConst;
    }
    public double Argument // calculated property
    {
        get { return realCoord * realCoord + imagCoord * imagCoord; }
    }

    public MapPoint Iterate()
    {
        // Do the z = z^2+c thing

        return new MapPoint(
            realCoord * realCoord - imagCoord * imagCoord + realConst,
            2 * realCoord * imagCoord + realConst,
            realConst, imagConst);
    }
}

Now to use this structure like this:

void Main() {
    for( double y=...
      for( double x=...

        int iterations = 0;
        MapPoint point = new MapPoint(x, y);

        while (iterations < 40 || point.Argument < 4.0)
        {
            point = point.Iterate();
            iterations++;
        }

        switch (iterations % 4)
        {
            //...
        }
    ...
}

The key to this is that all the calculations are done inside the structure, near where the information is stored. This way the struct can be re-used on other places with minimal effort without having to re-write the calculation block again. Also any temporary values are thrown away when not needed and not stored next to the important values.

Please promise to read up on structures here and here and then re-write the code above as your own.

John Alexiou
  • 28,472
  • 11
  • 77
  • 133
  • Great answer! Took a while for me to break down though lol but thanks for the help and for the links to structs. Hope others come across this if they are struggling with structs. Thanks again! – wilbomc Sep 07 '12 at 17:20
  • It may be worth noting that although the second linked post suggests wrapping struct fields in properties, it's generally better to simply have them expose public fields than to have mutable properties backed by private fields. None of the normal advantages which would apply to encapsulating class fields behind properties are applicable to structures, and the performance penalties for such encapsulation are generally greater than for classes. – supercat Sep 19 '12 at 15:08
  • @supercat, I agree. I would definitely add the `readonly` keyword to public fields and allow change the whole structure atomically in order to make sure it goes through a constructor and values are valid at all times. – John Alexiou Sep 19 '12 at 18:11
  • @ja72: In most cases, I think it's better not to have structs try to enforce invariants on themselves, but to instead specify that code which receives a struct and expects it to meet certain conditions should validate those conditions. A struct is essentially a collection of storage locations bound together with duct tape. While there are times that a struct can usefully pretend to be something more than that, I think structs should behave as collections of variables, possibly with some helper methods, absent a good reason for them to do otherwise. – supercat Sep 19 '12 at 19:45
  • 1
    @ja72: Personally, I think .net would have been better if boxing had to be explicit rather than implicit, and it didn't try to pretend that structs and classes were the same thing. Some people regard mutable structs as evil because they don't act like classes, but that's whole reason mutable structs are *useful*. In many cases, piecewise-mutable objects would be more convenient that immutable ones except that there's often no practical way to know whether anything that holds a reference to an object might be expecting it not to change. Structs eliminate that problem. – supercat Sep 19 '12 at 19:59