複天一流:どんな手を使ってでも問題解決を図るブログ

宮本武蔵の五輪書の教えに従い、どんな手を使ってでも問題解決を図るブログです(特に、科学、数学、工学の問題についてですが)

東京大学2023数学[3] part-3: gnuplotで図を量産する方法

前回のあらすじ

東大2023数学入試問題のうち、図形を扱う問題3に突入したのだが、とりあえずグラフを図示してみようということで、gnuplotによる描画を始めたのであった。問題に導入されている円Cの位置を表すパラメータ$a$を様々に変化させる「シミュレーション」もどきを行い、問題の概要をつかもうとしたわけであるが、考察が長引き、構成がぐちゃぐちゃになりはじめたところで、「いったん落ち着こう」ということになり、休憩をとることにした。

シミュレーションのまとめ

話がぐちゃぐちゃになり始めたので、まずはシミュレーションの結果をまとめてみたいと思う。

まず、状況は(1) 円Cと放物線Bが交点を持たない場合と、(2)交点を持つ場合に大別される。

さらに(1)の状況は(1-a)円Cが放物線の上に来る場合(C>Bと表現)と、(1-b)CがBの下に来る場合(C<B)の2つに細分される。

一方(2)の状況は(2-a)BとCが一点で接する場合、(2-b)2点で交差する場合、(2-c)3箇所で交点を持つ場合(ただし、そのうち1点は接点)、(2-d)4点で交差する場合の4つのケースに細分される。

それぞれの場合に属する$a$の具体的な値はいくつか見つけてあり、その結果を利用してgnuplotで描画することはできている。ただ、問題に答えるためには、その値の範囲を決定しなくてはならないのだが、そこまでは到達していない、というのが現況である。

ということで、シミュレーションの結果見つかった、それぞれのケースに対応する値を用いてグラフを描いてみる。

(1-a) 円が放物線の上に来る場合(C>B)

この状況こそが「探せ」と問題文で指示が出ている状況である。$a$の値を大きくとればとるほど簡単に見つかるが、 ここではできるだけ下限に近い値と思われる$a=1.5$の場合を表示させる。

$a=1.5$の場合

(1-b) 円が放物線の下に来る場合 (C<B)

この状況は、(1-a)の状況と同じように、CとBが交点を全く持たない。しかし、題意を満たさない。円Cが放物線Bの下に来てしまうからである。この場合は、$a$を負の大きな値にすればするほど簡単に見つかるが、ここでは$a=-1.5$の場合を表示してみた。

$a=-1.5$の場合

(2-a) 円Cと放物線Cが一点で接する場合

ここからは交点、あるいは接点を持つ場合をみてみる。以下では「接点」のことも「交点」に含むことにする。

(1-b)の状況から、$a$を少しずつ増加させていくと、このケースにたどり着く。簡単な幾何学的考察から、それが$a=-1$の場合であることはすぐにわかる。無論、このケースも題意を満たさない。

$a=-1$の場合

(2-b) 2点で交差する場合

(2-a)の状況から少しずつ$a$を増加させた時に見つかる状況である。まずは$a=-0.5$の場合を見てみよう。

$a=-0.5$の場合

$a$の値をいろいろに振って調べてみると、結構な範囲でこの状況が続くことがわかる。$a=0$も$a=0.5$もこの状況に属することは、gnuplotでシミュレーションしてみると確認できる。

2つの交点は、y軸に対称的な点、つまり$x$と$-x$という関係になっていて、前回の分析では「ペア(対)」と呼んだ。これは、円Cと放物線Bが、y軸に対して対称的な形をもっていることに起因する。

(2-c) 3点で「交差」する場合

「交差」といっても、1点が接点($x=0$)であり、残りの2点が「ペア」である($x=\pm 1$)。 この状況は$a=1$の場合のみに発生することが、(やはり)幾何学的な考察からわかる。

$a=1$の場合

(2-d)4点で交差する場合

2つの「ペア」が発生し、合計で4つの交点を持つ場合である。この状況が発生する$a$の上限値を求めることが、問題を解くことに相当する。つまり、上限値においては、2つのペアが「2つの接点」に集約し、その後、交点が消失するからである。

この状況は$a=1$から少しずつ$a$の値を増加させると見つけることができる。ここでは$a=1.1$の場合を図示する。

$a=1.1$の場合

gnuplotでシミュレーションの図を大量に生成させる方法

