ハローラスク

absent-minded

2018年度センター試験数学IAをPythonで解く

実行環境はJupyter Notebook 5.0.0 (+ Python 3.6.0) です.
Jupyter上でSymPyのinit_printing()を使うと出力の数式が自動でTeX表記になってとても良いです.

問題: https://www.toshin.com/center/q/sugaku-1a.pdf
解答: http://www.dnc.ac.jp/albums/abm.php?f=abm00011353.pdf&n=01数学ⅠA.pdf


f:id:HelloRusk:20180206155124p:plain


解説
ア: 5
SymPyのexpandで多項式を展開しています. 問題文で与えられた式の形と微妙に違いますが, n の係数が分かればいいので気にしないことにします.
イ: 6, ウエ: 14
上手いやり方が分からなかったので, しらみつぶしに式が一致するものを探してみました. if solve(B) == solve(A): で, B = 0 と A = 0 の解が一致しているかどうかを調べることにより, 実質的に B = A が恒等式になっているかどうかを判定しています.
オ: 2, カ: 8
subsで値を代入しているだけです. roundで四捨五入しています.


f:id:HelloRusk:20180206200229p:plain
f:id:HelloRusk:20180206200240p:plain


解説
キ: 2, ク: 0
set型を使って素直に解けます. ちなみにですが, 高校数学では ⊂ の記号は真部分集合ではなく単に部分集合を指すのでプログラムの if A <= C: は誤りではないです.
ケ: 2, コ: 0
前の問題と違って実数集合が相手なのでどうしようもないです. とりあえず-100から100まで0.1刻みで x に値を代入して反例があるかを調べる, という酷い方法でやっています.
もっと正確な方法として, matplotlibで数直線のような図を出力して包含関係を確認する, というのも思いついたのですが, それは結局人間が解いているような気がするので採用する気になれませんでした. もっと良い方法を知っているよという方がもし居たらTwitterとかで教えてください.


f:id:HelloRusk:20180207154158p:plain


解説
サ: 1, シ: 3
与えられた関数の x の(偏)導関数が0になるような x の値を直接求めています. SymPyのdiffで微分を求めることができます. 最後にexpandを使うことで問題文で与えられた式の形に合わせています.
ス: 1, セ: 1
まともな受験生であれば, 放物線の軸と区間の位置関係で場合分けをするところですが, 候補が a = 1 〜 9 のたった9個しかないので全部グラフを描いてしまいました. グラフを見ると, x = 4 で最小値をとっているのは a = 1 のみで, しかもグラフの頂点が x = 4 に来ている気がするので, a = 1 以外の答えはあり得ないでしょう.
ソ: 4, タ: 5
f(4) が 1 となるような a の値を求めているだけです. 解の候補も1つしかありません.
チ: 7, ツテ: 13, ト: 4
f(p) が 1 となるような a の値を求めているだけです. 解の候補が2つありますが, 1 ≦ a を満たしているのは2つ目だけなので, それが答えになります(問題文から符号は明らかなのですが). 最後にfactorを使うことで問題文で与えられた式の形に合わせています.


f:id:HelloRusk:20180207174555p:plain
f:id:HelloRusk:20180207174605p:plain


解説
ア: 7, イ: 9, ウ: 4, エ: 2, オ: 9
実を言うと当初は「ここで余弦定理を思いつくのは人間がやらなければならないことだろう」と思っていたのですが, 第5問を解き始めた後から, これは余弦定理を知らなくても解けることに気づきました.
流れとしては, まずSymPyのGeometryモジュールのTriangleを用いています. Triangleは, 例えば Triangle(sss=(3, 4, 5)) とすると, Point(0, 0), Point(3, 0), Point(3, 4) を返します. すなわち長さが3の辺が x 軸上に来るように, そして原点に頂点の1つが来るように3辺の長さが 3, 4, 5 の三角形の頂点を配置してくれます. これを利用することで, 点Bが原点, 点Cがx軸の正の範囲にあるように三角形ABCを置き, そのときの点Aの座標を求めることができます. その x 座標を AB の長さで割れば cosABC が, y 座標を AB の長さで割れば sinABC が得られます.
ただし, cosABC を普通に求めようとすると循環小数になってしまいます. これは sinABC では分子に根号(sqrt)が登場するのでSymPyのMul型(ルートや分数を小数にしようとせずそのまま残してくれる型)として扱ってくれるのですが, cosABC では分子が有理数なので普通のfloat型として扱ってしまうことが要因だと思われます. そこで cosABC では一度Fraction型というものを用いています. これについては第3問の解説の方が詳しいのでそちらを参照してください.
カ: 0
普通に値を比較しています.
キ: 4
誘導を無視しています(誘導の意味を理解するのは人間が解いているようなものなので). 「辺ADと辺BCが平行」と「辺ABと辺CDが平行」の二択しかないので, 前者の場合辺の長さが虚数になって矛盾が生じ, 後者しかあり得ないことを確かめています.
ク: 2, ケコ: 33
電卓です.


f:id:HelloRusk:20180208183634p:plain
f:id:HelloRusk:20180208183737p:plain


解説
サ: 1, シ: 6
センター試験ではおなじみの「データの分析」の問題です. どうやってPythonにやらせようか迷いましたが, データから必要な統計量を読み取ってdictionary型で整理してみました.


