虚数はわからなくていい

安原祐二氏(以下、安原):ここでは、複素数の話をしたいと思います。がんばって聞いてください。虚数の話になりますが、虚数単位のiというものをみなさんも聞いたことがあると思うんですよね。でも、なんのことかがわからない人も多いかと思います。

これは、ルートの中にマイナス1が入っているという定義だったり、2乗するとマイナス1になるよという定義だったりするのですが、意味がわからなくて当然というか、これがわかるという人がいたらちょっと信用しないほうがいいと思います。これ、わからないんですよね。わからなくていいんです。わからなくていいという話をします。

複素数の足し算と掛け算

その前に、虚数がなぜ便利かというと複素数があるからです。複素数は何かというと、実部と虚部があって、虚部にはiが掛かってるという、(スライドを示して)こういう2つの数字を足したものです。例えばzが複素数だと言われた時は、その中に実部と虚部があります。aという実部と、bという虚部があるんだなとイメージをすることが大事です。

複素数の足し算を考えてみましょう。z0とz1という複素数があって、その足し算はどうなるかというと、これはぜんぜんなんてことはありません。実部同士を足して、虚部同士を足して、それぞれ実部と虚部にすれば完成です。ぜんぜん難しくありません。

ちょっとややこしいのが掛け算かなと思います。掛け算なので展開をしていきますが、このiの2乗がちょっとややこしいポイントというか、これがマイナスに変わるんですよ。そのことだけに注意をすれば、掛け算は機械的にできるかなと思います。

複素数の足し算と掛け算をプログラムで実装する方法

これを理解しようと思った時に有効な方法があります。それはプログラムで実装してみることです。やってみましょう。

複素数は英語でComplex Numberと言います。Complexのreとim、つまりReal PartとImaginary Part、虚部と実部の2つの変数を宣言しておいて、じゃあ足し算はどうするの? といったら、なんのことはありません。実部同士、虚部同士を足して、それぞれ実部と虚部に入れるだけです。ぜんぜん難しくないですよね。これはわかりやすいと思います。

では、掛け算は難しいかというと、ちょっと複雑なだけです。先ほどの式に忠実に入れていくだけです。マイナスが1個現れるのに注意しておけばいいです。これで足し算と掛け算を実装すれば、Complexの実装はほぼ完了です。これはぜんぜん難しくないので、ぜひ練習としてやってみてもらいたいと思います。

これを自分でやると気づくことがあります。いいですか。iはどこにも登場していないんですね。つまりiというのは、虚数というよりも計算で使う作用と考えたほうがわかりやすい、と僕は思います。ここに囚われるよりも、足し算および掛け算でルールに使用されているんだなと思っちゃえば、ぜんぜん難しくなく、こういうもんだなと思えます。

複素平面で考える、複素数の足し算と掛け算の概念

というわけで、複素平面というものがあります。実軸を横に取って、虚軸を縦に取るというのがあって、複素平面だと、zがあったらこんな点が表現できます。

では、足し算を複素平面上でやってみようと思った時に、z0とz1があったとしましょう。この2つを足したいと思った時に、(スライドを示して)こうなるわけですね。z0から始めたら、z0にz分平行移動するわけです。

逆から行ってもいいですが、z1から始めた場合、そこからz0分平行移動するとこの2つの足し算になります。

掛け算の概念がちょっとややこしいですが、半径1の単位円を考えたとしましょう。たまたま今z0が単位円の上に乗っていて、z1は乗っていないとしましょう。

この2つを掛ける時に考えなきゃいけないのは、z0は実軸に対して角度があるということで、この角度が大事です。あと長さがあります。r0とθ0というものがあります。

そして、z1に対してもあります。これは下側に行っているので、角度としてはこういう大きい大回りしたものか、マイナスの符号を付ければ逆回転してもいいのですが、そういうものになります。

この2つを掛けるということは、角度は足し算なのですね。z0の角度はだいたい60度ぐらいだったじゃないですか。それで、z1はマイナス30度ぐらいかな。なので、新しくできたz0、z1の角度は、30度ぐらいになるわけです。z0の長さはたまたま1で、長さは掛け算になるのでz0、z1と掛けた結果の長さはz1の長さと一致するわけです。こんな性質があります。

Unityでは回転だけに限定している複素数の拡張・クォータニオン

複素数の話をしたいのは、クォータニオンというものがあるからです。「クォータニオン完全マスター」という、非常によくできた解説動画があります。自信作です。3年ぐらい前の講演ですが、今見てもわりとおもしろいし役に立つと思うので、ぜひ見てみてください。

動画では、クォータニオンは回転だという話をしているのですが、実は、クォータニオンそのものは大きさを変える性質も持っています。ただUnityは回転だけに限定しているので、大きさのところはキャンセルするように働いています。これをちょっと覚えておいてください。

なので、もともと複素数やクォータニオンは、拡大する能力があるわけです。すごく単純なジェネラティブアートを作ったとしましょう。今回はアーティストの方も来ていると思いますが、すごくシンプルなジェネラティブアートはこんな感じです。

今、repeatと書いていますが、ここを毎フレーム実行すると思ってください。そうすると、最初の値が(1f,0f)。実部が1で、虚部が0だったとして、それに対して(0.01f,0.25f)のComplex Numberを掛け続ける。足していくわけですね。これは漸化式になるわけです。

