お馬の写真 別館

「お馬の写真」管理者による徒然なるブログ

マンデルブロ集合: 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上だからかなぁ…

 - プログラミング