前回までのあらすじ
2024年の東京大学数学入試問題の5番を試験会場で解くやり方で解いた。が、「複天一流」の精神に基づいて、再度この問題に挑戦する。といっても答えはわかっているので解答を見つけるのが目的ではなく、立体図形をどのように図示するかという問題について考察を深めていきたい。
スクリーンで3次元図形を表示すること
立体図形を紙やコンピュータスクリーンで表現することは、3次元多様体をその部分多様体である2次元多様体に「射影」することにほかならない。つまり平面への立体の「投影図」をどうやってつくるかという問題である。
しかし、一般の立体図形すべてをここで扱うのは荷が重すぎるし、頑張って挑戦したとしてもかなり分厚い教科書の分量ほどの内容となってしまう可能性が高い。そこで一般的な議論を展開するのは避けて、あくまで「問題5を解く」という観点からの「解決法」の探究を推し進めたい。とはいえ、この先、東大入試では立体図形の問題が少なくとも1題は例年出題されるので、その解決に役立つ程度くらいには「一般性」をもった方法をここで見つけたいと思う。
東大に限らず、入試問題で扱われる「曲面をもった立体」の例は限られている。せいぜい球、あるいは軸対称性をもった楕円体である。一方、実際に出題される多くの「立体」とは、部分多様体、つまり(3次元空間中で)2次元や1次元的「質量」分布をもつ3次元空間中の平面や直線であろう。
「次元の低い」平面や直線だからといってバカにすることはできない。実際、一般の3次元オブジェクトは計算機上では平面で近似されることが多い。これは「ポリゴンを用いた表面モデル」と呼ばれたりする。大抵のビデオゲームなどではこの方法で3次元物体を表現している(有名なのがSony PlayStation 2で、これにより3Dゲームプログラマーになるには線形代数が必須の知識となった)。したがって、3次元空間中で平面を扱う方法をマスターすること自体が、(近似的だが)立体を扱うための近道であり基礎固めなのである。したがって平面の処理を学ぶことが「時間の無駄」とは言い切れない。さらに、3次元空間中における直線に関しても、それは2つの平面同士の共通部分(交線)として表現できるから、やはり平面の扱いが基本となるのである。
ちなみに、ポリゴンとは「多角形」を意味し、3角形や4角形のことである。したがって基本は平面(部分平面)である。たいていの表面モデルは情報量が少ない3角形を用いた計算を行うが、ここでは正方形を用いて「基本平面」を定義してみることにする。デカルト座標を採用するので、その方が平面を定義しやすいからである。とはいえ、正三角形を基本ポリゴンにした場合の基本立体図形は正四面体であり、4つの頂点だけで定義できる。これに対し、正方形を基本ポリゴンとした場合の立方体は8つの頂点が必要となる。情報量が2倍に増えるのはプログラミング的にはよくないことであることは覚えておこう。
平面の方程式
平面は法線ベクトルによって定義される。これを \begin{equation} \boldsymbol{n}=a\boldsymbol{e}_x+b\boldsymbol{e}_y+c\boldsymbol{e}_z\end{equation} と書くことにする。(主に日本の)大学の数学の講義では太字で表す量はベクトルを意味する。$\boldsymbol{e}$はデカルト座標における(それぞれの方向の)単位ベクトルを指すものとする。
表現したい平面内に2つの点P,Qを考え、原点からのベクトル$\overrightarrow{OP}$と$\overrightarrow{OQ}$を考える。2点が平面内にあるということは、$\overrightarrow{QP}$が平面内に閉じ込められているということを意味する。点Qを平面における基準点(原点O'のようなもの)とし定ベクトルとみなす。一方、点Pは自由に平面上を動ける「変数」とする。すなわち、平面上の点は一般に \begin{equation} \overrightarrow{QP} =(x-x_0)\boldsymbol{e}_x + (y-y_0)\boldsymbol{e}_y + (z-z_0)\boldsymbol{e}_z \end{equation} と表現することができる。これは方程式ではなく、単なる表現なので、このままでは平面を同定する方程式とはみなせない。
そこで、この平面と法線ベクトルが直交する、という条件によって方程式を規定することとする。つまり、 \begin{equation} \boldsymbol{n}\cdot\overrightarrow{QP}=0 \end{equation} である。ここで採用したデカルト座標による表現として書き出すと、この式は次の式と等価である。 \begin{equation} ax+by+cz+d=0, \end{equation} ただし \begin{equation} d = -ax_0-by_0-cz_0 \end{equation} 定数項$d$は平面の「基準点」の座標データと法線ベクトルによって決まることがわかる。
情報量が1である(未定)係数$d$を決めるために、情報量3つからなる基準点の座標$(x_0,y_0,z_0)$が必要になるというのは、数合わせがあわないように思える。しかし、切片を考えれば1対1になっていることがわかる。3次元の場合の切片とは、3つある座標のうち、2つの値が必ず0となるということを意味する。たとえば、z切片の場合は$x_0=y_0=0$を意味するから、 \begin{equation} d=-cz_0 \end{equation} となって、法線ベクトルがわかっている時なら$d$と$z_0$は1対1の関係となる。(3次元の場合)切片には3つの自由度があり、z切片だけでなく、x切片でもy切片でもよい。どれか一つ切片がわかれば$d$が決まる。もちろん、3次元の自由度をフルに生かして、3つの座標が非零値をとる「基準点」を利用しても$d$は決められる。状況に応じて自由に利用していい。
法線ベクトルを角度によって指定する
法線ベクトルの3つの座標自体をパラメータとして平面を決めて行ってもよいが、座標軸からの角度を用いて法線ベクトルを規定した方が便利なときもある。回転演算の行列を用いて法線ベクトルを3次元極座標と対応させる方法をここでは考察しよう。
極座標の基本は$\boldsymbol{e}_z$だろう。まずは天頂角$\theta$を使って、この単位ベクトルがz軸からどれだけ「ずれ」たかを表すことにする。z軸の周りの対称性を考えれば、z軸に垂直な軸ならどの周りに回してもいいが、ここは慣習に従ってy軸周りに$\theta$だけ回転させる演算を考える。この場合、z方向の単位ベクトルを回転したベクトル$\boldsymbol{r}(\theta)$は\begin{equation}\boldsymbol{r}(\theta)=\sin\theta\boldsymbol{e}_x+\cos\theta\boldsymbol{e}_z\end{equation}となるはずだ。同じ演算をx方向の単位ベクトルに作用した場合は\begin{equation}\boldsymbol{v}(\theta)=\cos\theta\boldsymbol{e}_x-\sin\theta\boldsymbol{e}_z\end{equation}となるだろう。一方、y方向の方向ベクトルはこの回転に対し不変であることは自明である。したがって、このような回転を実現する演算行列$R_y(\theta)$は次のように書けることがわかる。 \begin{equation} R_y(\theta)=\begin{pmatrix}\cos\theta & 0 & \sin\theta\\ 0 & 1 & 0 \\ -\sin\theta& 0 & \cos\theta &\end{pmatrix} \end{equation}
同様な考え方で、今度はz軸周りに$\phi$だけ回転させることを考える。この演算行列$R_z(\phi)$は次のように書ける。 \begin{equation} R_z(\phi) =\begin{pmatrix}\cos\phi & -\sin\phi & 0 \\ \sin\phi & \cos\phi & 0 \\ 0 & 0 & 1\end{pmatrix} \end{equation}
したがって、$\boldsymbol{e}_z$を最初に$R_y(\theta)$で回転させ、次に$R_z(\phi)$で回転すると、結果的に得られるベクトルは 極座標の角度$\theta,\phi$を向いたベクトルすなわち \begin{equation} \boldsymbol{r}(\theta,\phi) = R_z(\phi)R_y(\theta)\boldsymbol{e}_z=\cos\phi\sin\theta\boldsymbol{e}_x + \sin\phi\sin\theta\boldsymbol{e}_y+\cos\theta\boldsymbol{e}_z \end{equation} 上で定義した法線ベクトル$\boldsymbol{n}$と比較すると、 \begin{equation} a=\cos\phi\sin\theta, \quad b =\sin\phi\sin\theta, \quad c=\cos\theta \end{equation} という対応になる。これが法線ベクトルを極座標で表現する方法である。
「基本平面」の設定
上の回転演算行列はz方向の単位ベクトル(基底ベクトル)に対して極座標が定義されている。この単位ベクトルを法線ベクトルとする平面はz軸に垂直な平面であり、数式で表すと$z=z_0$、ただし$z_0$は定数、となる。
別にこのまま計算をしていっても構わないのだが、これまでの考察では問題5で扱った立体図形を$x=x_0$という平面によって「切断」したので、ここでもその見方を踏襲することにする。つまり、$\boldsymbol{e}_z$ではなく、$\boldsymbol{e}_x$を法線ベクトルとする平面を「基本平面」とし、それを回転行列で傾けることによって任意の平面の方程式をつくる形式に切り替える。xとzの単位基底ベクトルの関係は$R_y$を用いて \begin{equation} \boldsymbol{e}_z=R_y\left(-\frac{\pi}{2}\right)\boldsymbol{e}_x \end{equation} と表せるので、極座標の角度で表される任意の法線ベクトルは次のように生成される。 \begin{equation} \boldsymbol{n}(\theta,\phi)=R_z(\phi)R_y\left(\theta-\frac{\pi}{2}\right)\boldsymbol{e}_x \end{equation} ただし \begin{equation} R(\theta,\phi)\equiv R_z(\phi)R_y\left(\theta-\frac{\pi}{2}\right)=\begin{pmatrix}\cos\phi\sin\theta & -\sin\phi & -\cos\phi\cos\theta \\ \sin\phi\sin\theta & \cos\phi & -\sin\phi\cos\theta\\ \cos\theta & 0 & \sin\theta\end{pmatrix} \end{equation}
基本平面を4つの頂点で定義する
$x=x_0$という平面を、その部分領域である正方形を使って数値計算することにする。これならば4つの頂点座標によって平面を表すことができる。今回はA(0,1,1), B(0,-1,1), C(0,-1,-1), D(0,1,-1)の4点を利用することにしよう。 ここで、計算を簡略化するために基本平面が常に原点を通過するものと定義されたことに注意する。つまり我々の基本平面は$x=0$であり、これをぐるぐると$R(\theta,\phi)$によって3次元回転することで任意の平面を作っていこうというコンセプトである。
たとえば、A(0,1,1)を$R(\theta,\phi)$で回すと、 \begin{align} A\rightarrow A' &=R(\theta,\phi)\overrightarrow{OA} \\ & = -(\sin\phi+\cos\phi\cos\theta)\boldsymbol{e}_x +(\cos\phi-\sin\phi\cos\theta)\boldsymbol{e}_y+\sin\theta\boldsymbol{e}_z \end{align} となる。これと似たような計算をB,C,Dについても行えば、法線ベクトル$\boldsymbol{n}(\theta,\phi)$をもった平面を正方形A'B'C'D'で表現することができる。
まずはgnuplotで描画
gnuplotには、3次元物体の座標データを与えるとそれをコンピュータスクリーン上に投影してくれる機能がすでにある。なにも自分でプログラムを書かなくてもこれをつかえばいいじゃないか、という気持ちはあるだろうが、javascriptやpyscriptを用いてHTML上にその機能を盛り込んだりするプロジェクトを考えているなら、やはり基本的なことは自分で理解しておく必要があるのはいうまでもない。とはいえ、「先輩」から学べることは多々あるので、まずはそれを使ってみようというスタンスを取ろう。まさに「複天一流」の極意である。
A',B',C',D'の座標を計算し、gnuplotに盛り込んだのが次のスクリプトである。
unset arrow set size square set view equal xyz PI=4.*atan(1.0) aq = 80. af = 0. qa=aq*PI/180 fa=af*PI/180 ax0 = cos(fa)*sin(qa) ay0 = sin(fa)*sin(qa) az0 = cos(qa) ax1 = -sin(fa) ay1 = cos(fa) az1 = 0.0 ax2 = -cos(fa)*cos(qa) ay2 = -sin(fa)*cos(qa) az2 = sin(qa) bAx = -sin(fa) - cos(fa)*cos(qa) bAy = cos(fa) - sin(fa)*cos(qa) bAz = sin(qa) bBx = sin(fa) - cos(fa)*cos(qa) bBy = -cos(fa) - sin(fa)*cos(qa) bBz = sin(qa) bCx = sin(fa) + cos(fa)*cos(qa) bCy = -cos(fa) + sin(fa)*cos(qa) bCz = -sin(qa) bDx = -sin(fa) + cos(fa)*cos(qa) bDy = cos(fa) + sin(fa)*cos(qa) bDz = -sin(qa) set arrow from 0,0,0 to 1,0,0 set arrow from 0,0,0 to 0,1,0 set arrow from 0,0,0 to 0,0,1 set arrow from 0,0,0 to ax0, ay0, az0 lc 2 set arrow from 0,0,0 to ax1, ay1, az1 lc 4 set arrow from 0,0,0 to ax2, ay2, az2 lc 7 set arrow from 0,1,1 to 0, -1,1 nohead set arrow from 0,-1,1 to 0,-1,-1 nohead set arrow from 0,-1,-1 to 0,1,-1 nohead set arrow from 0,1,-1 to 0,1,1 nohead set arrow from bAx, bAy, bAz to bBx, bBy, bBz nohead set arrow from bBx, bBy, bBz to bCx, bCy, bCz nohead set arrow from bCx, bCy, bCz to bDx, bDy, bDz nohead set arrow from bDx, bDy, bDz to bAx, bAy, bAz nohead m1z = -sin(fa)*tan(qa) set arrow from 0,1,m1z to 0,-1,-m1z nohead splot [-1:1][-1:1][-1:1] "origin.dat" w p
今回は、set arrowというコマンドを初めて利用してみた。postscriptをプログラミング言語としてみたとき、直線を描くコマンドが設定されているので実は「線」こそ描きやすいオブジェクトである。gnuplotはpostscriptに準じた実装がなされていると思うので(未確認だが)、実はplotやsplotよりもset arrowの方が扱いが簡単なのかもしれない。これまでこのコマンドを知らなかったのは「損失」であると反省した次第である。ただ、set arrowだけでは描画ができないので、原点だけを記録したデータファイルをダミー的に準備し、それをsplotで描画するという形でarrowつまり線分を描画する形式を今回は用いてみた。
スクリプトの内容を簡単にみておこう。 aqが$\theta$、afが$\phi$の値に相当する。基本平面は$x=0$であるが、これは$\theta=90^\circ, \phi=0^\circ$に相当する。上のスクリプトではaq=80, af=0にセットしてあるので、Z軸方向から若干回転させた法線ベクトルをもつ平面が生成される(下図)。
生成した平面の法線ベクトルは緑色の矢印で表現した。また、平面内の直交軸2本は横軸(y軸に相当)は黄色、縦軸(z軸に相当)は赤色で表した。y軸の周りに少しだけ回転したので、垂直の状態から「上目線でのけぞっている」のが確認できるだろう。
さて、gnuplotでは任意の角度から立体を観察することができ、その操作は矢印キーでできる。最終的にはそのような機能を盛り込みたいとは思うが、まだ敷居が高いので、そこはおいおいやっていくことにして、まずはこの図形をy軸の+方向から眺めてみることにする。gnuplotで表示すると次の図のようになる。生成した平面をわかりやすくするように黄色の太線で囲ってみた。基本平面($x=0$)は黒線で囲んでいるが、この方向からみると鉛直直線(線分)に見える。
今度は+x軸の方向からみてみよう。図が重なってしまうため、ほんのちょっとだけz軸およびy軸周りに回転した方向から眺めている。
さらにz軸周りに回転した方向から観測すると基本平面と生成された平面のずれが少しずつ目立ち始め、すこしだけわかりやすくなる(次の図)
すでに上の方で計算した結果を利用すれば、我々もgnuplotの3次元プロット(splotコマンド)のような図形を「2次元図形」として打ち出すことが可能である。