さて、上に掲載した複数のグラフの図であるが、パラメータを手入力で少しずつ変えながら印字させるのは、なかなか骨の折れる仕事である。そこで、gnuplotスクリプトのように利用して、系統的に様々な$a$の値について作図させたい、という欲求が湧き上がってきた。いろいろ調べると、どうやら可能なようである。さっそく「複天一流」の精神に基づいて、やってみることにした。

まず見つかったのが、シェルスクリプトgnuplotスクリプトを複製しつつ、sedawkで$a$の値をおのおの書き換えるというものであった。が、できればそういう「外部機能」は使わずに、gnuplotの機能だけでやってみたい。ということで、この系統の資料は無視することにした。

検索を効率よく行うために、「仕様書」のようなものを描いてみることにした。

  1. $a$の値を変えながら、複数個のグラフを作図する。
  2. $a$の値に応じて、異なる名前のファイルに出力したい。

基本的にはこれだけである。ということで、まずは繰り返しをしないバージョンのgnuplotスクリプトを書いてみる。

set term qt
set size square
set key at 3.0, -2.5 font "Courier,15"

a=-1.5

set param
plot [][-3:3][-3:3] \
cos(t), sin(t)+a title "a=-1.0" , \ 
t, t**2 title "Parabola"

$a=-1.5$にセットしたので、(1-b)で載せた図と(基本的には)同じものが作図されるはずである。

(1)の仕様を満たすために、$a$の値を動かしたい、というのが次のステップである。gnuplotの最高の教本は「デモスクリプト集」であろう。今回はこちらを利用した。変数の値を変えていきたいので、do forの構文を利用してみた。

set term qt
set size square
set key at 3.0, -2.5 font "Courier,15"

set param
do for [n=0:6] {
  a=0.5*n - 1.5
  plot [][-3:3][-3:3] \
   cos(t), sin(t)+a title "a=-1.5" , \ 
   t, t**2 title "Parabola"
  pause 0.2
}

このスクリプト(plot.gpなどと名付けて保存する)を実行すると(gnuplot plot.gpなどとする)、$a=-1.5$から$a=1.5$までのグラフを0.5刻みで作図し、次々とスクリーン表示してくれる。アニメーションのようである。

ここで不満がひとつ生じた。グラフのラベル(タイトル)が$a=-1.5$に固定されている点である。これを$a$の値に応じて変化させたいものである。

まずは繰り返し変数$n$を使って書いてみる。

set term qt
set size square
set key at 3.0, -2.5 font "Courier,15"

set param
do for [n=0:6] {
  a=0.5*n - 1.5
  plot [][-3:3][-3:3] \
   cos(t), sin(t)+a title "n=".n , \ 
   t, t**2 title "Parabola"
  pause 0.2
}

gnuplotでは、2つの文字列の結合は"."(ピリオド)で行うという決まりがあるようだ(参考文献はこちら)。実行してみるとわかるように、このスクリプトは機能する。

ここで調子に乗ってタイトルの部分を

plot .....  title "a=".a 

に変えてみたら、エラーが出た。なぜだろう?

"plot.gp" line 10: internal error : STRING operator applied to undefined or non-STRING variable

英語のエラーメッセージの意味は、正直「意味不明」である。

このエラーを回避するのに数時間を要してしまったのだが、よく読むと最初の参考文献に書いてあったのだ。つまり、文字列に利用できる変数としては「整数」だけなのである。整数を文字列に連結した場合は自動的に整数を文字列に変換してくれるのだそうだが、小数(実数)の場合は自動変換してくれないのであった.....。

ここでさらに苦労したのであるが、先ほど参考にしたiteration.demに解決法が書いてあったのである。どうやら

sprintf("%3.1f",a)

という命令を利用する必要があるらしい。小数(実数)を文字列に変換してくれる命令だそうだ。したがって、正解は

plot .....  title "a=".sprintf("%3.1f",a) 

ということになる。

次はいよいよ(2)の仕様、つまりファイル名を$a$の値に応じて変えつつ、そこに結果を保存する、という機能である。

set term png
set size square
set key at 3.0, -2.5 font "Courier,15"

set param
do for [n=0:6] {
  a=0.5*n - 1.5
  set output "Result-a[".sprintf("%3.1f",a)."].png"
  plot [][-3:3][-3:3] \
   cos(t), sin(t)+a title "a=".sprintf("%3.1f",a) , \ 
   t, t**2 title "Parabola"
}

これで、一応は最初の目標を実現することができた!

一枚だけ、このスクリプトで生成したグラフをここで表示しておこう。

$a=1.0$の場合

gnuplotでシミュレーションさせるための「繰り返し命令」が利用できるようになった。この技術は今後あちこちで役立つことだろう。