プログラミングで数学検定1級は合格できるのか

#はじめに
この記事で取り扱っているのは、1~5問目です。（全7問中）

#数検1級の難易度は
大学程度・一般レベル。日本数学検定協会によると、合格率は5.7%で、すべて記述式だそう。東大の入試問題よりしばしば難しいとも言われます。

#どの問題を解くのか
[数学検定1級1次試験　サンプル問題](https://www.su-gaku.net/digitalbook/past_question/1q_que_1ji/index_h5.html#2)　のうち何問かを解いていこうと思います。（模範解答は[ここ](https://www.su-gaku.net/digitalbook/past_question/1q_ans_1ji/index_h5.html#1)から入手できます。

#実行環境
Python......[paiza.io](https://paiza.io/ja)
FORTRAN......[AtCoder　コードテスト](https://atcoder.jp/)の中の[ABC187のコードテスト](https://atcoder.jp/contests/abc187/custom_test)

#実際に解いていきましょう

##1問目

>
問題1
2018n ≡ 2 mod(1000) を満たす正の整数nの最小値を求めよ。

となる。まず、modとはどういうものだろうか。

>modとは
p ≡ q(mod n)のとき、p-qがnで割り切れる。

なんとなくわかりましたか？
では、例をみてみましょう。

>例
3 ≡ 8(mod 5)
3-8は5で割り切れます。

こんな感じの記号です。

これを全探索（全てのパターンを試すアルゴリズム）で解いていこうと思います。(本当は1桁目で分類>2桁目で分類みたいな感じでとくのだろう）

####Python
######プログラム
```Python
n = 1
while True:
    if (2018*n-2)%1000 == 0:
        break
    n += 1
    
print(n)
```

######出力

```Python
389
```

####FORTRAN
######プログラム

```Fortran
program question1
  do n = 1,1000
    !ここでは、mod 1000 なので、nが1001以上の値を取らないことが証明できます。
    if (mod((2018*n-2),1000) == 0) then
      print '(i0)', n
    end if
  end do
end
```
######出力

```Swift
389
889
```
よって、最も小さい389が答えだと考えられます（最初から1000と2018が偶数であるから500まで全探索でも良いのですが。）。

####Swift
######プログラム
```Swift
var n = 0
while n < 1001{
    if (2018*n-2)%1000 == 0{
        print(n)
        break
    }
    n += 1
}
```
while文での実装です。
######出力

```Swift
389
```


解答を見たら正解でした。

##2問目
>tan(2 Arctan (1/3)+Arctan(1/12))を求めなさい。ただし、Arctan xはtan xの逆関数を表し、-π/2 < Arctan x < π/2 とします。

「Arctanはtanの逆関数」とは？
>[この](https://qiita.com/FukuharaYohei/items/398763e4c4b4ec03f17f)記事に詳しく載っています。

これは普通に計算して、近い値を見つけていきたいと思います。

####Python（精度　低）
######プログラム

```Python
import math
inside = 2*math.degrees(math.atan(1/3))#tanの中身
inside += math.degrees(math.atan(1/12))
print(math.tan(math.radians(inside)))
```


######出力

```Python
0.8888888888888887
```
だいたい、8/9くらいでしょうか（正解でした）。
本当はArctanの加法定理を使うのかな......

####Python（精度　UP）
＜方針＞
- Arctanの加法定理を実現しよう
- 小数は誤差が出やすいので、分数で考えよう
- 上の二つの条件を満たす関数を作ろう。

Wikipediaにはこうある。

```math
\arctan u+\arctan v=\arctan \left({\frac  {u+v}{1-uv}}\right){\pmod  \pi },\qquad uv\neq 1\,.
```
ここでは、-π/2~π/2でのみ定義しているため、(mod π)をなくして考える。u=a/b,v=c/dとして考える。問題ではその後、tanでまた元に戻しているため、(u+v)/(1-uv)のみ考えれば良い(tan(arctan(x)) = x)。結局、答えは(u+v)/(1-uv)なので、これを出力するプログラムを考える。

とりあえず、こんな関数を作ってみました。

```Python
from fractions import Fraction
def arctan_sum(a,b,c,d):
    u = Fraction(a,b)
    v = Fraction(c,d)
    return ((u+v),(1-u*v))
```
戻り値はタプルです。下のように書くと、
######プログラム
```Python
from fractions import Fraction
def arctan_sum(a,b,c,d):
    u = Fraction(a,b)
    v = Fraction(c,d)
    return ((u+v),(1-u*v))
left = arctan_sum(1,3,1,3)
total = arctan_sum(left[0],left[1],1,12)
```
######出力結果

```Python
8/9
```
綺麗に答えが出ました。


##3問目
>xyz空間内の4点(1,-4,1),(2,2,2),(2,-6,-3),(3,-2,-1)を頂点に持つ四面体の体積を求めよ。

[サラスの公式](https://mathtrain.jp/sarrus_formula)を使って解きます。まず、(0,0,0)(x1,y1,z1),(x2,y2,z2),(x3,y3,z3)を頂点に持つ四面体の体積を求める関数を作る。しかし、これは(0,0,0)を頂点に持つ四面体を考えなければならないので、x1~z3を手計算（一番早そうなので）で求めていきたいの思います。


####Python
######関数
```Python
from fractions import Fraction
def sarasu(x1,y1,z1,x2,y2,z2,x3,y3,z3):
    return Fraction(abs(y1*z2*x3+z1*x2*y3+x1*y2*z3-z1*y2*x3-x1*z2*y3-y1*x2*z3),6)
```
######プログラム全体
```Python
from fractions import Fraction
def sarasu(x1,y1,z1,x2,y2,z2,x3,y3,z3):
    return Fraction(abs(y1*z2*x3+z1*x2*y3+x1*y2*z3-z1*y2*x3-x1*z2*y3-y1*x2*z3),6)
    
print(sarasu(1,6,1,1,-2,-4,2,2,-2))
```

######出力結果
```Python
3
```
これもあっさり答えが出ました。（正解でした。）

##4問目(1)

```math

  3次正方行列 A = \left(
    \begin{array}{ccc}
      4 & 1 & 1 \\
      1 & 2 & 1 \\
      1 & 1 & 2
    \end{array}
  \right) について次の問いに答えなさい。

```

```math
①固有値を求めなさい。
```
####Python
[Pythonで固有値を求めることができる](https://keymale.xyz/2020/09/09/post-735/)ので、求めていこうと思います。

######プログラム
```python
import numpy as np
import numpy.linalg as LA

a = np.array([[4,1,1],[1,2,1],[1,1,2]])#固有値を求めたい行列


aeig_val = LA.eig(a)[0]
aeig_val = np.sort(aeig_val)

print(aeig_val)
```
######出力結果
```Python
[1. 2. 5.]
```
リストにしようとすると、誤差が出るため、このような結果にしました。（答えは1,2,5なので、あってます。）

##4問目(1)進化版
replaceで解答の形に変えました。
####Python
######プログラム（進化版）
```Python
import numpy as np
import numpy.linalg as LA

a = np.array([[4,1,1],[1,2,1],[1,1,2]])#固有値を求めたい行列


aeig_val = LA.eig(a)[0]
aeig_val = np.sort(aeig_val)

ans = str(aeig_val)
ans = ans.replace("[","").replace(".]","").replace(". ",", ")

print(ans)
```
######出力結果
```Python
1, 2, 5
```

##4問目(2)
固有値問題を数値的に解く - 高知大学、[行列の累乗](https://python.atelierkobato.com/matrix_power/)、[3　単位行列、行列の転置(行列)](http://www.math.kobe-u.ac.jp/HOME/higuchi/h21kogi/1022.pdf)　などを参考にして、求めていきたいと思います。これはやるだけで、大丈夫です。

####Python
######プログラム
```Python
import numpy as np
import numpy.linalg as LA

A = np.array([[4,1,1],[1,2,1],[1,1,2]])
I = np.array([[1,0,0],[0,1,0],[0,0,1]])

A3 = np.linalg.matrix_power(A, 3)#A^3
A2 = np.linalg.matrix_power(A, 2)#A^2

ans = A3-8*A2+18*A-12*I#そのまま演算子で計算
print(ans)
```
######出力結果

```Python
[[2 1 1]
 [1 0 1]
 [1 1 0]]
```
わかりやすく書くと、

```math

\left(
    \begin{array}{ccc}
      2 & 1 & 1 \\
      1 & 0 & 1 \\
      1 & 1 & 0
    \end{array}
  \right)

```
（これもまた正解でした）

##5問目(1)
>一定の期間内における機械の故障回数はポアソン分布に従うことが知られています。ある会社では2つの機械A, Bを使用しており、1年間で故障する回数が機械Aが平均2.5回、機械Bは平均4.5回のポアソン分布に従うとき、次の問いに答えなさい。ただし、A,Bが1年間に故障する回数は互いに独立であるとし、解答の際は1-6-6にあるポアソン分布表を用い、答えは小数第4位を四捨五入して小数第3位まで求めなさい。

>①機械Aが1年間に4回以上故障する確率を求めなさい。

ポアソン分布というのは......

「ポアソン分布を表す確率関数は，

```math
P(k) = (e^{-k}*λ^k) / k!
```
です。
つまり，単位時間あたり平均 λ 回起こるようなランダムなイベントが，単位時間に k 回発生する確率が P(k) です。」

（[高校数学の美しい物語　ポアソン分布の意味と平均・分散](https://mathtrain.jp/poisson)　より抜粋）


この問題では、壊れた回数が4回未満になる確率を全体から引く。このとき、λが2.5、kが0~3だとわかる。
####Python
######プログラム

```Python
zero_three = 0.0821+0.2052+0.2565+0.2138#足しただけ

print(round((1-zero_three)*1000)/1000)
```

######出力結果
```Python
0.242
```
正解でした。

##5問目(2)
>一定の期間内における機械の故障回数はポアソン分布に従うことが知られています。ある会社では2つの機械A, Bを使用しており、1年間で故障する回数が機械Aが平均2.5回、機械Bは平均4.5回のポアソン分布に従うとき、次の問いに答えなさい。ただし、A,Bが1年間に故障する回数は互いに独立であるとし、解答の際は1-6-6にあるポアソン分布表を用い、答えは小数第4位を四捨五入して小数第3位まで求めなさい。

>②機械A,Bが合わせて8回以上故障する確率を求めなさい。

まず、平均2.5回、4.5回のポアソン分布の値をリストにします。次に、故障した回数が8未満になる確率を求め、全体から引きます。
####Python
######プログラム
```Python
A = [0.0821,0.2052,0.2565,0.2138,0.1336,0.0668,0.0278,0.0099,0.0031,0.0009,0.0002]#平均2.5回のポアソン分布
B = [0.0111,0.0500,0.1125,0.1687,0.1898,0.1708,0.1281,0.0824,0.0463,0.0232,0.0104]#平均4.5回のポアソン分布

ans = 0
for i in range(8):
    for j in range(i+1):
        ans += A[i-j]*B[j]

print(round((1-ans)*1000)/1000)
```

######出力結果
```Python
0.401
```
正解でした。

#結論
**合格します（合格ラインは5問）！**ただ、普通に計算した方がプログラムを組むよりも早い問題もあります。

#参考文献
- [Qiita Markdown 書き方まとめ](https://qiita.com/shizuma/items/8616bbe3ebe8ab0b6ca1)
- [数学検定　算数検定　ホームページ](https://www.su-gaku.net/suken/support/past_questions/)
- [日本 NAG Fortran入門](https://www.nag-j.co.jp/fortran/index.html)
- [Yahoo 知恵袋　エラーに関する質問](https://detail.chiebukuro.yahoo.co.jp/qa/question_detail/q12148659632)
- [Tech Academy 繰り返し処理！Swiftで書くwhile/for文の使い方【初心者向け】](https://techacademy.jp/magazine/14546)
- [Pythonで三角関数を計算（sin, cos, tan, arcsin, arccos, arctan）](https://note.nkmk.me/python-math-sin-cos-tan/)
- [Wikipedia 逆三角関数](https://ja.wikipedia.org/wiki/%E9%80%86%E4%B8%89%E8%A7%92%E9%96%A2%E6%95%B0)
- [fractions --- 有理数](https://docs.python.org/ja/3/library/fractions.html)
- [LaTeXコマンド集　行列](http://www.latex-cmd.com/equation/matrix.html)
- [NumPyのsortとargsort関数で任意の行・列を基準にソート](https://note.nkmk.me/python-numpy-sort-argsort/)
- 固有値問題を数値的に解く - 高知大学
- [行列の累乗](https://python.atelierkobato.com/matrix_power/)
- [3　単位行列、行列の転置(行列)](http://www.math.kobe-u.ac.jp/HOME/higuchi/h21kogi/1022.pdf)
- [Pythonで小数・整数を四捨五入するroundとDecimal.quantize](https://note.nkmk.me/python-round-decimal-quantize/)


#注意
- この記事は数学検定でのカンニングを推奨するものではありません。悪用しないでください。