f:id:HelloRusk:20180208183033p:plain
f:id:HelloRusk:20180208183045p:plain


解説
ス: 4, セ: 5
まず選択肢を見ると, 選択肢0のみ X と W についての選択肢で残りは全て Z についての選択肢なので, 選択肢0の正誤判定は諦めることにしました(選択肢1〜5の中に正解を2つ見つけることができれば選択肢0は選ばず, 1つしか見つからなければ選択肢0を選ぶという方針です).
その上で, まず, 散布図の様子から Z のデータを再現することにしました.
f:id:HelloRusk:20180209082810p:plain
例えば男子短距離であれば, 直線の傾きを参考にすると, およそ20〜26の範囲に Z が存在すると読み取れます. 仮に Z の散らばりが正規分布で近似できるとすると, 20〜26の範囲が2シグマ区間(約95%)とみなせます. その場合, 正規分布の平均が23, 標準偏差が1.5なので, 平均23, 分散2.25の正規分布に従う乱数を男子短距離の人数分(328人分)発生させています.
これだけでデータの散らばりは大体再現できますが, 最大値・最小値が実際のデータと異なるのはあまり良くないと考えました. そこで, 作ったデータのうち, まず「実際のデータの最大値」よりも大きい値の要素を「実際のデータの最大値」に置き換え, 次に(全てのデータが「実際のデータの最大値」より小さい可能性があるので)データの最大値を「実際のデータの最大値」に置き換えるという操作を加えました. 最小値についても同様です.
matplotlib.pyplot.boxplot でデータから自動的に箱ひげ図を作成することができます(ただしboxplotでは勝手に外れ値判定をしてしまい, 最大値や最小値が点になってしまっているものがあります). ここまで丁寧に再現されたデータで箱ひげ図で再現すれば, 問題文の箱ひげ図と比べてどれがどの図かを判定するのはたやすいです. あとは前問のサ、シと同様に選択肢の正誤判定を行います.


f:id:HelloRusk:20180209162028p:plain


解説
ソ: 2
要素の数 n を変数のまま考えるのは難しそうなので n = 5 とまず決めてしまいます. その上で,  x_1,...,x_n,w_1,...,w_n を定義し, 問題文で与えられた式をそのまま作ります. 最後は選択肢0〜3の候補を全てあてはめてみて, (左辺)ー(右辺)を展開したときに0になるものを見つけています.


f:id:HelloRusk:20180210233813p:plain
f:id:HelloRusk:20180210233828p:plain


解説
ア: 1, イ: 6, ウ: 1, エ: 6, オ: 1, カ: 9
まず初めにサイコロの目の配列を作り, 条件を満たす要素の数え上げをすることで確率を求めています. ただし, 普通にやると答えが循環小数になってしまうので, fractionsモジュールのFractionインスタンスを用いて既約分数に近似しています. Fractionインスタンスの利点は, 普通のfloat型のように四則演算が使えること, そしてlimit_denominatorメソッドによって近似する分数の分母の上限を指定できることです.
キ: 1, ク: 4, ケ: 1, コ: 6
素直に条件付き確率を求めています.
サ: 1, シ: 2
素直に大小比較をしています.
ス: 1, セ: 4, ソ: 3, タ: 2, チ: 1, ツ: 8, テ: 1
まともな受験生であれば, B と C が同時に起こらないことを利用してうまく解くべきところでしょうが, そういう難しいことを考えなくても機械的に計算できてしまいます.


f:id:HelloRusk:20180212014018p:plain
f:id:HelloRusk:20180212014031p:plain


解説
ア: 4, イ: 3, ウ: 2, エオ: 15
素因数分解のプログラムや約数の個数を求めるプログラムなどは, ライブラリの助けを借りることなく書けてしまいますが, せっかくここまでSymPyを活用してきたので今回も使ってみましょう. SymPyのfactorintで素因数分解が, divisor_countで約数の個数のカウントができます.
カ: 2, キク: 41, ケ: 7, コサシ: 144
SymPy.solversのdiophantineで, 整数係数不定方程式の整数解を求めることができます.
ス: 2, セソ: 23
問題文に書かれていることをそのままやっています. 特に解説することは無いです.


f:id:HelloRusk:20180212050147p:plain f:id:HelloRusk:20180212050200p:plain


解説
ア: 2, イ: 5, サ: 3, エオ: 20, カ: 9, キク: 10, ケ: 9, コ: 0, サ: 4, シ: 5, ス: 8, セ: 5, ソ: 3
SymPyのGeometryモジュールを駆使して図形問題を愚直に解いています. Geometryモジュールの使い方は以下の記事が詳しいので解説はそちらに譲ります.

qiita.com

先を見越して点Fまで一気に求めています.
個人的には, 点Aを通り点Dで辺BCに接する円の中心の求め方がイマイチだな, と思っています. もっとプログラム任せの方法で出来たかもしれません.
タ: 1
Triangle.circumcenter で外心を, .incenter で内心を, .centroid で重心を勝手に求めてくれます. 便利ですね. 他にも .orthocenter で垂心を求めてくれます. 傍心だけそういうのが無いそうです.

結論