マンデルブロ集合: C++でも書いてみた

mandelbrot

調子にのってC++でもマンデルブロ集合の計算をやってみました。

F#: インターフェイスの使用 (マンデルブロ集合)」でインターフェイスを作っておいたので、これを実装する形でコードを書く形。

念のため、インターフェイスを再掲。

using System.Collections.Generic;
using System.Numerics;

namespace Masaminh.Mandelbrot
{
    public interface IMandelbrot
    {
        string Name { get; }
        IEnumerable<int> Calc(Complex center, double step, int width, int height, int threshold);
    }
}

これを実装する形でC++/CLIのクラスを作成。Visual Studioでクラスライブラリを作った時点で.hに実装を書くようになっていたのでそれに従ってみましたけど、C++/CLIではヘッダーに実装を書くのが流儀なんですかね?

#pragma once

#include "MandelbrotCppLogic.h"
#include <complex>
#include <vector>

using namespace System;
using namespace System::Collections::Generic;
using namespace System::Numerics;

using namespace std;

namespace Masaminh {
	namespace Mandelbrot {

		public ref class MandelbrotCpp : public IMandelbrot
		{
		public:
			virtual property String ^Name {
				String ^get()
				{
					return "C++ロジック";
				}
			}

			virtual IEnumerable<int> ^Calc(Complex center, double step, int width, int height, int threshold)
			{
				vector<int> result;
				MandelbrotCppLogic::Calc(&result, complex<double>(center.Real, center.Imaginary), step, width, height, threshold);
				auto rc = gcnew List<int>(result.size());
				for (auto x : result) {
					rc->Add(x);
				}
				return rc;
			}

		};
	}
}

ここからがロジック本体。このクラスはネイティブで動かすためにC++/CLIではなくてC++で書いてます。MandelbrotCppLogic::Implクラスはプライベートメソッドを隠ぺいするために用意してます。

#pragma once

#include <complex>
#include <vector>

class MandelbrotCppLogic
{
public:
	static void Calc(std::vector<int> *result, const std::complex<double> &center, double step, int width, int height, int threshold);

private:
	MandelbrotCppLogic();
	~MandelbrotCppLogic();

	class Impl;
};
#include "stdafx.h"

#pragma unmanaged

#include "MandelbrotCppLogic.h"

using namespace std;

class MandelbrotCppLogic::Impl {
public:
	static int CalcPoint(int threshold, const complex<double> &c)
	{
		complex<double> z;

		for (int i = 0; i < threshold; ++i)
		{
			z = z * z + c;
			if (z.real() * z.real() + z.imag() * z.imag() > 4.0) {
				return i;
			}
		}

		return -1;
	}

private:
	Impl();
	~Impl();
};

void MandelbrotCppLogic::Calc(
	vector<int> *result,
	const complex<double> &center, double step,
	int width, int height, int threshold)
{
	if (result == nullptr) {
		return;
	}

	result->reserve(width * height);

	complex<double> c(0, center.imag() - step * height / 2.0);

	for (int y = 0; y < height; ++y)
	{
		c.real(center.real() - step * width / 2.0);

		for (int x = 0; x < width; ++x)
		{
			result->push_back(Impl::CalcPoint(threshold, c));
			c.real(c.real() + step);
		}

		c.imag(c.imag() + step);
	}
}

C++で書いたネイティブコードの方がF#で書いたロジックより速いだろう、と思っていたのですが、ほぼ同じくらいか微妙にF#ロジックの方が速いくらい(厳密に計測はしてないけど)。ネイティブと言ってもVirtualBox上だからかなぁ…

スポンサーリンク

フォローする

スポンサーリンク