複素整数のわたし的自然な数値フォーマット
複素整数のわたし的自然な数値フォーマット
プログラミングで複素数を扱おうと思ったら、どう実装しますか?
2024年現在、今でなお複素数をネイティブでサポートしている言語はまだそんなに多くないので、ライブラリを探すか自分で実装するかのどちらかを選択することになるかと思います。
ライブラリとしては、PythonならNumPy、C言語なら<complex.h>、JavaScriptならMath.jsあたりがメジャーそうです。
ライブラリにしろ自分で実装するにしろ、複素数はa + b iのかたちで表せるので「実部と虚部をそれぞれ変数に格納する」が十中八九のソリューションじゃないでしょうか。
否、複素数全体ではなく複素整数(ガウス整数)ですが、実部と虚部をあわせてひとつの整数として扱ってしまったほうが、原理的には計算上簡単になる、という魔法のような数値フォーマットを発明したのでご紹介します。
愚直な複素平面
アイデアはほぼ、私が過去に日曜数学会で「剰余の奇妙な平面」として発表した原理に基づきます。
符号化の第一歩は、iに相当する数を割り当てたうえで、複素整数に対応する整数を対応付けることです。
たとえばi=8とした場合、以下のように原点で折り返される整数の割り当てになります。
ここで、負の数にゲタを履かせて正の数にします。
この工程において、「自然に」を意識してやると大抵はこうなるんじゃないかと思います。
2進数で表現するとこうなります。
こうやって見ても、前半ビットと後半ビットで実部と虚部が分かれていて、申し分ないように感じられます。
しかしどうでしょう、i=8なのでと、i^2 = 64になっているかというとそうはなりません。なぜなら、この平面は mod 64の剰余として64 ≡ 0を基本としているからです。
まあ、複素数を整数としてみなすなんて無理矢理なので、計算の辻褄があうなんてことは期待しないほうがいいですよね…。
オメガ平面
いいえ!じつは計算の辻褄が合う方法があるんです。
ひとつだけずらして、こうです!
先に別の表現を入れてしまったので複雑な作り方に思えてしまいそうですが、
の複素平面にi=8と無理矢理代入してmod 65として0〜64の範囲に収めたに過ぎません。
こうすることによって、なんと
なぜかというと、虚数単位の原理がそのまま通用するのです。
ということで、i=8だったら8×8+1=65の剰余とすればいいんですね。
今のところ欠点もいくつかあって、先程と同様に2進数で表現したとき
と、パッと見で読みづらくなるのはまだしも、-1だけのために1ビット余計に必要になるのが計算機実装において足かせになりそうなのです。
ただ、数学的に捉えれば、「i進数」において-1が特別な値として考えられる、ということも示唆しています。
なお、-1が余剰になった代わりに、この場合は28が欠番になって、私の研究でこの28などのことを「Ω」と呼んでいたことからこの平面のことを「オメガ平面」と命名した次第です。
計算機実装
今回、簡単に実装したものではありますがJavaScriptとC言語でエンコード・デコードする処理を実装しました。具体的なコードが気になる方はこちらのGist投稿を御覧ください。
特にJavaScriptでは独特の旨味があります。数値として解釈されるインスタンスに +
や *
の演算を施したときにはあくまでも数値の加算や乗算として扱われ、つまり演算子のオーバーライドによる自前の関数に持っていけないという縛りがあるので、加算と乗算の整合性が(一定の範囲内ならば)取れているという凄みがあるのです。
なので、JavaScriptでの今回の実装からすると
const c1 = new IntComplex8(x1, y1)
const c2 = new IntComplex8(x2, y2)
const c = IntComplex8.from(c1 * c2)
のように計算してしまえます。
ただ、この「(一定の範囲内ならば)」がむしろ微妙なところで、おそらく浮動小数点数などの数値フォーマットもそうなんでしょうが、結局演算結果の処理がやはり大変になってしまうので、一筋縄に「簡単なフォーマット」とは言い切れないのが我ながら苦しいところではあります。
しかし、イメージとしては整数フォーマットに「2の補数」の概念を導入して計算が楽になるといった恩恵がかなり大きいので、このi^2+1の剰余を用いるオメガ平面の符号化が標準規格になる未来はだいぶあり得ると自信を持っています。
私は今後も、数学的原理を工学に適用して「より自然な計算機」の実現を目指します。