そうしていくとこんなふうに渦巻きが描けるので、この数字を変えるといろいろな渦巻きが出ます。小さなコードで絵が出るとうれしいですよね。Complex Numberにはこういう使い方があります。

教養として覚えていたい「マンデルブロ集合」

もう1つ、これは教養として覚えてもらうと、けっこうかっこいいので覚えてください。マンデルブロ集合という有名なものがあります。最近はレイマーチングなどが有名なので、みなさんそっちに走ると思いますが、こっちのほうが古典的で基本なので、これは押さえておきたいですね。

これは実は、複素数の概念で作られています。複素数の漸化式ですね。前の値を2乗して、cという複素数を足したのを次の値にするという操作。これがマンデルブロ集合です。

もうちょっと詳しく説明します。初期値を0にして、cにいくつを与えるかで値がすごく大きくなって返ってこなくなる、あるいは0になって消えてしまう。このどちらかを調べるのが、マンデルブロ集合を作るということです。

なので、複素平面上で表示したい画素を全部調べます。だいたい単位円の中は収束していきますが、外にあっても収束するやつがいるんですね。発散するやつもいます。

収束・発散はどうやって調べるかというと、何回か回して大きな値になったら、「ああもうこれは発散だね」と、見切っちゃうわけですね。そういう操作をプログラム上でやります。そうすると、ポイントによってはすごいスピードで発散して、あっという間に返ってこなくなったり、すごく粘って発散しちゃったりするんですね。その何回回して発散するかというのを覚えておくと、マンデルブロ集合を描画する時にグラデーションを作れます。

プログラム例はこんな感じになります。すごくシンプルですが、赤枠で囲ったところが複素数の計算です。この中では先ほど説明したような複素数の掛け算、および足し算が実装されていて、要するに100回ループして、これがある値よりも大きくなるかどうかだけをチェックします。それでこの関数の引数のcが、自分の調べたい点の座標を入れていくわけです。

右に描いてある絵は、Panel、Quadが回転するスピードをマンデルブロの計算結果にしたものです。

これはDOTSテクノロジーですね、こんな感じでたくさんものを出してますが、100掛け100のパネルをくるくる回すスピードをマンデルブロ集合で変えてみると、実験としてはおもしろいですよね。

なので、パネルを回すだけでもいろいろな模様が出るよねという話です。甲子園とかで、アルプススタンドでパネルをこう広げて絵を描く方がいますが、高校生のみなさんもマンデルブロ集合をアルプススタンドに描いたら、僕はすごく盛り上がりますね。

(会場笑)

「おっ、キター!」みたいになりますね。やっている人は気づかないみたいな、やらせてる人だけが楽しむみたいな。おもしろいですよね。

という話でした。ここはまとめはなくて、パート7はここまでとなります。

複素平面で一般項は出せるのか?

最後に、パート8です。複素数列です。最後なので僕の自由研究を披露したいと思っています。けっこう複雑な話になってしまったのでちょっと流しちゃいます。ブログに詳しく書いたので、気になる方はご覧ください。

パート1から7までは指数、対数、複素数、数列など、そういうものをお話ししました。

自由研究は、こういうふうにくるくる回るものを、複素平面で一般項を出せるんじゃないかとチャレンジした熱い戦いになります。

やっていくためには、パネルに番号を振ります。1が中心にあるのがわかるでしょうか。2、3、4と追っていくと、渦巻きになっているのがわかりますよね。

複素平面上で描くとこうですね。左から見てください。6のところ。関数に6を入れたら、マイナスiが返ってきてほしいわけです。あるルールに従って13を入れたら2+2iが返ってくるような、そういう一般項を出せれば僕の勝利ということになります。

こうやって4つに分解して、群数列という数列の概念があるんですが、1つ1つの項目を考えていきます。

さらに4つあるので、rという値を持ってきて、0、1、2、3、スタート地点がそれぞれこうなっていて、複素平面上ではこうなってるよねと。

(スライドを示して)ちょっと小さいですが、最初の点は1だった、その右下は2-iだったみたいな点があります。これを90度回すと2個目のスタート地点になります。90度回すということは、iを掛ければいいことになるわけです。

そんな感じでもうほとんど根気だけでやっていきました。結果はこれです(笑)。

(会場笑)

ちょっとしんどい感じになりますが、これは合っているんですね。合っているので、僕はこの渦巻きをいくらでも描ける能力を手に入れました。という話でした。

この話はまだ続きがあります。これでもできるんじゃないの? と考えてみました。

これも6つに分けて、群数列を考えてがんばっていくわけです。それで、結果はこうですね。1個に書くと死ぬのでバラバラにしましたが、いろいろな文字を使えばできる感じでした。

光っている先頭の点が全部連続しているので、ずーっと追っていくと、途切れずに真ん中に吸い込まれていくまで追っていけるんですね。これをどっかに飾っておくと、たぶん小学校時代の僕だったら3時間ぐらい見ているんじゃないかなぁという気がします。

というわけで、まとめにはブログのURLしか書いていませんが、パート8はこれで終わりです。