Complex構造体は.NET Framework 4より使用できるようになった構造体で、複素数型を表す構造体です。 実部・虚部それぞれをDouble型の値で格納し、一つの型として扱えるようにした型となっています。
なお、BigInteger構造体と同様、Complex構造体はアセンブリSystem.Numerics.dllに含まれています。 使用する場合は、System.Numerics.dllへの参照を追加する必要があります。
演算子
Complex構造体には、四則演算子・等価/不等価演算子が定義されているため、プリミティブな実数型と同じように演算式を記述することができます。
using System;
using System.Numerics;
class Sample {
static void Main()
{
Complex a = 1.0;
Complex b = new Complex(0.0, 1.0);
Complex c = a + b;
Console.WriteLine(a);
Console.WriteLine("-{0} = {1}", b, -b);
Console.WriteLine("{0} + {1} = {2}", a, b, c);
Console.WriteLine("{0} - {1} = {2}", a, b, a - b);
Console.WriteLine("{0} * {1} = {2}", a, b, a * b);
Console.WriteLine("{0} / {1} = {2}", a, b, a / b);
if (a == b)
Console.WriteLine("{0} == {1}", a, b);
else
Console.WriteLine("{0} != {1}", a, b);
if (a != c)
Console.WriteLine("{0} != {1}", a, c);
else
Console.WriteLine("{0} == {1}", a, c);
}
}
Imports System
Imports System.Numerics
Class Sample
Shared Sub Main()
Dim a As Complex = 1.0
Dim b As New Complex(0.0, 1.0)
Dim c As Complex = a + b
Console.WriteLine(a)
Console.WriteLine("-{0} = {1}", b, -b)
Console.WriteLine("{0} + {1} = {2}", a, b, c)
Console.WriteLine("{0} - {1} = {2}", a, b, a - b)
Console.WriteLine("{0} * {1} = {2}", a, b, a * b)
Console.WriteLine("{0} / {1} = {2}", a, b, a / b)
If a = b Then
Console.WriteLine("{0} = {1}", a, b)
Else
Console.WriteLine("{0} <> {1}", a, b)
End If
If a <> c Then
Console.WriteLine("{0} <> {1}", a, c)
Else
Console.WriteLine("{0} = {1}", a, c)
End If
End Sub
End Class
(1, 0) -(0, 1) = (0, -1) (1, 0) + (0, 1) = (1, 1) (1, 0) - (0, 1) = (1, -1) (1, 0) * (0, 1) = (0, 1) (1, 0) / (0, 1) = (0, -1) (1, 0) != (0, 1) (1, 0) != (1, 1)
また、IEquatable<BigInteger>も実装しているので、このインターフェースを使用した比較も行えます。
IEquatableインターフェイスおよびIEquatableインターフェイスを使った比較については等価性の定義と比較を参照してください。
型変換
Complex構造体は、Doubleなどの実数型、Int32などの整数型からの暗黙の型変換をサポートしています。 型変換によりComplex構造体を構築した場合、値は実部として扱われ、虚部は常に0となります。 Decimal, BigIntegerからも型変換を行うことができますが、オーバーフローや精度が失われる可能性があるため明示的な型変換を行う必要があります。
using System;
using System.Numerics;
class Sample {
static void Main()
{
Complex a = 1.0; // double
Complex b = 100; // int
Complex c = (Complex)(1.0m / 3.0m); // decimal
Console.WriteLine(a);
Console.WriteLine(b);
Console.WriteLine(c);
}
}
Imports System
Imports System.Numerics
Class Sample
Shared Sub Main()
Dim a As Complex = 1.0R ' Double
Dim b As Complex = 100 ' Integer
Dim c As Complex = CType(1.0D / 3.0D, Complex) ' Decimal
Console.WriteLine(a)
Console.WriteLine(b)
Console.WriteLine(c)
End Sub
End Class
(1, 0) (100, 0) (0.333333333333333, 0)
PointF構造体などからの変換はサポートされていないので、コンストラクタを使う必要があります。
using System;
using System.Drawing;
using System.Numerics;
class Sample {
static void Main()
{
PointF pt = new PointF(3.0f, 4.0f);
//Complex z = (Complex)pt;
// error CS0030: 型 'System.Drawing.PointF' を型 'System.Numerics.Complex' に変換できません
Complex z = new Complex(pt.X, pt.Y);
Console.WriteLine(z);
}
}
Imports System
Imports System.Drawing
Imports System.Numerics
Class Sample
Shared Sub Main()
Dim pt As New PointF(3.0F, 4.0F)
'Dim z = As Complex = CType(pt, Complex)
' error CS0030: 型 'System.Drawing.PointF' を型 'System.Numerics.Complex' に変換できません
Dim z As New Complex(pt.X, pt.Y)
Console.WriteLine(z)
End Sub
End Class
また、Complex型から他の実数型・整数型へ直接変換することはできないので、実部・虚部を個別に取得して変換する必要があります。
実部・虚部・絶対値・偏角
Complexに格納されている値の状態を知るために、次のようなプロパティが用意されています。
プロパティ | 値 |
---|---|
Real | 複素数の実部の値(Re z)を取得する。 |
Imaginary | 複素数の虚部の値(Im z)を取得する。 |
Magnitude | 複素数の絶対値(|z|)を取得する。 返される値はMath.Sqrt(z.Real * z.Real + z.Imaginary * z.Imaginary)と同じ。 |
Phase | 複素数のガウス平面上での偏角(arg z)を取得する。 単位はラジアン。 返される値はMath.Atan2(z.Imaginary, z.Real)と同じ。 |
using System;
using System.Numerics;
class Sample {
static void Main()
{
Complex z = new Complex(0.86603, 0.50000);
Console.WriteLine(z);
Console.WriteLine("Re z = {0}", z.Real);
Console.WriteLine("Im z = {0}", z.Imaginary);
Console.WriteLine("|z| = {0}", z.Magnitude);
Console.WriteLine("arg z = {0}", z.Phase);
}
}
Imports System
Imports System.Numerics
Class Sample
Shared Sub Main()
Dim z As New Complex(0.86603, 0.50000)
Console.WriteLine(z)
Console.WriteLine("Re z = {0}", z.Real)
Console.WriteLine("Im z = {0}", z.Imaginary)
Console.WriteLine("|z| = {0}", z.Magnitude)
Console.WriteLine("arg z = {0}", z.Phase)
End Sub
End Class
(0.86603, 0.5) Re z = 0.86603 Im z = 0.5 |z| = 1.00000398044208 arg z = 0.523596477499666
なお、絶対値 |z| はComplex.Absメソッドでも得られます。
using System;
using System.Numerics;
class Sample {
static void Main()
{
Complex z = new Complex(0.86603, 0.50000);
Console.WriteLine(z);
Console.WriteLine("|z| = {0}", z.Magnitude);
Console.WriteLine("|z| = {0}", Complex.Abs(z));
}
}
Imports System
Imports System.Numerics
Class Sample
Shared Sub Main()
Dim z As New Complex(0.86603, 0.50000)
Console.WriteLine(z)
Console.WriteLine("|z| = {0}", z.Magnitude)
Console.WriteLine("|z| = {0}", Complex.Abs(z))
End Sub
End Class
(0.86603, 0.5) |z| = 1.00000398044208 |z| = 1.00000398044208
定数
Complex構造体には、次のような定数を参照するための静的フィールドが用意されています。
フィールド | 定数 |
---|---|
Zero | 実部・虚部ともに0のComplex (0 + i0) |
One | 実部が1、虚部が0のComplex (1 + i0) |
ImaginaryOne | 実部が0、虚部が1のComplex、虚数単位 (0 + i1) |
using System;
using System.Numerics;
class Sample {
static void Main()
{
Console.WriteLine(Complex.Zero);
Console.WriteLine(Complex.One);
Console.WriteLine(Complex.ImaginaryOne);
Console.WriteLine("{0} * {0} = {1}",
Complex.ImaginaryOne,
Complex.ImaginaryOne * Complex.ImaginaryOne);
}
}
Imports System
Imports System.Numerics
Class Sample
Shared Sub Main()
Console.WriteLine(Complex.Zero)
Console.WriteLine(Complex.One)
Console.WriteLine(Complex.ImaginaryOne)
Console.WriteLine("{0} * {0} = {1}", _
Complex.ImaginaryOne, _
Complex.ImaginaryOne * Complex.ImaginaryOne)
End Sub
End Class
(0, 0) (1, 0) (0, 1) (0, 1) * (0, 1) = (-1, 0)
極形式
絶対値と偏角からComplex型を構築するには、FromPolarCoordinatesメソッドを使います。 このメソッドを使う場合、
- Complex構造体は直行形式で値を格納するため、極形式→直行形式の変換が行われることによる誤差が生じる場合がある
- Phaseプロパティで得られる偏角は -π ~ +π の範囲に正規化されるため、メソッドに指定した偏角とは異なる値となる場合がある
の2点に注意が必要です。
using System;
using System.Numerics;
class Sample {
static void Main()
{
double r = 1.0;
double theta = 4.0 * Math.PI / 3.0;
Complex z = Complex.FromPolarCoordinates(r, theta);
Console.WriteLine("{0}∠{1} = {2} + i{3}", r, theta, z.Real, z.Imaginary);
Console.WriteLine("r = {0}", z.Magnitude);
Console.WriteLine("Θ = {0}", z.Phase);
}
}
Imports System
Imports System.Numerics
Class Sample
Shared Sub Main()
Dim r As Double = 1.0
Dim theta As Double = 4.0 * Math.PI / 3.0
Dim z As Complex = Complex.FromPolarCoordinates(r, theta)
Console.WriteLine("{0}∠{1} = {2} + i{3}", r, theta, z.Real, z.Imaginary)
Console.WriteLine("r = {0}", z.Magnitude)
Console.WriteLine("Θ = {0}", z.Phase)
End Sub
End Class
1∠4.18879020478639 = -0.5 + i-0.866025403784438 r = 1 Θ = -2.0943951023932
共役・逆数
共役複素数を得るには、Conjugateメソッドが使えます。 得られる結果は、単純に虚部の符号を反転した値をコンストラクタに指定した場合と同じです。
using System;
using System.Numerics;
class Sample {
static void Main()
{
Complex z = new Complex(3.0, 4.0);
Console.WriteLine(z);
Console.WriteLine(Complex.Conjugate(z));
Console.WriteLine(new Complex(z.Real, -z.Imaginary));
}
}
Imports System
Imports System.Numerics
Class Sample
Shared Sub Main()
Dim z As New Complex(3.0, 4.0)
Console.WriteLine(z)
Console.WriteLine(Complex.Conjugate(z))
Console.WriteLine(New Complex(z.Real, -z.Imaginary))
End Sub
End Class
(3, 4) (3, -4) (3, -4)
逆数を得るには、Reciprocalメソッドが使えます。
using System;
using System.Numerics;
class Sample {
static void Main()
{
Complex z = new Complex(3.0, 4.0);
Console.WriteLine(z);
Console.WriteLine(Complex.Reciprocal(z));
}
}
Imports System
Imports System.Numerics
Class Sample
Shared Sub Main()
Dim z As New Complex(3.0, 4.0)
Console.WriteLine(z)
Console.WriteLine(Complex.Reciprocal(z))
End Sub
End Class
(3, 4) (0.12, -0.16)
逆数を1に対する除算で求める場合と、Reciprocalメソッドで求める場合の違いは、値(除数)が0だった場合の結果の違いです。 1に対する除算で逆数を求めた場合、値が0の場合は結果が非数NaNとなります。 一方で、Reciprocalメソッドで逆数を求めた場合は、値が0の場合でも結果は0となります。
using System;
using System.Numerics;
class Sample {
static void Main()
{
Console.WriteLine(Complex.Zero);
Console.WriteLine(1 / Complex.Zero);
Console.WriteLine(Complex.Reciprocal(Complex.Zero));
}
}
Imports System
Imports System.Numerics
Class Sample
Shared Sub Main()
Console.WriteLine(Complex.Zero)
Console.WriteLine(1 / Complex.Zero)
Console.WriteLine(Complex.Reciprocal(Complex.Zero))
End Sub
End Class
(0, 0) (NaN (非数値), NaN (非数値)) (0, 0)
数学関数
Mathクラスに用意されている数学関数は、Complexをサポートしていません。 代わりに、これらの関数はComplex構造体のメソッドとして提供されています。 Complexには次のようなメソッドが用意されています(rはDoubleの数、z, wはComplexの数)。 Mathクラスと同様、いずれも静的メソッドです。
関数 | Complexのメソッド | 相当するMathクラスのメソッド |
---|---|---|
絶対値 |z| | Complex.Abs(z) | Math.Abs |
累乗 zr | Complex.Pow(z, r) | Math.Pow |
累乗 zw | Complex.Pow(z, w) | Math.Pow |
ネイピア数の累乗 ez | Complex.Exp(z) | Math.Exp |
平方根 √z | Complex.Sqrt(z) | Math.Sqrt |
自然対数 ln z | Complex.Log(z) | Math.Log |
常用対数 log10z | Complex.Log10(z) | Math.Log10 |
対数 logrz | Complex.Log(z, r) | Math.Log |
三角関数 sin z, cos z, tan z | Complex.Sin(z), Complex.Cos(z), Complex.Tan(z) | Math.Sin, Math.Cos, Math.Tan |
逆三角関数 sin-1z, cos-1z, tan-1z | Complex.Asin(z), Complex.Acos(z), Complex.Atan(z) | Math.Asin, Math.Acos, Math.Atan |
双曲線関数 sinh z, cosh z, tanh z | Complex.Sinh(z), Complex.Cosh(z), Complex.Tanh(z) | Math.Sinh, Math.Cosh, Math.Tanh |
関数 | Complexのメソッド | 相当するMathクラスのメソッド |
実部・虚部が非数NaNや無限大を含む場合の動作と結果は、Mathクラスのものとほぼ同じとなります。 詳しくは各メソッドのリファレンスを参照してください。
書式
Complex構造体は書式指定子を指定した文字列化をサポートしています。 Double型の値に適用できる書式指定文字を指定することができ、実部・虚部それぞれの値に書式が適用されます。
using System;
using System.Numerics;
class Sample {
static void Main()
{
Complex z = new Complex(0.86603, 0.500000);
Console.WriteLine("{0}", z);
Console.WriteLine("{0:F2}", z);
Console.WriteLine("{0:E2}", z);
}
}
Imports System
Imports System.Drawing
Imports System.Numerics
Class Sample
Shared Sub Main()
Dim z As New Complex(0.86603, 0.500000)
Console.WriteLine("{0}", z)
Console.WriteLine("{0:F2}", z)
Console.WriteLine("{0:E2}", z)
End Sub
End Class
(0.86603, 0.5) (0.87, 0.50) (8.66E-001, 5.00E-001)
Complex型を文字列化する場合、その形式は常に "(実部, 虚部)" となります。 これ以外の形式にしたい場合は、個別に書式を指定するか、カスタム書式プロバイダを実装する必要があります。
書式の種類については書式指定子、書式プロバイダに関しては書式の定義と実装およびカルチャと書式・テキスト処理・暦を参照してください。
Complexの使用例・マンデルブロ集合
Complex構造体を使った例として、マンデルブロ集合の描画を行ってみます。
![実行結果](mandelbrot.png)
// csc /unsafe+ /r:System.Drawing.dll /r:System.Numerics.dll /r:System.Windows.Forms.dll mandelbrot.cs
using System;
using System.Diagnostics;
using System.Drawing;
using System.Drawing.Imaging;
using System.Numerics;
using System.Windows.Forms;
class Mandelbrot : Form {
private static readonly Size windowSize = new Size(720, 720);
private static readonly RectangleF view = RectangleF.FromLTRB(-1.5f, -1.0f, +0.5f, +1.0f);
private static readonly Color colorFill = Color.White;
private Bitmap bitmapMandelbrot = null;
private Stopwatch stopwatch = new Stopwatch();
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new Mandelbrot());
}
public Mandelbrot()
{
this.ClientSize = windowSize;
stopwatch.Start();
bitmapMandelbrot = GenerateMandelbrotImage();
stopwatch.Stop();
}
protected override void OnPaint(PaintEventArgs e)
{
e.Graphics.DrawImageUnscaled(bitmapMandelbrot, 0, 0);
e.Graphics.DrawString(stopwatch.Elapsed.ToString(), SystemFonts.DefaultFont, Brushes.Black, 0.0f, 0.0f);
}
private Bitmap GenerateMandelbrotImage()
{
Bitmap bitmap = new Bitmap(this.ClientSize.Width,
this.ClientSize.Height,
PixelFormat.Format24bppRgb);
BitmapData data = bitmap.LockBits(new Rectangle(0, 0, bitmap.Width, bitmap.Height),
ImageLockMode.WriteOnly,
PixelFormat.Format24bppRgb);
RenderMandelbrot(data);
bitmap.UnlockBits(data);
return bitmap;
}
private unsafe void RenderMandelbrot(BitmapData data)
{
double scaleX = view.Width / (double)data.Width;
double scaleY = view.Height / (double)data.Height;
for (int y = 0; y < data.Height; y++) {
byte* pixel = (byte*)data.Scan0.ToPointer() + y * data.Stride;
double ci = view.Bottom - y * scaleY;
for (int x = 0; x < data.Width; x++) {
const int nDivLimit = 200;
const double magDivLimit = 2.0;
double cr = view.Left + x * scaleX;
Complex c = new Complex(cr, ci);
Complex z = Complex.Zero; // z(0)
pixel[0] = colorFill.B;
pixel[1] = colorFill.G;
pixel[2] = colorFill.R;
for (int n = 0; n < nDivLimit; n++) {
Complex zn = (z * z) + c; // z(n + 1) = z(n)^2 + c
if (zn.Magnitude <= magDivLimit) {
z = zn;
continue;
}
else {
Color col = FromHsv(360 * n / nDivLimit, 255, 255);
pixel[0] = col.B;
pixel[1] = col.G;
pixel[2] = col.R;
break;
}
}
pixel += 3;
} // for x
} // for y
}
private Color FromHsv(int h, int s, int v)
{
if (s == 0)
return Color.FromArgb(v, v, v);
byte p = (byte)((v * (255 - s)) / 255);
int ht = h * 6;
int d = ht % 360;
ht /= 360;
if ((ht % 2) == 0) {
// ht = 0, 2, 4
byte t = (byte)((v * (255 - s * (360 - d) / 360)) / 255);
switch (ht) {
case 0: return Color.FromArgb(v, t, p);
case 2: return Color.FromArgb(p, v, t);
default: return Color.FromArgb(t, p, v);
}
}
else {
// ht = 1, 3 ,5
byte q = (byte)((v * (255 - s * d / 360)) / 255);
switch (ht) {
case 1: return Color.FromArgb(q, v, p);
case 3: return Color.FromArgb(p, q, v);
default: return Color.FromArgb(v, p, q);
}
}
}
}