複素数の魔法

複素数を余裕でサポートしているMathematicaは、2Dグラフィックスをより簡単に処理できます。

## 座標としてリストを用いた星形の描画

例として星形を使います。
星形を描くには次のように組むとある程度すっきりするでしょう。

```mathematica
listPts = Table[{Cos[a Pi], Sin[a Pi]}, {a, 0, 4, 4/5}]
ListLinePlot[listPts, AspectRatio -> Automatic]
```

<img width="496" alt="mathematica_star_list.png" src="https://qiita-image-store.s3.amazonaws.com/0/6117/de2bea28-0dc9-1662-3ef3-2a53c52c5ba6.png">


### 平行移動

これを右に1、上に2だけ平行移動するには、以下のように座標 $(1, 2)$ を足せばいいでしょう。
``listPts``に直接``{1, 2}``を足すのも直感的に行けそうな気がするかもしれませんが、次元が合わないのが駄目なようなのでやむなくリストのそれぞれに適用させます。

```mathematica
listPts2 = (# + {1, 2}) & /@ listPts
ListLinePlot[listPts2, AspectRatio -> Automatic]
```

<img width="575" alt="mathematica_star_list_translate.png" src="https://qiita-image-store.s3.amazonaws.com/0/6117/21157d33-9622-bbbb-c042-5607eec3cd4e.png">


### 回転

ベクトル解析をやったことのある人はわかると思いますが、
ベクトルをアフィン変換（回転、拡大縮小など）するには行列を掛ける必要があります。

Mathematicaには回転行列などを求める関数があるので比較的楽に行えます。

```mathematica
listPts2 = (RotationMatrix[30 Degree].#) & /@ listPts
ListLinePlot[listPts2, AspectRatio -> Automatic]
```

<img width="583" alt="mathematica_star_list_rotate.png" src="https://qiita-image-store.s3.amazonaws.com/0/6117/82fce0da-ec99-7750-1432-39288dfe4887.png">


が、これもちょっと面倒だと（僕は）思います。



## 座標として複素平面を用いた星形の描画

複素平面上の複素数として座標値を扱うと、魔法のように扱いやすくなります。

```mathematica
compPts = (-1)^Range[0, 4, 4/5]
{Re[#], Im[#]} & /@ compPts
ListLinePlot[{Re[#], Im[#]} & /@ compPts, AspectRatio -> Automatic]
```

<img width="494" alt="mathematica_star_complex.png" src="https://qiita-image-store.s3.amazonaws.com/0/6117/e2425dd9-0721-0209-fbaa-b3040eac3c5d.png">


``{Re[#], Im[#]} & /@ compPts``が先ほどの``listPts``に相当します。

でも、まだ有り難みが感じられませんね。
では、以下の場合はどうでしょう。


### 平行移動

座標 $(1, 2)$ に相当する複素数 $1+2i$ を足すだけです。

```mathematica
compPts2 = compPts + (1 + 2 I)
ListLinePlot[{Re[#], Im[#]} & /@ compPts2, AspectRatio -> Automatic]
```

<img width="662" alt="mathematica_star_complex_translate.png" src="https://qiita-image-store.s3.amazonaws.com/0/6117/988f2883-57fb-12be-3e75-41fed8d2dc35.png">


### 回転

30度（$\frac{\pi}6$ ラジアン）回転は、複素数 $e^{\frac{\pi}6 i}$ を掛けるだけです。

```mathematica
compPts2 = E^(Pi/6 I) compPts
ListLinePlot[{Re[#], Im[#]} & /@ compPts2, AspectRatio -> Automatic]
```

<img width="480" alt="mathematica_star_complex_rotate.png" src="https://qiita-image-store.s3.amazonaws.com/0/6117/31ab3b14-5e33-3752-adc7-7681517b8f6f.png">


### 複素数でできること

アフィン変換のうち

- せん断（skew）
- アスペクト比が変わる伸縮

は難しいですが、

- 平行移動
- 回転
- 拡大縮小（→定数倍で可能）

は四則演算の組み合わせだけで行えます。


## Complex

複素数は

```mathematica
In[1] = FullForm[1 + 2 I]
Out[1] = Complex[1, 2]
```

となるように、頭部がComplexとなった要素で、
実部（``Re``）と虚部（``Im``）を取り出すには先ほどのように

```mathematica
{Re[#], Im[#]} &
```

という関数を通せばリストとして返ってきます。

Mathematicaにそこそこ詳しい人なら、

```mathematica
List @@ (1 + 2I)
```

とやって頭部``Complex``を``List``に置き換えればいいじゃん、と
思いつくかもしれませんが、何故かその手は禁じられています。

```mathematica
(1 + 2I)[[{1, 2}]]
```

これも駄目です。

逆に、

```mathematica
Complex @@ {1, 2}
```

とやってリストを複素数にすることは可能です。

いっそのこと

```mathematica
reim[c_?NumberQ] := {Re[c], Im[c]}
reim[li_List] := {Re[#], Im[#]}& /@ li
```

とか関数作っちゃってもいいかと、記事を書きながら思いましたが、
もはや``{Re[#],Im[#]}&``の記述に慣れすぎてるので有用かどうかわかりません。

追記2018-12-15: バージョン10.1から `ReIm` 関数が導入されています。まんま！
[ReIm—Wolfram言語ドキュメント](https://reference.wolfram.com/language/ref/ReIm.html)

## 本当、役に立ちます。

来週執筆予定のコードゴルフ連載でも頻発することになると思いますが、
座標をリストとせず複素数とすることにより、一数値扱いになってある意味カプセル化的な役割を果たしているので、たとえば``Map``（``/@``演算）や``Flatten``を多用するときに、リストよりかなり都合がいいのです。
（場合によっては``Complex``や``List``の代わりに``Point``を使ってもいいかもしれませんが、あくまでもグラフィックスプリミティブなのでちょっと違う気がします。）

騙されたと思ってやってみてください。